Each mode is to refer to a variety of information summed up, this article is long, I hope to help you, if useful to you, please like to support a, is also to give me the motivation to write

Introduction to Design Patterns

Design patterns represent best practices and are generally adopted by experienced object-oriented software developers. Design pattern is a solution to the common problems faced by software developers during software development. These solutions have been developed by numerous software developers over a long period of trial and error.

A design pattern is a set of repeated, well-known, catalogued code design lessons. Design patterns are used to reuse code, make it easier for others to understand, and ensure code reliability. There is no doubt that design patterns are a win-win for yourself, others and systems. Design patterns make coding truly engineering. Design patterns are the cornerstones of software engineering, like the bricks and stones of a mansion.

Design pattern principles

  • S — Single Responsibility Principle
    • A program only does one thing
    • If the functionality is too complex, break it down and keep each part separate
  • O — OpenClosed Principle
    • Open to extension, closed to modification
    • When adding requirements, extend new code, not modify existing code
  • L — Liskov Substitution Principle
    • A subclass can override its parent class
    • Subclasses can appear wherever a parent class can
  • I-interface Segregation Principle Interface Segregation Principle
    • Keep interfaces single and separate
    • Similar to the single responsibility principle, there is a greater focus on interfaces
  • D — Dependency Inversion Principle
    • Interface – oriented programming relies on abstraction rather than concrete
    • The user only cares about the interface and not the implementation of the concrete class
SO, for example :(e.g. Promise)
  • The single responsibility principle: The logic in each then does one thing well
  • Open closed principle (open to extensions, closed to modifications) : If new requirements are added, extend then
For example, I would like to give you another exampleVigil – Improves all aspects of the code)
//result: false let checkType=function(STR, type) {switch (type) {case 'email': return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str) case 'mobile': return /^1[3|4|5|7|8][0-9]{9}$/.test(str); Case 'tel: return / ^ (0 \ d {2, 3} - \ d {7, 8}) (\ d {1, 4})? $/.test(str); default: return true; }}Copy the code

There are two problems:

  • If you want to add other rules, you have to add cases to the function. Add a rule and change it once! This violates the open-closed principle (open for extensions, closed for modifications). It also makes the entire API bloated and difficult to maintain.
  • For example, A value check is required on page A and A date check is required on page B, but the value check is required only on page A and the date check is required only on page B. If you keep adding cases. That is, page A adds verification rules that are only needed on page B, resulting in unnecessary overhead. The same goes for page B.

The suggested approach is to add an extended interface to the API:

