JS design mode 7: decorator Mode
Decorator Pattern. The object function is dynamically extended without changing the original class and inheritance. By packing an object, a new object with the same interface of the original object is implemented.
Decoration Mode features: 1. Add functions without modifying the original structure of the original object.
2. The decoration object has the same interface as the original object, so that the customer can use the decoration object in the same way as the original object.
3. The decoration object contains the reference of the original object, that is, the decoration object is the real original object packaged here.
The modifier mode can be used to add functions for objects, instead of writing a large number of child classes.
Taking the JS design model as an example, there are four types of bicycles in the bicycle store:
- var ABicycle = function(){ ... };
- var BBicycle = function(){ ... };
- var CBicycle = function(){ ... };
- var DBicycle = function(){ ... };
When a bicycle store needs to provide additional functions or accessories for each type of bicycle, such as headlights, baskets, and color changes, the most basic way is to create a subclass for each combination, for example, A type A car with bells, A Type B car with yellow bells, and A type C car with A basket,
- var ABicycleWithBell = function(){ ... };
- var BBicycleWithColorYellow = function(){ ... };
- var CBicycleWithColorBule = function(){ ... };
- ...
Indeed, it is just like the feeling you see at first glance. This method is decisive and unreliable, n types of configuration, at the end, the 4n + 4 types (including the original 4 parent classes) will be generated, which is never desirable.
Of course, our first possible idea is that we can set the color, the car basket or something as an instance property and control the generation by the passed parameters in the class. This is indeed good, no problem, however, some function extensions may be insufficient, such as the speed control function, brake enhancement function, and anti-theft function added to bicycles, it is easier to use the modifier mode to add these functions (if it is more embedded in the class, it will be processed by passing parameters, in the case of many features, it is obviously not reliable ).
First, we need A class of basic bicycle.
- function ABicycle(){ }
- ABicycle.prototype = {
- wash : function(){ },
- ride : function(){ },
- getPrice : function(){
- return 999;
- }
- }
The next step is to set a bell for the bicycle and the function of ringing the bell:
The simplest thing is to directly wrap the object instance:
- function bicycleBell( bicycle ){
- var price= bicycle.getPrice();
- bicycle.bell = function(){
- console.log("ding! ding! ding!");
- };
- bicycle.getPrice = function(){
- return price + 100;
- };
- return bicycle;
- }
Use
- var bicycleA = new ABicycle();
- bicycleA = bicycleBell( bicycleA );
In the following format:
- bicycle.getPrice = function(){
- return bicycle.getPrice() + 100;
- };
Of course, this form will lead to infinite loop calls ~
Indeed, this packaging method is not to wrap the object into a constructor again, but to adjust the instance object, which is both convenient and simple. However, it is not a problem for returning constant data results like the getPrice method, you can save it in the form of a closure.
However, if the function needs to be called in the packaging and saved in the form of a closure, it will be cumbersome and unchanged.
So let's look at the decoration method below:
- function BicycleBell( bicycle ){
- this.bicycle = bicycle;
- }
- BicycleBell.prototype = {
- wash : function(){
- return this.bicycle.wash();
- },
- ride : function(){
- return this.bicycle.ride();
- },
- getPrice : function(){
- return this.bicycle.ride() + 100;
- },
- bell : function(){
- console.log("ding! ding! ding!");
- }
- }
Package the instance, simulate the original class again, encapsulate the instance as a parameter, and provide the same interface as the original class. This method solves some methods that need to be modified and depend on the original method. Of course, it also has its own shortcomings. This decoration is too cumbersome, and all, such as acceleration, color switching, and the decoration of the car basket or anything, must be in such a form, and the code will be a little messy.
So, extract it.
First, an inherited method is required, and the prototype pointing to the parent class is required.
- Function extend (subClass, superClass ){
- Var F = function (){};
- F. prototype = superClass. prototype;
- SubClass. prototype = new F ();
- SubClass. prototype. constructor = subClass;
- // Prototype pointing to superClass is more convenient than directly saving superClass
- SubClass. superclass = superClass. prototype;
- If (superClass. prototype. constructor === Object. prototype. constructor ){
- SuperClass. prototype. constructor = superClass;
- }
- }
Then, each decoration person can depend on the inherited intermediate decoration person:
- function BicycleDecorator( bicycle ){
- this.bicycle = bicycle;
- }
- BicycleDecorator.prototype = {
- wash : function(){
- return this.bicycle.wash();
- },
- ride : function(){
- return this.bicycle.ride();
- },
- getPrice : function(){
- return this.bicycle.ride();
- }
- }
Then it's clever to use extend.
- Var BicycleBell = function (bicycle ){
- // Inherit the data or method defined by this in BicycleDecorator
- BicycleBell. superclass. constructor. call (this, bicycle );
- }
- // Inherit the BicycleDecorator. prototype and add the BicycleBell. superclass to point to the BicycleDecorator. prototype
- Extend (BicycleBell, BicycleDecorator );
- // Add or modify
- BicycleBell. prototype. bell = function (){
- Console. log ("ding! Ding! Ding! ");
- }
- BicycleBell. prototype. getPrice = function (){
- Return this. bicycle. getPrice () + 100;
- }
Use the same method for instance Packaging
- var bicycleA = new ABicycle();
- bicycleA = new BicycleBell( bicycleA );
The above method is a better form of annotator mode. More comprehensive interfaces may need to be obtained through the original class Traversal method when defining the BicycleDecorator and BicycleDecorator. prototype objects.
Of course, these methods can be used as needed. Targeted processing is the most effective solution.