Design patterns

preface

Design patterns are designed for better code reuse, readability, reliability, and maintainability.

1. Adaptor mode: A kind of converter in life that makes incompatible things compatible

I don’t want you but I want you to be what I want and that transformation process is the adaptor mode which can be thought of as a “compensation mode”. For example: know that many UI components or libraries will render according to the specified data format, we use the same data in several places, different formats. Function 1 May use the following data format:

[{" day ", "Monday", "uv" : 6300}, {" day ":" Tuesday ", "uv" : 7100}, {" day ":" on Wednesday ", "uv" : 4300}, {" day ":" on Thursday, "" uv" : 3300}, {" day ":" Friday ", "uv" : 8300}, {" day ":" Saturday ", "uv" : 9300}, {" day ":" Sunday ", "uv" : 11300}]Copy the code

Function 2 is Echarts chart graphs need data format:

[" Tuesday ", "Tuesday "," Wednesday ", "Thursday "," Friday ", "Saturday "," Sunday "] // data of X-axis [6300. 7100, 4300, 3300, 8300, 9300, 11300] // data of coordinate pointsCopy the code

When the backend brother does not want to provide an interface, we need an adapter to solve the problem:

Function echartXAxisAdapter(res) {return res.map(item => item.day); } function echartDataAdapter(res) {return res.map(item => item.uv); }Copy the code

And I’m kind of introducing a library of plugins. I’m introducing a method, like the extend function in LoDash, where I merge two objects and I add a length property to the object to convert it to an array via array.from(). Can be adapter to achieve;

function extendLengthAdapter(obja, objb) {
    let obj =  _.extend(obja, objb);
    obj.length = Object.keys(obj).length;
    return obj;
}
Copy the code

The computational properties of vUE are actually an adapter idea. For example, if you lose a bet in a game, you need to write your name upside down. The original output interface of your name is not suitable, so you need to adapt a suitable adapter based on the original.

//HTML code <div id="example"> <p>Original message: "{{message}}"</p> <p>Computed reversed Message: "{{ reversedMessage }}"</p> </div> //javascirpt var vm = new Vue({ el: '#example', data: { message: 'Hello'}, computed: {// Calculate the getter reversedMessage for attributes: Return this.message.split('').reverse().join('')}}}) "Hello" Computed reversed message: "olleH"Copy the code

2. State mode

The use of the state pattern is particularly clear, with the following two scenarios: (1) An object’s behavior depends on its state, and it must change its behavior according to the state at runtime. ② An operation contains a large number of branch statements, and these branch statements depend on the state of the object. The state is typically a representation of one or more enumerated constants. In short, you can use state mode to simplify application scenarios when you encounter many if-else or switch equivalents: bulb state, traffic light switch, and other common favorites.

