“This is the 20th day of my participation in the August Gwen Challenge.

preface

At the advanced end of the interview, there are often some questions about design patterns, and the answers are not good. Coincides with the activities of more challenges in August, I plan to spend a month to clarify the knowledge points about design patterns, to increase the confidence of my interview.

In the last article, a responsibility chain model was simply implemented, but the transfer of responsibility is very rigid and violates the open-closed principle. Therefore, this paper introduces the implementation of a flexible and separable responsibility chain node.

The decoupling

Let’s take a look at the responsibility chain nodes we designed earlier.

// const orderType = (orderType, pay, Stock) => {if (orderType === 1 && pay === = true) {console.log('1000 yuan deposit order, get 500 coupons '); } else { order500(orderType, pay, stock); // Pass the request to the pre-paid 1000 deposit order}}; // Const order500 = (orderType, pay, stock) =>{if (orderType === 2 && pay === = true) {console.log('500 元 Get 200 coupons '); } else { orderNormal(orderType, pay, stock); // Pass the request to a normal order}}; // Const orderNormal = (orderType, pay, stock) =>{if (stock > 0) {console.log(' orderType, no coupon '); } else {console.log(' phones out of stock '); }};Copy the code

First decouple it. How to decouple it, with a convention that if a node can’t handle the request, it returns a specific string toNextNode to indicate that the request needs to be passed on to subsequent nodes.

// const orderType = (orderType, pay, Stock) => {if (orderType === 1 && pay === = true) {console.log('1000 yuan deposit order, get 500 coupons '); } else { return 'toNextNode'; // I don't know who the next node is. // Const order500 = (orderType, pay, stock) =>{if (orderType === 2 && pay === = true) {console.log('500 元 Get 200 coupons '); } else { return 'toNextNode'; // I don't know who the next node is. // Const orderNormal = (orderType, pay, stock) =>{if (stock > 0) {console.log(' orderType, no coupon '); } else {console.log(' phones out of stock '); }};Copy the code

Assembly chain

After decoupling, each of the above functions becomes a normal function, and a wrapper function is created to wrap the normal function as a responsibility chain node.

The wrapper function is a class that has a method for setting up the next request node, and a method for executing the current node, and then continuing to request the next node if the request cannot be processed.

class Chain{ constructor(fn){ this.fn = fn; this.nextNode = null; } setNextNode(fn){ return this.nextNode = fn; } passRequest(){ const res = this.fn.apply(this,arguments); if(res === 'toNextNode'){ return this.nextNode && this.nextNode.passRequest.apply(this.nextNode, arguments); } return res; }}Copy the code

SetNextNode method sets the next request node, passRequest method is used to execute the current node, and after execution, if the request cannot be processed, continue to request the next node.

Now wrap the previous three functions as responsibility chain nodes:

const chainOrder1000 = new Chain(order1000);
const chainOrder500 = new Chain(order500);
const chainOrderNormal = new Chain( orderNormal); 
Copy the code

Then assemble the responsibility chain nodes into a responsibility chain:

chainOrder1000.setNextNode( chainOrder500 );
chainOrder500.setNextNode( chainOrderNormal ); 
Copy the code

Finally begin the chain of duties:

chainOrder1000.passRequest( 1, true, 500 ); / / output: 1000 yuan deposit and advance, get 500 coupons chainOrder1000 passRequest (2, true, 500); / / output: 500 yuan deposit and advance, get 200 coupons chainOrder1000 passRequest (3, true, 500); / / output: ordinary purchase, no coupons chainOrder1000. PassRequest (1, false, 0); // Output: Not enough phones in stockCopy the code

Disassemble the chain of responsibility and reassemble it

For example, now we need to add a new marketing strategy to pay 300 yuan in advance and get 50 coupons. You can do it this way.

Const orderType = (orderType, pay, stock) =>{if (orderType === 4 && pay === = true) {console.log('300 元 Get 50 coupons '); } else { return 'toNextNode'; // I don't know who the next node is. chainOrder300= new Chain(order300); chainOrder1000.setNextSuccessor(chainOrder500); chainOrder500.setNextSuccessor(chainOrder300); chainOrder300.setNextSuccessor(chainOrderNormal); chainOrder1000.passRequest( 4, true, 500 ); // Output: 300 yuan deposit pre-order, get 50 couponsCopy the code

In this way, the responsibility chain can be flexibly disassembled. When changing the sequence of nodes in the responsibility chain or adding new nodes in the responsibility chain, the content of the nodes in the responsibility chain will not be modified, and the open-closed principle will be followed.

How are asynchronous responsibility chain nodes implemented

Each of the responsibility chain nodes described above synchronously returns a specific string toNextNode to indicate that the request needs to be passed on to subsequent nodes. What if the responsibility chain node’s request is asynchronous? Wait for the result of the asynchronous request to decide whether to continue the request to the next responsibility chain node. How do you do that? Leave a thought question, which will be covered in the next article.