Chain Of Responsibility Model

Chain of Responsibility Pattern creates a chain of receiver objects for a request, decoupling the sender and receiver of the request. This design pattern falls under the category of behavioral patterns. In this pattern, each receiver usually contains a reference to another receiver. If an object cannot handle the request, it passes the same request to the next receiver, and so on.

Description

In object-oriented design, the Chain of Responsibility Pattern is composed of a command object source and a series of handling objects. Each handling object contains logic that defines the type of command objects it can handle, and the remaining objects are passed to the next handling object in the chain. To avoid coupling the request sender and receiver together and allow multiple objects to potentially receive the request, these objects are connected in a chain, and the request is passed along the chain until an object handles it. The handlers on the chain are responsible for processing the request, and the client only needs to send the request to the chain, without worrying about the details of request handling and propagation. Therefore, the Chain of Responsibility decouples the sender and receiver of the request.

Advantages

  • It reduces coupling by decoupling the sender and receiver of the request.
  • It simplifies objects by eliminating the need for objects to know the structure of the chain.
  • It enhances flexibility in assigning responsibilities to objects, allowing dynamic addition or removal of responsibilities by changing the members within the chain or their order.
  • It facilitates the addition of new request handling classes.

Disadvantages

  • It does not guarantee that a request will be received.
  • System performance may be affected, and it may be inconvenient for code debugging, potentially leading to circular calls.
  • Observing runtime characteristics may be difficult, hindering debugging.

Applicable scenarios

  • When multiple objects can handle the same request, and the specific object to handle the request is determined automatically at runtime.
  • When submitting a request to one of multiple objects without explicitly specifying the receiver.
  • When a dynamic group of objects needs to be specified to handle a request.

Implementation

/*
    When an account is set with three payment methods: bank, paypal, and bitcoin,
    with balances of 100RMB, 200RMB, and 300RMB respectively, 
    the payment priority is bank, then paypal, and finally bitcoin.
    When trying to purchase something worth 251RMB using the Chain of Responsibility pattern, 
    first, the bank account is checked for purchase eligibility. If eligible, the purchase is made and the chain is broken. 
    If not, the request will move on to the paypal account to check if the amount is sufficient. 
    If it is, the purchase will be made and the chain will be broken. 
    Otherwise, the request will continue to be forwarded until a suitable handler is found. 
    Here, bank, paypal, and bitcoin serve as links in the chain, 
    and the entire phenomenon is the responsibility chain.
*/

class Account {
    setNext(mode) {
        this.mode = mode;
    }
    
    pay(amountToPay) {
        if (this.canPay(amountToPay)) {
            console.log(`Paid ${amountToPay} using ${this.name}`);
        }else if(this.mode) {
            console.log(`Cannot pay using ${this.name}. Proceeding...`);
            this.mode.pay(amountToPay);
        }else{
            console.log("None of the accounts have enough balance");
        }
    }
    
    canPay(amount) {
        return this.balance >= amount;
    }
}

class Bank extends Account {
    constructor(balance) {
        super();
        this.name = "bank";
        this.balance = balance;
    }
}

class Paypal extends Account {
    constructor(balance) {
        super();
        this.name = "Paypal";
        this.balance = balance;
    }
}

class Bitcoin extends Account {
    constructor(balance) {
        super();
        this.name = "bitcoin";
        this.balance = balance;
    }
}


(function(){

    const bank = new Bank(100);          // Bank with balance 100
    const paypal = new Paypal(200);      // Paypal with balance 200
    const bitcoin = new Bitcoin(300);    // Bitcoin with balance 300

    bank.setNext(paypal);
    paypal.setNext(bitcoin);
bank.pay(251);

// Cannot pay using bank. Proceeding...
// Cannot pay using Paypal. Proceeding...
// Paid 251 using bitcoin

Daily Question

https://github.com/WindrunnerMax/EveryDay

Reference

https://juejin.im/post/6844903855348514829 https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html https://github.com/sohamkamani/javascript-design-patterns-for-humans#-chain-of-responsibility