let checkType=(function(){ let rules={ email(str){ return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str); }, mobile(str){ return /^1[3|4|5|7|8][0-9]{9}$/.test(str); }}; Return {check(STR, type){return rules[type]? rules[type](str):false; }, // addRule addRule(type,fn){rules[type]=fn; }}}) (); Console. log(checktype. check('188170239','mobile')); / / add amount checking rules checkType. AddRule (' money ', function (STR) {return / ^ [0-9] + (. [0-9] {2})? $/.test(str) }); Console. log(checktype. check('18.36','money'));Copy the code

For more details on this example, see -> Waiting for I – Refactoring – Improving aspects of your code

Classification of Design Patterns (23 design Patterns)

  • Create a type
    • The singleton pattern
    • The prototype pattern
    • The factory pattern
    • Abstract Factory pattern
    • Builder model
  • structured
    • Adapter mode
    • Decorator mode
    • The proxy pattern
    • The appearance model
    • The bridge model
    • Portfolio model
    • The flyweight pattern
  • Behavior type
    • Observer model
    • Iterator pattern
    • The strategy pattern
    • Template method pattern
    • Chain of Responsibility model
    • Command mode
    • Memo mode
    • The state pattern
    • Visitor pattern
    • The mediator pattern
    • Interpreter mode

The factory pattern

The factory pattern defines an interface for creating objects that subclasses decide which class to instantiate. This pattern delays instantiation of a class to subclasses. Subclasses can override interface methods to specify their own object types when they are created.

class Product {
    constructor(name) {
        this.name = name
    }
    init() {
        console.log('init')
    }
    fun() {
        console.log('fun')
    }
}

class Factory {
    create(name) {
        return new Product(name)
    }
}

// use
let factory = new Factory()
let p = factory.create('p1')
p.init()
p.fun()

Copy the code
Applicable scenario
  • The factory pattern is ideal if you don’t want strong coupling between one subsystem and the larger object, but want the runtime to pick and choose from many subsystems
  • Encapsulate the new operation simply. When encountering new, we should consider whether to use factory mode.
  • When you need to create different instances that have the same behavior depending on the specific environment, you can use the factory pattern to simplify the implementation process and reduce the amount of code required for each object, which helps eliminate the coupling between objects and provides greater flexibility
advantages
  • The process of creating an object can be complex, but we only need to care about the result.
  • Constructor and creator separated, in accordance with the “open closed principle”
  • A caller who wants to create an object needs only to know its name.
  • High scalability. If you want to add a product, just extend a factory class.
disadvantages
  • When new products are added, new specific product classes need to be written, which increases the complexity of the system to a certain extent
  • Considering the scalability of the system, it is necessary to introduce an abstraction layer, which is used to define the client code, increasing the abstraction and difficulty of understanding the system
When not to use it

When applied to the wrong type of problem, this pattern introduces a great deal of unnecessary complexity into the application. Unless providing an interface for creating objects is a design goal of the library or framework we write, I recommend using explicit constructors to avoid unnecessary overhead.

Because of the fact that the object creation process is efficiently abstracted behind an interface, this also poses problems for unit tests that depend on how complex the process may be.

example
  • The once familiar $() of JQuery is a factory function that creates elements depending on the parameters passed in or finds elements in context to create the appropriate JQuery object
Class jQuery {constructor(selector) {super(selector)} add() {}} window.$= function(selector) {return  new jQuery(selector) }Copy the code
  • Asynchronous components of VUE

In large applications, we may need to break the application into smaller code blocks and load a module from the server only when needed. For simplicity, Vue allows you to define your component as a factory function that asynchronously parses your component definition. Vue fires the factory function only when the component needs to be rendered, and caches the result for future re-rendering. Such as:

Vue.component('async-example', function (resolve, Reject) {setTimeout(function () {resolve({template: '<div>I am async! </div>' }) }, 1000) })Copy the code

The singleton pattern

A class has only one instance and provides a global access point to access it.

Class LoginForm {constructor() {this.state = 'hide'} show() {if (this.state === 'show') {alert(' already shown ') return} This. State = 'show' console. The log (' login box shows success ')} hide () {if (this. State = = = 'hide') {alert (' has hidden) return} this. State = 'hide' console.log(' hide' console.log ')}} LoginForm. GetInstance = (function () {let instance return function () {if (! instance) { instance = new LoginForm() } return instance } })() let obj1 = LoginForm.getInstance() obj1.show() let obj2 = LoginForm.getInstance() obj2.hide() console.log(obj1 === obj2)Copy the code

advantages

  • Partition the namespace and reduce global variables
  • Improve modularity, organize your code in a global variable name, in a single location, easy maintenance
  • And it will only be instantiated once. Simplifies code debugging and maintenance

disadvantages

  • Because the singleton pattern provides a single point of access, it can lead to strong coupling between modules that is not conducive to unit testing. You cannot test a class that calls a method from a singleton alone, but only with that singleton as a unit.

Example scenario

  • Define namespaces and implement branching methods
  • The login dialog
  • Store in vuex and Redux

Adapter mode

The interface of one class is converted into another interface to meet user requirements, so that interface incompatibilities between classes can be solved through adapters.

Class Plug {getName() {return 'iPhone charger '; } } class Target { constructor() { this.plug = new Plug(); } getName() {return this.plug.getName() + 'adapter Type-c charging head '; } } let target = new Target(); target.getName(); // iPhone charging head adapter to Type-C charging headCopy the code

advantages

  • You can have any two unrelated classes run together.
  • Improved class reuse.
  • Adapt objects, adapt libraries, adapt data

disadvantages

  • The creation of additional objects, not directly called, has some overhead (and unlike the proxy pattern, performance is optimized at some function points)
  • If the adapter pattern is not necessary, consider refactoring, and if so, document as well as possible

scenario

  • Integrate third-party SDKS
  • Encapsulate old interface
Ajax ({url: '/getData', type: 'Post', dataType: 'json', data: {test: 111}}) done (function () {}) / / because of historical reasons, the code is all: / / $. Ajax ({... Var $= {ajax: function (options) {return ajax(options)}}Copy the code
  • The computed vue
<template> <div id="example"> <p>Original message: "{{ message }}"</p> <! -- Hello --> <p>Computed reversed message: "{{ reversedMessage }}"</p> <! -- olleH --> </div> </template> <script type='text/javascript'> export default { name: 'demo', data() { return { message: 'Hello' } }, computed: { reversedMessage: function() { return this.message.split('').reverse().join('') } } } </script>Copy the code
The data in the original data does not meet the current requirements, and is adapted to the format we need through the rules of calculating attributes. The original data is not changed, but only the expression form of the original data

The difference between

The adapter and proxy patterns are similar

  • Adapter pattern: Provide a different interface (such as different versions of plugs)
  • Proxy mode: provides identical interfaces

Decorator pattern

  • Adding additional responsibilities to an object dynamically is an alternative to implementing inheritance
  • By wrapping and extending the original object without changing it, the original object can meet the user’s more complex needs without affecting other objects derived from this class
}} class Decorator {constructor(Cellphone) {this. Cellphone = Create () this.cellphone. Create () this.createshell (cellphone)} createShell() {console.log(' Generate mobile case ')}} // Create () console.log('------------') Let dec = new Decorator(cellphone) dec.create()Copy the code

Example scenario

  • For example, there are four types of bicycles, and we have defined a list for each of them

Alone in the class. Now, each bike has to be fitted with three accessories: headlights, tail lights and bells. If you use inheritance to create subclasses for each bike, you need 4×3 = 12 subclasses. But if you dynamically group objects like headlights, taillights, and bells onto a bike, you only need to add three additional classes

  • Decorator ES7 Decorator
  • core-decorators

advantages

  • The decorator class and the decorator class are decoupled from each other by caring only about their core business.
  • Convenient dynamic extension functionality, and provides more flexibility than inheritance.

disadvantages

  • Multi-layer decoration is more complicated.
  • Our application architecture is often complicated by the introduction of many small objects that look similar but do very different things

The proxy pattern

Is to provide a proxy or placeholder for an object to control access to it

Assuming that A receives flowers when she is in A good mood, xiaoming has A chance of success

60%, and when A received flowers when she was in A bad mood, the success rate of Xiao Ming’s confession approached 0. Xiao Ming and A have only known each other for two days and still can’t tell when A is in A good mood. If you send the flowers to A in an inappropriate way, there is A high possibility that the flowers will be thrown away directly. Xiao Ming got the flowers after eating instant noodles for seven days. However, A’s friend B knows A very well, so Xiaoming just gives the flower to B, and B will monitor A’s mood change and then choose A to give the flower to A when A is in A good mood, the code is as follows:

let Flower = function() {} let xiaoming = { sendFlower: function(target) { let flower = new Flower() target.receiveFlower(flower) } } let B = { receiveFlower: function(flower) { A.listenGoodMood(function() { A.receiveFlower(flower) }) } } let A = { receiveFlower: Function (flower) {console.log(' received flowers '+ flower)}, listenGoodMood: function(fn) { setTimeout(function() { fn() }, 1000) } } xiaoming.sendFlower(B)Copy the code

scenario

  • HTML element event broker
<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  let ul = document.querySelector('#ul');
  ul.addEventListener('click', event => {
    console.log(event.target);
  });
</script>
Copy the code
  • ES6 proxy Ruan Yifeng Proxy
  • JQuery. The proxy () method

advantages

  • The proxy mode can separate the proxy object from the called object and reduce the coupling degree of the system. The proxy mode acts as an intermediary between the client and the target object, thus protecting the target object
  • Proxy objects extend the functionality of target objects; By modifying the proxy object is ok, in line with the open and close principle;

disadvantages

The speed of processing requests can vary, and indirect access has overhead

The difference between

The decorator pattern is similar in implementation to the proxy pattern

  • Decorator mode: extended functions, original functions remain unchanged and can be used directly
  • Proxy mode: shows the original functionality, but with limitations

The appearance model

Providing a consistent interface for a set of interfaces for a subsystem defines a high-level interface that makes the subsystem easier to use

  1. Compatible with browser event binding
let addMyEvent = function (el, ev, fn) {
    if (el.addEventListener) {
        el.addEventListener(ev, fn, false)
    } else if (el.attachEvent) {
        el.attachEvent('on' + ev, fn)
    } else {
        el['on' + ev] = fn
    }
}; 
Copy the code
  1. Encapsulated interface
let myEvent = { // ... stop: e => { e.stopPropagation(); e.preventDefault(); }};Copy the code

scenario

  • Early in the design process, there should be a conscious separation of the two different layers, such as the classic three-tier structure, with a Facade between the data access layer and the business logic layer, and between the business logic layer and the presentation layer
  • During development, subsystems tend to become more complex as they evolve through refactoring, and adding a Facade can provide a simple interface that reduces dependencies between them.
  • Using a Facade is also appropriate when maintaining a large legacy system that may already be difficult to maintain. Develop a Facade class for the system to provide a clear interface to poorly designed and highly complex legacy code to allow the new system to interact with Facade objects. The Facade interacts with the legacy code for all the complex work.

Reference: Big talk design patterns

advantages

  • Reduce system interdependence.
  • Increase flexibility.
  • Improved security

disadvantages

  • Inconsistent open and close principle, if you want to change things is very troublesome, inheritance rewrite are not appropriate.

Observer model

Defines a one-to-many relationship, let more observer object at the same time to monitor a theme, the theme of the state of an object changes will notify all observers object, allowing them to automatically update themselves, when an object changes to other objects, and it does not know how many objects need to be changed, You should consider using the observer model.

  • Publish & Subscribe
  • More than a pair of
// The theme saves the state, Class Subject {constructor() {this.state = 0 this.state = []} getState() {return this.state} setState(state) { this.state = state this.notifyAllObservers() } notifyAllObservers() { this.observers.forEach(observer => {obser.update ()})} attach(observer) {this.observers. Push (observer)}} // 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 s = new subject () let o1 = new Observer('o1', s) let o2 = new Observer('02', s) s.setState(12)Copy the code

scenario

  • DOM events
document.body.addEventListener('click', function() { console.log('hello world! '); }); document.body.click()Copy the code
  • Vue responsive

advantages

  • Supports simple broadcast communication that automatically notifies all objects that have subscribed
  • The abstract coupling between the target object and the observer can be independently extended and reused
  • Increased flexibility
  • What the observer pattern does is decouple, making both sides of the coupling dependent on the abstract rather than the concrete. So that the changes in each side don’t affect the changes in the other side.

disadvantages

Overuse can weaken the relationship between objects and make programs difficult to track, maintain and understand


The state pattern

Allows an object to change its behavior when its internal state changes, and the object appears to modify its class

Class State {constructor(State) {this. State = State} handle(context) {console.log(' this is ${this.state} light`) context.setState(this) } } class Context { constructor() { this.state = null } getState() { return  this.state } setState(state) { this.state = state } } // test let context = new Context() let weak = new State('weak') // Weak light weak. Handle (context) console.log(context.getState()) Handle (context) console.log(context.getState()) // Disable off.handle(context) console.log(context.getState())Copy the code

scenario

  • An object’s behavior depends on its state, and it must change its behavior at runtime based on its state
  • An operation contains a large number of branch statements, and these branch statements depend on the state of the object

advantages

  • Define the relationship between state and behavior, encapsulated in a class, more intuitive and clear, easy to change
  • States and actions are independent of each other
  • Use objects instead of strings to record the current state, making state switching more obvious

disadvantages

  • Many state classes are defined in the system
  • Logic scattered

Iterator pattern

Provides a way to order elements in an aggregate object without exposing the internal representation of the object.

class Iterator { constructor(conatiner) { this.list = conatiner.list this.index = 0 } next() { if (this.hasNext()) { return this.list[this.index++] } return null } hasNext() { if (this.index >= this.list.length) { return false } return Class Container {constructor(list) {this.list = list} getIterator() {return new Iterator(this)} let container = new Container([1, 2, 3, 4, 5]) let iterator = container.getIterator() while(iterator.hasNext()) { console.log(iterator.next()) }Copy the code

Example scenario

  • Array.prototype.forEach
  • JQuery $. In the each ()
  • ES6 Iterator

The characteristics of

  • Access the content of an aggregate object without exposing its internal representation.
  • Provides a unified interface for traversing different collection structures, enabling the same algorithm to operate on different collection structures

conclusion

The iterator pattern can be used if the internal structure of a collection is often variable and you do not want to expose the internal structure, but you want the client code to transparently access its elements


The bridge model

The Bridge pattern separates the abstract part from its implementation so that they can all change independently.

class Color { constructor(name){ this.name = name } } class Shape { constructor(name,color){ this.name = name this.color = color} draw(){console.log(' ${this.color.name} ${this.name} ')}} // test let red = new color ('red') let yellow = new Color('yellow') let circle = new Shape('circle', red) circle.draw() let triangle = new Shape('triangle', yellow) triangle.draw()Copy the code

advantages

  • Helps manage the components independently and decouples abstraction from implementation
  • Improve scalability

disadvantages

  • A large number of classes will result in an increase in development costs and possibly a reduction in performance.

Portfolio model

  • Group objects into a tree structure to represent a whole-part hierarchy.
  • Through the polymorphic representation of objects, users can use single objects and combined objects coherently.
Class TrainOrder {create () {console.log(' create train ticket order ')}} class HotelOrder {create () {console.log(' create HotelOrder ')}} class  TotalOrder { constructor () { this.orderList = [] } addOrder (order) { this.orderList.push(order) return this } create ForEach (item => {item.create()}) return this}} let train = new TrainOrder() let hotel = new HotelOrder() let total = new TotalOrder() total.addOrder(train).addOrder(hotel).create()Copy the code

scenario

  • Represents an object-whole hierarchy
  • The user is expected to ignore the difference between a composite object and a single object, and the user will use all objects (methods) in the composite structure uniformly

disadvantages

If too many objects are created through the composite pattern, they may become unaffordable for the system.


The prototype pattern

The prototype pattern refers to the type of object being created by pointing to prototype instances and creating new objects by copying those prototypes.

class Person {
  constructor(name) {
    this.name = name
  }
  getName() {
    return this.name
  }
}
class Student extends Person {
  constructor(name) {
    super(name)
  }
  sayHello() {
    console.log(`Hello, My name is ${this.name}`)
  }
}

let student = new Student("xiaoming")
student.sayHello()
Copy the code

The prototype pattern, which creates a shared prototype and copies it to create new classes for creating duplicate objects, improves performance.


The strategy pattern

Define a set of algorithms, encapsulate them one by one, and make them interchangeable

< HTML > <title> <meta Content ="text/ HTML; charset=utf-8" http-equiv="Content-Type"> </head> <body> <form id = "registerForm" method="post" Action = "http://xxxx.com/api/register" > userName: < input type = "text" name = "userName" > password: <input type="text" name="password"> <input type="text" name="phoneNumber"> <button type="submit" </button> </form> <script type="text/javascript"  const strategies = { isNoEmpty: function (value, errorMsg) { if (value === '') { return errorMsg; } }, isNoSpace: function (value, errorMsg) { if (value.trim() === '') { return errorMsg; } }, minLength: function (value, length, errorMsg) { if (value.trim().length < length) { return errorMsg; } }, maxLength: function (value, length, errorMsg) { if (value.length > length) { return errorMsg; } }, isMobile: function (value, errorMsg) { if (! /^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) { return errorMsg; Constructor () {this.cache = []} add(dom, rules) {for(let I = 0, rule; rule = rules[i++];) { let strategyAry = rule.strategy.split(':') let errorMsg = rule.errorMsg this.cache.push(() => { let strategy = strategyAry.shift() strategyAry.unshift(dom.value) strategyAry.push(errorMsg) return strategies[strategy].apply(dom, strategyAry) }) } } start() { for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {let errorMsg = validatorFunc() if (errorMsg) {return errorMsg}}}} // call let registerForm = document.getElementById('registerForm') let validataFunc = function() { let validator = new Validator() Add (registerform. userName, [{strategy: 'isNoEmpty', errorMsg: 'userName cannot be empty'}, {strategy: 'isNoSpace', errorMsg: 'Whitespace is not allowed'}, {strategy: 'minLength:2', errorMsg: }]) validator.add(registerform. password, [{strategy: 'minLength:6', errorMsg: 'password length can not less than 6}]) the validator. Add (registerForm. PhoneNumber, [{strategy:' isMobile, errorMsg: }]) return validator.start()} registerform.onsubmit = function() {let errorMsg = validataFunc() if (errorMsg) { alert(errorMsg) return false } } </script> </body> </html>Copy the code

Example scenario

  • If there are many classes in a system that are distinguished only by their ‘behavior’, then using the policy pattern can dynamically let an object choose one behavior among many behaviors.
  • A system needs to dynamically choose one of several algorithms.
  • Form validation

advantages

  • Multiple conditional selection statements can be effectively avoided by using combination, delegate and polymorphism techniques
  • Provides perfect support for the open-closed principle, encapsulating algorithms in a separate strategy, making them easy to switch, understand, and extend
  • Using composition and delegation to give the Context the ability to perform the algorithm is also a lighter alternative to inheritance

disadvantages

  • Many policy classes or policy objects are added to the program
  • To use the strategy pattern, it is necessary to understand all strategies, and to understand the differences between strategies in order to choose a suitable strategy

The flyweight pattern

The reuse of a large number of fine – grained objects is effectively supported by sharing technology. The system only uses a small number of objects, and these objects are very similar, the state change is very small, can realize the object multiple reuse. Because the share mode requires that the objects that can be shared must be fine grained objects, it is also called lightweight mode, which is an object structure mode

ExamCarNum = 0 // ExamCar {constructor(carType) {examCarNum++ this. CarId = examCarNum this.carType = carType ? 'Manual' : /* examine(candidateId) {return new Promise((resolve) => { This. UsingState = true console.log(' candidateId} - ${this.carType} - ${this.carId}) setTimeout(() => {this.usingState = false console.log(' %c candidate - ${candidateId}) ${this.carType} - ${this.carId} 'color:#f40') resolve(), math.random () * 2000)}))}} / [], // candidatequeue: []. // candidateList (candidateList) {candidateList. ForEach (candidateId => this.registCandidate(candidateId)) }, // if (examCar) {// if (examCar) {// if (examCar) {// if (examCar) {// if (examCar) { ExamCar. Examine (candidateId) // Then (() => {const nextCandidateId = this._candidatequue. Length && this._candidatequue. Shift ()) nextCandidateId && this.registCandidate(nextCandidateId) }) } else this._candidateQueue.push(candidateId) }, */ initexamcar (manualExamCarNum) {for (let I = 1; i <= manualExamCarNum; I++) {this._pool.push(new ExamCar(true))}}, /* getManualExamCar() {return this._pool.find(car =>! Car. UsingState)}} ManualExamCarPool. InitManualExamCar (3) / / there are three driving test car ManualExamCarPool. RegistCandidates ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) // Ten candidates come to take the examCopy the code

Example scenario

  • File upload requires the creation of multiple file instances
  • The free element pattern should be considered if an application uses a large number of objects, and these large numbers of objects cause significant storage overhead

advantages

  • Greatly reduce the creation of objects, reduce the system memory, improve efficiency.

disadvantages

  • It increases the complexity of the system, the need to separate out the external state from the internal state, and the external state has inherent properties,

It should not change as the internal state changes, otherwise it will cause chaos in the system

Template method pattern

The template method pattern consists of two parts, the first part is the abstract parent class, the second part is the concrete implementation child class. The algorithm framework of a subclass is usually encapsulated in an abstract superclass, including implementing some common methods and encapsulating the execution order of all methods in the subclass. By inheriting this abstract class, subclasses also inherit the entire algorithm structure and can choose to override the methods of the parent class.

class Beverage { constructor({brewDrink, AddCondiment}) {this.brewdrink = brewDrink this.addcondiment = addCondiment} BoilWater () {console.log(' Water has boiled === shared ')} /* Fill the cup, /* Template method */ init() {this.boilwater () this.brewdrink () this.pourcup () {console.log(' pour into a cup === share ')} This.addcondiment ()}} /* coffee */ const coffee = new Beverage({/* brew coffee, overlay abstract method */ brewDrink: Function () {console.log(' make coffee ')}, /* addCondiment */ addCondiment: function() {console.log(' add milk and sugar ')}}) coffee.init()Copy the code

Example scenario

  • Implement the invariant parts of an algorithm once and leave the mutable behavior to subclasses
  • Common behaviors in subclasses should be extracted and aggregated into a common parent class to avoid code duplication

advantages

  • Extract common code parts, easy to maintain

disadvantages

  • Increased system complexity, mainly by increasing abstract classes and inter-class relationships

Chain of Responsibility model

To avoid coupling between the sender and receiver of the request by giving multiple objects the opportunity to process it, link the objects into a chain and pass the request along the chain until one object processes it

// Leave approval, Class Action {constructor(name) {this.name = name this.nextAction = null} setNextAction(Action) {constructor(name) {this.name = name this.nextAction = null} NextAction = Action} handle() {console.log(' ${this.name} approve ') if (this.nextAction! Nextaction.handle ()}} let a2 = new Action(" manager ") let a3 = new Action(" director ") a1.setNextAction(a2) a2.setNextAction(a3) a1.handle()Copy the code

Example scenario

  • Events bubble in JS
  • The scope chain
  • Prototype chain

advantages

  • Reduce coupling. It decouples the sender and receiver of the request.
  • Simplifies the object. The object does not need to know the structure of the chain
  • Enhanced flexibility in assigning responsibilities to objects. Responsibilities can be dynamically added or removed by changing members in the chain or reordering them
  • It is convenient to add new request handling classes.

disadvantages

  • There is no guarantee that a request will be processed by the nodes in the chain, in which case you can add a guaranteed receiver node at the end of the chain to handle requests that are leaving the chain.
  • Make a lot of nodes in the program object, may request again in the process, most of the nodes did not play a substantial role. Their role is simply to get the request passed on, in terms of performance, to avoid the performance cost that comes with a long chain of responsibilities.

Command mode

Encapsulating a request as an object allows you to parameterize clients with different requests, queue requests or log requests, and provide command undo and recovery capabilities.

// Class Receiver {execute() {console.log(' Receiver performs the request ')}} // Class Command {constructor(Receiver) { This.receiver = receiver} execute () {console.log(' command '); This.receiver. Execute () {constructor(command) {this.receiver = command} invoke() {this.receiver. Console. log(' start ') this.man.execute ()}} // warehouse const warehouse = new Receiver(); // const order = new Command(warehouse); // const client = new Invoker(order); client.invoke()Copy the code

advantages

  • Encapsulate commands to make them easy to expand and modify
  • The command sender and receiver are decoupled so that the command sender can execute the command without knowing the specific execution process

disadvantages

  • Using command mode can cause some systems to have too many specific command classes.

Memo mode

Capture the internal state of an object and store the state outside of the object without breaking encapsulation. This allows you to restore the object to its saved state later.

Constructor (content){this.content = content} getContent(){return this.content}} // Memento{constructor(content){this.content = content CareTaker { constructor(){ this.list = [] } add(memento){ this.list.push(memento) } get(index){ return this.list[index] Constructor (){this.content = null} setContent(content){this.content = content} getContent(){ return this.content } saveContentToMemento(){ return new Memento(this.content) } GetContentFromMemento (memento){this.content = memento.getContent()}} let editor = new editor () let careTaker =  new CareTaker() editor.setContent('111') editor.setContent('222') careTaker.add(editor.saveContentToMemento()) editor.setContent('333') careTaker.add(editor.saveContentToMemento()) editor.setContent('444') console.log(editor.getContent()) //444 editor.getContentFromMemento(careTaker.get(1)) console.log(editor.getContent()) //333 editor.getContentFromMemento(careTaker.get(0)) console.log(editor.getContent()) //222Copy the code

Example scenario

  • Paging controls
  • Revocation of the component

advantages

  • It provides a mechanism for the user to restore the state, enabling the user to easily return to a historical state

disadvantages

  • Consume resources. If a class has too many member variables, it will take up a lot of resources, and each save will consume a certain amount of memory.

The mediator pattern

Decouple objects from each other. By adding a mediator object, all related objects communicate through the mediator object rather than referring to each other, so when an object changes, the mediator object only needs to be notified. The mediator loosens the coupling between objects and can change their interactions independently. The mediator pattern transforms the netted many-to-many relationship into a relatively simple one-to-many relationship (similar to the observer pattern, but unidirectional and centrally managed by the mediator).

class A {
    constructor() {
        this.number = 0
    }
    setNumber(num, m) {
        this.number = num
        if (m) {
            m.setB()
        }
    }
}
class B {
    constructor() {
        this.number = 0
    }
    setNumber(num, m) {
        this.number = num
        if (m) {
            m.setA()
        }
    }
}
class Mediator {
    constructor(a, b) {
        this.a = a
        this.b = b
    }
    setA() {
        let number = this.b.number
        this.a.setNumber(number * 10)
    }
    setB() {
        let number = this.a.number
        this.b.setNumber(number / 10)
    }
}

let a = new A()
let b = new B()
let m = new Mediator(a, b)
a.setNumber(10, m)
console.log(a.number, b.number)
b.setNumber(10, m)
console.log(a.number, b.number)
Copy the code

Example scenario

  • There are complex reference relationships among objects in the system, which leads to the disorganized structure of the dependency relationship between them and the difficulty of reuse the object
  • You want an intermediate class to encapsulate behavior in multiple classes without generating too many subclasses.

advantages

  • Loose coupling between objects and the ability to change their interactions independently
  • The one-to-many relationship between intermediaries and objects replaces the net-like many-to-many relationship between objects
  • If the complexity of the coupling between objects makes maintenance difficult, and the coupling increases rapidly from project to project, an intermediary needs to refactor the code

disadvantages

  • A new mediator object is added to the system, and because of the complexity of the interaction between the objects, the complexity of the mediator object is transferred to the mediator object, making the mediator object often huge. The mediator object itself is often a difficult object to maintain.

Interpreter mode

Given a language, define a representation of its grammar and define an interpreter that uses that representation to interpret sentences in the language.

This example comes from Xintan blog

class Context { constructor() { this._list = []; This. _sum = 0; } get sum() {return this._sum; } set sum(newValue) { this._sum = newValue; } add(expression) { this._list.push(expression); } get list() { return [...this._list]; } } class PlusExpression { interpret(context) { if (! (context instanceof Context)) { throw new Error("TypeError"); } context.sum = ++context.sum; } } class MinusExpression { interpret(context) { if (! (context instanceof Context)) { throw new Error("TypeError"); } context.sum = --context.sum; **/ const context = new context (); / / in turn add: addition | | addition subtraction expression context. The add (new PlusExpression ()); context.add(new PlusExpression()); context.add(new MinusExpression()); / / in sequence: addition | | addition subtraction expression context. The list. The forEach (expression = > expression. Interpret (context)); console.log(context.sum);Copy the code

advantages

  • Easy to change and extend grammars.
  • Because classes are used in the interpreter schema to represent the grammar rules of the language, the grammar can be changed or extended through mechanisms such as inheritance

disadvantages

  • The execution is inefficient, and the interpreter mode uses a lot of loops and recursive calls, making it slow to interpret more complex sentences
  • Complex grammars are difficult to maintain

Visitor pattern

Represents an operation that operates on elements in an object structure. It allows you to define new operations on elements without changing their classes.

/ / visitors to a class Visitor {constructor () {} visitConcreteElement (ConcreteElement) {ConcreteElement. Operation ()}} / / element class class ConcreteElement{ constructor() { } operation() { console.log("ConcreteElement.operation invoked"); } accept(visitor) { visitor.visitConcreteElement(this) } } // client let visitor = new Visitor() let element = new ConcreteElement() element.accept(visitor)Copy the code

Example scenario

  • The class corresponding to an object in an object structure rarely changes, but it is often necessary to define new operations on this object structure
  • You need to do many different and unrelated operations on objects in an object structure, and you want to avoid having those operations “contaminate” the classes of those objects, and you don’t want to modify those classes when you add new operations.

advantages

  • Consistent with the principle of single responsibility
  • Excellent scalability
  • flexibility

disadvantages

  • Specific elements disclose details to visitors, violating the Demeter principle
  • Violates the dependency inversion principle by relying on concrete classes instead of abstractions.
  • Specific element changes are difficult

Brother, if it helps you, please give me a thumbs up as well as support

The resources

  • Double Yue -Javascript design pattern system explanation and application
  • JavaScript design patterns and development practices
  • Uncle Tom’s design pattern
  • www.yuque.com/wubinhp/uxi…