An in depth understanding of the JavaScript series (38) : a detailed explanation of the responsibility chain of design patterns

  • 2020-05-10 17:38:34
  • OfStack

introduce

The responsibility chain pattern (Chain of responsibility) is to give multiple objects the opportunity to process the request, thereby avoiding the coupling between the sender and recipient of the request. Link the object into a chain and pass the request along the chain until one object handles it.

That is, after the request, starting with the first object, the object receiving the request in the chain either processes it itself or forwards it to the next candidate in the chain. The object that submits the request does not know exactly which object will handle it -- that is, the request has an implicit recipient (implicit receiver). Depending on the runtime, any candidate can respond to requests, the number of candidates is arbitrary, and you can decide at runtime which candidates are involved in the chain.

The body of the

For the JavaScript implementation, we can take advantage of its prototype features to implement the chain of responsibility pattern.


var NO_TOPIC = -1;
var Topic; function Handler(s, t) {
    this.successor = s || null;
    this.topic = t || 0;
} Handler.prototype = {
    handle: function () {
        if (this.successor) {
            this.successor.handle()
        }
    },
    has: function () {
        return this.topic != NO_TOPIC;
    }
};

Handler just accept two parameters, the first is the successor (used to process the request to pass down), are the second level (can be used to control whether to perform a particular operation under a certain level, also can need not), revealed a handle Handler prototype method, this is the point that implement the pattern, let's look at how to use the above code.


var app = new Handler({
        handle: function () {
            console.log('app handle');
        }
    }, 3);     var dialog = new Handler(app, 1);     var button = new Handler(dialog, 2);     button.handle();

Change the code through the prototype feature, calling the code from button.handle ()- > dialog.handle()- > app.handle()- > handle() in the parameter, the first three are all called handle in the prototype, and finally handle in the incoming parameter is found, and then the result is output, which means that only the last layer is processed.

So how do you get the dialog object to handle only when you call it? You can define the handle method of dialog instance object, but you need to do it before new button. The code is as follows:


var app = new Handler({
        handle: function () {
            console.log('app handle');
        }
    }, 3);     var dialog = new Handler(app, 1);
    dialog.handle = function () {
        console.log('dialog before ...')
        // Here you do the actual processing
        console.log('dialog after ...')
    };     var button = new Handler(dialog, 2);     button.handle();

The execution result of this code is the result of the processing in dialog.handle, instead of the handle execution defined in the parameters passed to app.

Can you deal with it yourself and then let your successor deal with it? The answer is yes, but after calling handle, the following code needs to be called using the features of the prototype:


Handler.prototype.handle.call(this);

The handle method of the prototype is called to continue calling the handle method of its successor, successor. The following code is shown as: handle defined by button/dialog/app3 objects is executed.

var app = new Handler({
    handle: function () {
        console.log('app handle');
    }
}, 3); var dialog = new Handler(app, 1);
dialog.handle = function () {
    console.log('dialog before ...')
    // Here you do the actual processing
    Handler.prototype.handle.call(this); // Keep going up
    console.log('dialog after ...')
}; var button = new Handler(dialog, 2);
button.handle = function () {
    console.log('button before ...')
    // Here you do the actual processing
    Handler.prototype.handle.call(this);
    console.log('button after ...')
}; button.handle();

Through code run results we can see that if you want to own processing first, and then call the successor processing, at the end of the execution Handler. prototype. handle. call (this); Code, if you want to handle successor code first, just in the beginning Handler. prototype. handle. call (this); The code.

conclusion

The responsibility chain pattern is often used with composite pattern 1, so that the parent component of a component can act as its successor.

At the same time, the event bubbling in the DOM mechanism and this seems a little bit similar, such as clicking a button, if you don't stop the bubble, the click incident will 1 straight to the parent element bubbling, using this mechanism can also handle many related problems, such as the series of design pattern in the flyweight pattern "case 1: event centralized management of the sample code.


Related articles: