In traditional object-oriented languages, the method of inheritance is often used to add functions to objects. However, the method of inheritance is not flexible, and it also brings many problems. On the one hand, it will lead to strong coupling between superclass and subclass. Inheritance, on the other hand, is often referred to as “white box reuse.” “White box” is relatively visible. In inheritance, the inner details of a superclass are visible to subclasses, and inheritance is often thought of as breaking encapsulation. Decorator pattern allows you to dynamically add responsibilities to objects during program execution without changing the objects themselves. Decorator is a lighter, more flexible approach than inheritance. It’s a “pay-as-you-go” approach

The story background

Let’s say we’re writing a game in which we’re fighting planes. As experience increases, the planes we’re flying can be upgraded to more powerful planes. At first, these planes can fire normal bullets, at level 2 they can fire missiles, and at level 3 they can fire atomic bombs.

Code implementation (using decorator pattern)

var plane = {
    fire: function(){
        console.log( 'Fire ordinary bullets' ); 
    }
}
var missileDecorator = function(){ 
    console.log( 'Launch a missile' );
}
var atomDecorator = function(){ 
    console.log( 'Launch an atomic bomb' );
}
var fire1 = plane.fire;
plane.fire = function(){ 
    fire1();
    missileDecorator(); 
}
var fire2 = plane.fire;
plane.fire = function(){ fire2(); atomDecorator(); } plane.fire(); // Separate output: firing ordinary bullets, firing missiles, firing atomic bombsCopy the code

Implement the Decorator pattern using AOP

First Function is given. The prototype. Before method and the Function prototype. After methods

Function.prototype.before = function( beforefn ){ var __self = this; // Save a reference to the original functionreturn function(){// returns a function containing the original and the new function"Agent"Function beforefn.apply(this, arguments); // The new function is executed without this being hijacked. The arguments accepted by the new function are also passed to the original function intact. The new function is executed before the original functionreturn__self.apply( this, arguments ); }} function.prototype. after =function( afterfn ){ 
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments ); 
        afterfn.apply( this, arguments );
        returnret; }};Copy the code

An application example of AOP

  • Data statistics reportFor example, there is a login button in the page, clicking the button will pop up the login floating layer, and at the same time, data should be reported to count how many users click the login button
    • Not using AOP
    var showLogin = function(){ 
       console.log( 'Open login float' ); 
       log( this.getAttribute( 'tag')); } varlog = function( tag ){
       console.log( 'Report label:' + tag );
       (new Image).src = 'http:// xxx.com/report?tag=' + tag;
    }
    document.getElementById( 'button' ).onclick = showLogin;
    Copy the code
    • Using AOP
    var showLogin = function(){ 
        console.log( 'Open login float' );
    }
    var log = function(){
        console.log( 'Report label:' + this.getAttribute( 'tag')); } showLogin = showLogin.after(log); Document.getelementbyid ('button' ).onclick = showLogin;
    Copy the code
  • Plug-in form validation Many of us have written a lot of form validation code, and in a Web project, there can be many forms, such as registration, login, modifying user information, and so on. Before submitting form data to the background, some validation is often done, such as verifying that the user name and password are null during login
    • Not using AOP
    var formSubmit = function() {if ( username.value === ' ') {return alert ( 'User name cannot be empty' ); 
        }
        if ( password.value === ' ') {return alert ( 'Password cannot be empty' );
        }
        var param = {
            username: username.value, password: password.value
        }
        ajax( 'http:// xxx.com/login', param );
    }
    submitBtn.onclick = function(){ 
        formSubmit();
    }
    Copy the code
    • Using AOP
    var validata = function() {if ( username.value === ' ' ){
            alert ( 'User name cannot be empty' );
            return false; 
        }
        if ( password.value === ' ' ){ 
            alert ( 'Password cannot be empty' ); 
            return false;
        } 
    }
    var formSubmit = function(){ 
        var param = {
            username: username.value,
            password:password.value
        }
        ajax( 'http:// xxx.com/login', param ); 
    }
    formSubmit = formSubmit.before( validata );
    submitBtn.onclick = function(){ 
        formSubmit();
    }
    Copy the code

summary

The decorator pattern and the proxy pattern look very similar in structure. Both patterns describe how to provide some degree of indirect reference to an object, and their implementation parts retain a reference to another object and send requests to that object. The most important difference between the agent pattern and the decorator pattern is their intent and design purpose. The purpose of the proxy pattern is to provide a substitute for an ontology when direct access to that ontology is inconvenient or undesirable. The ontology defines key functionality, and the proxy provides or denies access to it, or does additional things before accessing the ontology. The role of decorator mode is to dynamically add behavior to the object.

Series of articles:

JavaScript Design Patterns and Development Practices