var Collection = function() { this.state = 'noCollection'; Off this.button = null; // Light switch button}; Collection.prototype.init = function() { var button = document.createElement('button'), self = this; Button. The innerHTML = '💗'; this.button = document.body.appendChild(button); this.button.onclick = function() { self.buttonWasPressed(); }}; Collection.prototype.buttonWasPressed = function() { if (this.state === 'noCollection') { this.collection() } else if (this.state === 'collectioned') { this.cancel(); }}; Collection.prototype.collection = function() { this.state = 'collectioned'; Console. log(' bookmarked successfully ')}; Collection.prototype.cancel = function() { this.state = 'noCollection'; Console. log(' unfavorites succeeded ')}; let coll = new Collection(); coll.init();Copy the code

Using state mode modification: With JavaScript’s delegate mechanism, state mode can be implemented as follows:

Const obj = {'noCollection': {press: function() {console.log(' collected successfully ') this.state = obj.collected; }}, 'collected': {press: function() {console.log(' collected successfully ') this.state = obj.noCollection; } }, } const Collection = function() { this.state = obj.noCollection; } Collection. The prototype. The init = function () {const BTN = document. The createElement method (' button) BTN. InnerHTML = '❤ ️'; document.body.append(btn) const self = this btn.addEventListener('click', The function () {self. The currentState. The call (self) / / the call through completion of the entrusted})} const coll1 = new Collection () coll1. The init ()Copy the code

The advantage is that there is no need for multi-level judgment, the press function is closed, need to increase when there is no need to judge the use of state mode after modifying the state of the press function and the contrast of the state mode.

Collection.prototype.buttonWasPressed = function() { if (this.state === 'noCollection') { this.collection() } else if (this.state === 'collecting') { this.collected(); }else if (this.state === 'collected') {// Add two states this.cancel(); }else if (this.state === 'canceling') { this.canceled(); }}; / / add two functions Collection. The prototype. The Collection = function () {this. State = 'collecting'; Console. log(' Favorites... ')}; Collection.prototype.collected = function() { this.state = 'collected'; Console. log(' bookmarked successfully ')}; Collection.prototype.cancel = function() { this.state = 'canceling'; Console. log(' Unbookmark... ')}; Collection.prototype.canceled = function() { this.state = 'noCollection'; Console. log(' unfavorites succeeded ')}; let coll3 = new Collection(); coll3.init();Copy the code

New additions to using state mode:

Const obj = {'noCollection': {press: function() {// Switch the current state to the next state console.log(' favorites... ') this.state = obj.collecting; }}, 'collecting': {press: function() {console.log(' collect successfully ') this.state = obj.collected; }}, 'collected': {press: function() {console.log(' collected... ') this.state = obj.canceling; }}, 'canceling': {press: function() {console.log() {this.state = obj.nocollection; }}},Copy the code

Common writing: The ~ buttonWasPressed method violates the open-closed principle. Every time you add or modify the light state, you need to change the buttonWasPressed code. This makes buttonWasPressed a very unstable method. All state-related behavior is encapsulated in the buttonWasPressed method. If we add states and other behaviors later, we won’t be able to predict how much this method will swell, and buttonWasPressed will be bulky and difficult to read and maintain. State pattern: to solve the problem of longhand ~ using object-oriented way will write a lot of classes, occupy a large space ~ due to dispersion in logic state class, while avoiding the unpopular conditional branch statements, but also has created the problem of scattered logic, we can’t see in a place like a whole state machine logic.

3. Decorator mode: Simply put, it is to add and extend the function of the original class without changing itself

Adding new functionality to an object does not change its original structure or functionality

Class Circle {draw() {console.log(" draw a Circle "); } } class Decorator { constructor(circle){ this.circle = circle; } draw() { this.circle.draw(); Console. log(" set red border ")}} // test code let circle = new circle (); circle.draw() let dec = new Decorator(circle); dec.draw();Copy the code

TS and ES7 already support the use of decorators, which can be used to decorate classes and methods, not functions. The method decorator expression is called as a function at run time, passing in the following three arguments: target — decorates the class as its constructor, and decorates the class’s methods as its prototype object. (The decorator is meant to “decorate” an instance of the class, but the instance has not yet been generated, so you can only decorate the prototype.) name — the name of the member. Descriptor — Attribute descriptor of a member.

Let border = (target, name, Descriptor) => {const oldValue = description. value // Replace the old draw method description. value = function () { oldValue.apply(null, Console. log(" I can set a red border ")}} class Circle {@border draw(s: String) {// If there is no decorator execute directly here console.log(s); } } let circle = new Circle(); circle.draw('i can draw circle'); Output: I can draw circle draw has been enhanced and I can set a red borderCopy the code

Observer mode and publish/subscribe mode

Concept: The Observer pattern refers to an object (Subject) that maintains a set of dependent objects (Observers) and notifies a set of observers of updates when the relevant state changes. In the Observer pattern, a Subject object has methods for adding, deleting, and notifying a list of observers, and so on, while an Observer has update methods, and so on. Defines a one-to-many dependency between objects in which all dependent objects are notified when an object’s state changes

 class Subject {
  constructor () {
    this.state = 0;
    this.observers = [];
  }
  getState () {
    return this.state;
  }
  setState (state) {
    this.state = state;
    this.notify();
  }
  notify () {
    this.observers.forEach(observer => {
      observer.update();
    })
  }
  attach (observer) {
    this.observers.push(observer);
  }
}


class Observer {
  constructor (name, subject) {
    this.name = name;
    this.subject = subject;
    this.subject.attach(this);
  }
  update () {
    console.log(`${this.name} update, state: ${this.subject.getState()}`);
  }
}

let sub = new Subject();
let observer1 = new Observer('o1', sub);
let observer2 = new Observer('o2', sub);

sub.setState(1);
Copy the code

The publish-subscribe pattern is a one-to-many dependency between objects. When an object’s state is sent to change, all dependent objects are notified of the state change. Subscriber registers the Event they want to Subscribe to the Event Channel. When Publisher publishes the Event to the Event Channel, that is, when the Event is triggered, The processing code registered with the dispatch center by the Fire Event subscriber.

< p style = “color: RGB (50, 50, 50); display: block; display: block; display: block; display: block; display: block; Execute the function in the cache list according to the event value (the publisher publishes the event to the dispatching center, and the dispatching center processes the code) 5, off method can unsubscribe according to the event value (unsubscribe) 6, once method only listens once, delete the cache function (subscribe once) after the call

List: {}, // On (event, fn) {let _this = this; // If there is no corresponding event value in the object, that is, there is no subscription, create a cache list for the event. The fn is added to the corresponding event cache list (_this. The list [event] | | (_this. The list [event] = [])). Push (fn); return _this; }, // listen once (event, fn) {let _this = this; function on () { _this.off(event, on); fn.apply(_this, arguments); } on.fn = fn; _this.on(event, on); return _this; }, // unsubscribe off (event, fn) {let _this = this; let fns = _this.list[event]; // If there is no corresponding fn in the cache list, false if (! fns) return false; if (! FNS && (fn. Length = 0); fns.length = 0; fns.length = 0; } else {if there is fn, iterate through the cache list to see which function is the same as the fn passed in. If there is fn, delete from the cache list directly. for (let i = 0, cbLen = fns.length; i < cbLen; i++) { cb = fns[i]; if (cb === fn || cb.fn === fn) { fns.splice(i, 1); break } } } return _this; }, // emit emit () {let _this = this; Let event = [].shift.call(arguments), FNS = [..._this.list[event]]; // If there is no fn in the cache list, return false if (! fns || fns.length === 0) { return false; ForEach (fn => {fn.apply(_this, arguments); }); return _this; }}; Function user1 (content) {console.log(' user1 subscribed to :', content); } function user2 (content) {console.log(' user2 subscribed :', content); } function user3 (content) {console.log(' user3 subscribed :', content); } function user4 (content) {console.log(' user4 subscribed :', content); } // Subscribe to eventEmitter. On ('article1', user1); eventEmitter.on('article1', user2); eventEmitter.on('article1', user3); // Unsubscribe from the user2 method eventEmitter. Off ('article1', user2); Once ('article2', user4) // Publish eventEmitter. Emit ('article1', 'Javascript published-subscribe '); EventEmitter. Emit ('article1', 'Javascript publish-subscribe '); EventEmitter. Emit ('article2', 'Javascript Observer mode '); EventEmitter. Emit ('article2', 'Javascript Observer mode '); // eventEmitter.on('article1', user3).emit('article1', 'test111'); 1 / * users subscribed to: Javascript publish-subscribe pattern users subscribed to: 3 Javascript publish-subscribe pattern users subscribed to: 1 Javascript publish-subscribe pattern users subscribed to: 3 Javascript publish-subscribe pattern users subscribed to: Javascript Observer mode */Copy the code

Differences: 1. In the observer mode, the observer knows the Subject, and the Subject keeps records of the observer. However, in the publish-subscribe model, publishers and subscribers are unaware of each other’s existence. They communicate only through the message broker. In the publish/subscribe model, components are loosely coupled, as opposed to the observer model. The observer pattern needs to be implemented in a single application address space, whereas publish-subscribe is more like the cross-application pattern.