I. Design principles

What are design patterns?

Design pattern is a general solution to a certain kind of problems that appear repeatedly in the process of software design and development. Design patterns are more guiding ideas and methodologies than off-the-shelf code, and of course each design pattern has a specific implementation in each language. Learning design patterns is more about understanding the inner thoughts and problems of each pattern. After all, this is the best practice summed up by countless previous experiences, and the code implementation is an aid to deeper understanding.

  • According to which kind of thought or standard to achieve the function
  • The same function, can be designed in different ways
  • Design can only work if requirements are constantly changing

SOLID five design principles

The first letter To refer to concept
S Single responsibility principle The single function principle says that objects should have only one concept of a single function
O Open and closed principle The open closed principle is the notion that the body of software should be open to extension but closed to modification
L Richter’s substitution principle The Richter substitution principle holds that an object in a program should be a concept that can be replaced by its subclasses without changing the correctness of the program
I Interface isolation Rule The interface isolation principle is the concept that multiple specific client interfaces are better than one broad purpose interface
D Dependency reversal principle Dependency injection is an implementation of the dependency reversal principle, which states that a method should follow the concept of relying on an abstraction rather than an instance.

O Open and closed principle

Open Closed Principle

  • Open for extension, closed for modification
  • When requirements are added, extend new code rather than modify existing code
  • The open closed principle is a general principle in design patterns
  • Add extension points to areas that may change in the near future and where the change is significant. Too many extension points will reduce readability
class Customer {
  constructor(public rank: string) {}}class Product {
  constructor(public name: string.public price: number) {}
  cost(customer: Customer) {
    switch (customer.rank) {
      case 'member':
        return this.price * 0.8;
      case 'vip':
        return this.price * 0.6;
      default:
        return this.price; }}}let p1 = new Product('Laptop'.1000);
let member = new Customer('member');
let vip = new Customer('vip');
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
Copy the code

Now let’s change the code

class Customer {
  constructor(public rank: string.public discount: number = 1) {}
  getDiscount() {
    return this.discount; }}class Product {
  constructor(public name: string.public price: number) {}
  cost(customer: Customer) {
    /* switch (customer.rank) { case 'member': return this.price * .8; case 'vip': return this.price * .6; default: return this.price; } * /
    return this.price * customer.getDiscount(); }}let p1 = new Product('Laptop'.1000);
let member = new Customer('member'.0.8);
let vip = new Customer('vip'.0.6);
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
Copy the code

For example, axios, the promise-based request library we use in daily development, also uses design patterns

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
let instance: AxiosInstance = axios.create();
instance.interceptors.request.use((config: AxiosRequestConfig) = > {
  config.url = 'http://localhost:8080' + config.url;
  return config;
});

instance.interceptors.response.use(response= > {
  if(response.status ! = =200|| response.data.code ! =0) {
    return Promise.reject(response);
  } else {
    returnresponse.data.data; }});/** * {code:0,data:{id:1,name:'golderbrother'}} */
instance({
  url: '/api/users'
}).then(
  result= > {
    console.log(result);
  },
  error= > {
    console.error(error); });Copy the code

S Single responsibility principle

Single responsibility principle

  • A class or module performs only one function, and if the function is particularly complex, it is broken up
  • A single responsibility can reduce class complexity and improve code readability and maintainability
  • When a class has too many lines of code, too many methods, too many functions, and too many responsibilities, it’s time to split up the class
  • Break upYou can’t overdo it ifBreak upExcessive losscohesionandmaintainability

You can refer to the following two tool library source code

  • lodashjs
  • jquery

L Richter substitution principle

Liskov Substitution Principle

  • All references to the base class must transparently use the objects of its subclasses
  • A child class can replace a parent class, and the user may not need to know whether it is a parent class or a child class, or vice versa
  • Richter's substitution principleOpen closed principle is the basis of the implementation, when the program design as far as possible to useBase class definition and reference, and then the runtime decides which subclass to use
  • Richter's substitution principleIt can improve code reuse, improve code extensibility, and increase coupling
  • So this principle, as opposed to polymorphism, is how do you design a class, and if a subclass violates a function of its parent class, is it violatingRichter's substitution principle

abstract class AbstractDrink {
  abstract getName(): string;
}
class CocaCola extends AbstractDrink {
  getName(): string {
    return 'coke'; }}class Sprite extends AbstractDrink {
  getName(): string {
    return 'spirits'; }}class Fanta extends AbstractDrink {
  getName(): string {
    return 'the fender'; }}class Customer {
  drink(drink: AbstractDrink) {
    console.log('喝'+ drink.getName()); }}let customer = new Customer();
let cocaCola = new CocaCola();
let sprite = new Sprite();
let fanta = new Fanta();
customer.drink(cocaCola);
customer.drink(sprite);
customer.drink(fanta);
Copy the code
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
  render() {
    return <div>App </div>; }}let element = React.createElement(App);
ReactDOM.render(element, document.getElementById('root'));
Copy the code
abstract class AbstractDrink {
  abstract getName(): any;
}
class CocaCola extends AbstractDrink {
  getName(): any {
    return 100; }}Copy the code

D depend on the inversion principle

Dependence Inversion Principle

  • Interface – oriented programming that relies on abstractions rather than implementations
  • We are required to refer to higher-level abstraction classes when passing parameters in program code or in association relationships
  • The user cares only about the interface and not about the implementation of the concrete class

Like SingleDog’s own new GirlFriend

abstract class GirlFriend {
  public age: number;
  public height: number;
  public abstract cook(): void;
}
class LinZhiLing extends GirlFriend {
  public cook(): void{}}class HanMeiMei extends GirlFriend {
  public cook(): void{}}class SingleDog {
  constructor(public girlFriend: GirlFriend) {}}let s1 = new SingleDog(new LinZhiLing());
let s2 = new SingleDog(new HanMeiMei());
Copy the code
import { createStore } from 'redux';
let store = createStore(state= > state);
export interface Action<T = any> {
  type: T;
}
export interface AnyAction extends Action {
  // Allows any extra properties to be defined in an action.
  [extraProps: string] :any;
}
let action: AnyAction = { type: 'increment', payload: 5 };
store.dispatch(action);
Copy the code

I Interface isolation rule

Interface Segregation Principle

  • Keep the interfaces single and independent to avoid fat interfaces
  • A client should not rely on interfaces it does not need, and dependencies between classes should be built on the smallest interface
  • The interface should be as detailed as possible, and the methods in the interface should be as few as possible
  • Similar to the single responsibility principle, more focus on interfaces
interface IUserManager {
  updateUserInfo(): void;
  updatePassword(): void;
}
interface IProductManager {
  updateProduct(): void;
  updatePrice(): void;
}
Copy the code

For example, if you want a Flying plane to run in the water, how do you do it?

Following the interface isolation principle, we can do this

The pseudocode is as follows:

interface Running {
  run(): void;
}
interface Flying {
  fly(): void;
}
interface Swimming {
  swim(): void;
}
class Automobile implements Running, Flying, Swimming {
  run() {}
  fly() {}
  swim() {}
}
Copy the code

Other principles

Demeter’s law

Law of Demeter, LOD

  • Sometimes called the least knowledge principle
  • A software entity should interact with other entities as little as possible
  • The original purpose of Demeter’s law is to reduce the coupling between classes
  • Try to implement the class definitioncohesionAnd to use lesspublicModifier, as much as possibleprivate,protected

For example, let’s look at the process of product sales related job relationships

/ / salesman
class Salesman {
  constructor(public name: string) {}
  sale() {
    console.log(this.name + 'On sale.... '); }}// Sales manager
class SaleManager {
  private salesmen: Array<Salesman> = [new Salesman('Joe'), new Salesman('bill')];
  sale() {
    this.salesmen.forEach(salesman= >salesman.sale()); }}/ / big boss
class CEO {
  private saleManager: SaleManager = new SaleManager();
  sale() {
    this.saleManager.sale(); }}let ceo = new CEO();
ceo.sale();
Copy the code

Principle of composite reuse

Type of relationship

  • There are three basic relationships between classes: association (aggregation and composition), generalization, and dependency
  • If a class is unidirectional dependent on another class, then they are unidirectional. If they are mutually dependent, they are mutually dependent, namely, bidirectional correlation
  • There are two special cases of association relationships: aggregation and composition
    • Aggregation, used to indicate the relationship or ownership between whole and parts, where objects representing parts may be owned by the whole but are not necessarily destroyed as the whole dies, such as classes and students
    • The synthesis, the synthesis or the combination is much stronger than the polymeric relationship, the life cycle of the parts and the whole is consistent between people and organs for example

Synthesis of reuse

  • The principle of composite reuse is implemented by incorporating existing objects into new objects as member objects of new objects
  • New objects can invoke the functions of existing objects, thus achieving reuse
  • The principle is to try to use a composition/aggregation approach first, rather than inheritance
  • Professional people do professional things

For example, we can’t cook, so we hire a professional chef to cook

class Cooker {
  cook() {}
}
class Person {
  private cooker: Cooker = new Cooker();
  cook() {
    this.cooker.cook(); }}Copy the code

conclusion

  • The open closed principle is the core, close to modification and open to extension is the cornerstone of software design
  • Single responsibility requires that we design interfaces and module functions to be as single and atomic as possible, so that changing one does not affect the global and other modules
  • The Richter substitution principle and the dependency inversion principle require programming to be interface oriented and abstract, and not to rely on concrete implementations, otherwise the upper caller will have to change accordingly

How to write good code?

There are a few principles to follow

  • Are maintainability bugs easy to fix?
  • Is it easy to read?
  • Is it extensible to add new features?
  • Flexibility Is it easy to add new features? Are old methods and interfaces easy to reuse?
  • Is the code simple and clear?
  • Don’t write the same code twice for reusability?
  • Is testability convenient for writing unit and integration tests?

Type and implementation of design pattern

Design patterns can be divided into three broad categories:

Structural Patterns

Simplify system design by identifying simple relationships among the components of the system.

Concrete implementation:

  • Important: Broker pattern, bridge pattern, decorator pattern, adapter pattern
  • Unimportant: Appearance mode, composition mode, enjoy element mode

Creational Patterns

Handle the creation of objects, using the appropriate way to create objects according to the actual situation. Conventional methods of object creation can cause design problems or add complexity to the design. The creation pattern solves the problem by controlling the creation of objects in some way.

Concrete implementation:

  • Important: Factory pattern (factory method pattern, Abstract factory pattern, simple Factory pattern), Builder pattern, singleton pattern
  • Unimportant: Prototype patterns

Behavioral Patterns

Used to identify common interaction patterns between objects and implement them, thus increasing the flexibility of these interactions.

Concrete implementation:

  • Important: Observer pattern (publish/subscribe), Template method pattern, policy pattern, Chain of Responsibility pattern, iterator pattern, state pattern
  • Unimportant: Visitor mode, memo mode, command mode, interpreter mode, mediator mode

I. Structural Patterns

1. Facade Pattern

The look and feel pattern is one of the most common design patterns that provides a unified, high-level interface for a set of interfaces in a subsystem, making the subsystem easier to use. In short, the design pattern abstracts complex logic from multiple subsystems to provide a more unified, concise, and easy-to-use API. Many commonly used frameworks and libraries follow the design pattern, such as JQuery, which abstracts and encapsulates complex native DOM operations and eliminates interoperability issues between browsers, thus providing a more advanced and easier to use version. In fact, in our daily work, we often use the appearance mode for development, but we do not know it.

For example, we can apply the appearance pattern to encapsulate a unified DOM element event binding/cancellation method that is compatible with different browser versions and easier to call:

// Bind the event
function addEvent(element, event, handler) {
  if (element.addEventListener) {
    element.addEventListener(event, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent('on' + event, handler);
  } else {
    element['on'+ event] = fn; }}// Cancel the binding
function removeEvent(element, event, handler) {
  if (element.removeEventListener) {
    element.removeEventListener(event, handler, false);
  } else if (element.detachEvent) {
    element.detachEvent('on' + event, handler);
  } else {
    element['on' + event] = null; }}Copy the code

2. Proxy Pattern

First, everything is agentable, both in the implementation world and in the computer world. In the real world, there are real estate agents, lawyers, and investment brokers, all of whom are agents who help you do things that you can’t do due to lack of time or expertise. In the computer world, proxies are used when an object itself is too expensive to access (e.g. too much memory, too long to initialize, etc.) or when additional logic needs to be added without modifying the object itself. Proxy functionality has also been added in ES6.

In summary, the proxy model can solve the following problems:

  1. Adds access control to an object

  2. Additional logic is required when accessing an object

There are three parts to implementing the proxy pattern:

  1. The Real Subject

  2. Proxy: Proxy object

  3. Subject interface: The interface that both the Real Subject and Proxy need to implement so that the Proxy can be used as a “surrogate” of the Real Subject

The class diagram

Proxy Proxy object, which contains a reference to the Target object, can be extended to the access and additional processing

The implementation code

abstract class Star {
  abstract answerPhone(): void;
}

// Target Indicates the Target object
class Angelababy extends Star {
  public available: boolean = true;
  answerPhone(): void {
    console.log('Hello, I'm Angelababy.'); }}// Proxy object
class AngelababyAgent extends Star {
  // Include a reference to the target object (Angelababy) to extend access and additional processing
  constructor(private angelababy: Angelababy) {
    super(a); } answerPhone():void {
    console.log('Hello, I'm Angelababy's manager.');
    if (this.angelababy.available) {
      this.angelababy.answerPhone(); }}}let angelababyAgent = new AngelababyAgent(new Angelababy());
angelababyAgent.answerPhone();
Copy the code

Usage scenarios

Event agent
  • Event capture refers to the top-down process of firing events from the document to the node that triggers the event
  • Event bubbling is bottom-up to trigger events
  • The third parameter to the binding event method is to control whether the event firing sequence is event capture. True: event capture; False is the event bubble. The default is false.

<body>
  <ul id="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
  <script>
    // list -> proxy object
    let list = document.querySelector('#list');
    list.addEventListener('click', event => {
      // event.target -> Target object
      alert(event.target.innerHTML);
    });
  </script>
</body>
Copy the code
The caching proxy

For example, there is a stock price query interface that takes a long time to call (using setTimeout to simulate the call time of 2s) :

StockPriceAPI:

function StockPriceAPI() {
  // Subject Interface implementation
  this.getValue = function(stock, callback) {
    console.log('Calling external API ... ');
    setTimeout((a)= > {
      switch (stock) {
        case 'GOOGL':
          callback('$1265.23');
          break;
        case 'AAPL':
          callback('$287.05');
          break;
        case 'MSFT':
          callback('$173.70');
          break;
        default:
          callback(' '); }},2000);
  };
}
Copy the code

We don’t want to request the remote interface every time, but we want to add a caching mechanism, when there is a cache directly from the cache, otherwise we request the remote interface. We can do this with a proxy:

StockPriceAPIProxy:

function StockPriceAPIProxy() {
  // Cache objects
  this.cache = {};
  // Real API objects
  this.realAPI = new StockPriceAPI();
  // Subject Interface implementation
  this.getValue = function(stock, callback) {
    // Cache proxy: retrieve the cache directly without re-requesting the remote interface
    const cachedPrice = this.cache[stock];
    if (cachedPrice) {
      console.log('Got price from cache');
      callback(cachedPrice);
    } else {
      this.realAPI.getValue(stock, price => {
        this.cache[stock] = price; callback(price); }); }}; }Copy the code

Note that the Proxy needs to implement the getValue() method just like the real object, and getValue() belongs to the Subject interface.

Test it out:

const api = new StockPriceAPIProxy();
api.getValue('GOOGL', price => {
  console.log(price);
});
api.getValue('AAPL', price => {
  console.log(price);
});
api.getValue('MSFT', price => {
  console.log(price);
});

setTimeout((a)= > {
  api.getValue('GOOGL', price => {
    console.log(price);
  });
  api.getValue('AAPL', price => {
    console.log(price);
  });
  api.getValue('MSFT', price => {
    console.log(price);
  });
}, 3000);
Copy the code

Output:

Calling external API ...
Calling external API ...
Calling external API ...
The $126523.The $287.05
The $173.70
Got price from cache
The $1265.23
Got price from cache
The $287.05
Got price from cache
The $17370.Copy the code

3. Decorator Pattern

  • A pattern that adds new functionality to an object without changing its original structure or functionality is called a decorator pattern
  • The most intuitive is the decoration after we buy a house
  • Decorators are more flexible than inheritance and allow for loose coupling between decorators and decorates
  • The decorator can be used by the decorator to dynamically add and undo functions

abstract class Shape {
  abstract draw(): void;
}
class Circle extends Shape {
  draw() {
    console.log('Draw circle'); }}class Rectangle extends Shape {
  draw() {
    console.log('Draw rectangle'); }}abstract class ColorfulShape extends Shape {
  public constructor(public shape: Shape) {
    super(a); }abstract draw(): void;
}

class RedColorfulShape extends ColorfulShape {
  draw() {
    this.shape.draw();
    console.log('Paint the border red'); }}class GreenColorfulShape extends ColorfulShape {
  draw() {
    this.shape.draw();
    console.log('Paint the border green'); }}let circle = new Circle();
let redColorfulShape = new RedColorfulShape(circle);
redColorfulShape.draw();

let rectangle = new Rectangle();
let greenColorfulShape = new GreenColorfulShape(rectangle);
greenColorfulShape.draw();
Copy the code

Application scenarios

A decorator
  • A decorator is a special type of declaration that can be attached to a class declaration, method, property, or parameter to modify the behavior of the class
  • Common decorators are class decorators, property decorators, method decorators, and parameter decorators
  • The writing method of decorator is divided into ordinary decorator and decorator factory
  1. Class decorator
  • Class decorators are declared before class declarations and are used to monitor, modify, or replace class definitions
  • Arguments are class definitions or constructors
  • babel-plugin-proposal-decorators

decorator

export {};
namespace decorator {
  interface Animal {
    swings: string;
    fly: any;
  }
  function flyable(target: any) {
    console.log(target);

    target.prototype.swings = 2;
    target.prototype.fly = function() {
      console.log('I can fly');
    };
  }
  @flyable
  class Animal {
    constructor() {}}let animal: Animal = new Animal();
  console.log(animal.swings);
  animal.fly();
}
Copy the code

decorator_factory

namespace decorator_factory {
  interface Animal {
    swings: string;
    fly: any;
  }
  function flyable(swings: number) {
    return function flyable(target: any) {
      console.log(target);

      target.prototype.swings = swings;
      target.prototype.fly = function() {
        console.log('I can fly');
      };
    };
  }

  @flyable(2)
  class Animal {
    constructor() {}}let animal: Animal = new Animal();
  console.log(animal.swings);
  animal.fly();
}
Copy the code
  1. Property decorator
  • The property decorator expression is called at run time as a function
  • Attributes are divided into instance attributes and class attributes
  • Methods are divided into instance methods and class methods
namespace property_namespace {
  // The instance attribute target is the prototype object of the class, and key is the attribute name
  function instancePropertyDecorator(target: any, key: string) {}
  // The class attribute target is the constructor
  function classPropertyDecorator(target: any, key: string) {}
  // The instance method decorator (target) is the prototype object, the key method name,descriptor is the method descriptor
  function instanceMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {}
  // The class method decorator target is the class constructor
  function classMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {}
  class Person {
    @instancePropertyDecorator
    instanceProperty: string;
    @classPropertyDecorator
    public static classProperty: string;
    @instanceMethodDecorator
    instanceMethod() {
      console.log('instanceMethod');
    }
    @classMethodDecorator
    classMethod() {
      console.log('classMethod'); }}}Copy the code
  1. core-decorator
  • core-decorator
  • deprecate-alias-deprecated
let { readonly } = require('core-decorators');
function deprecate(msg: string, options: any) {
  return function(target: any, attr: any, descriptor: any) {
    //DEPRECATION Calculator#add: This function will be removed in future versions.
    let oldVal = descriptor.value;
    descriptor.value = function(. args:any[]) {
      let message = msg
        ? msg
        : `DEPRECATION ${target.constructor.name}#${attr}: This function will be removed in future versions.`;
      let see = options && options.url ? `see ${options.url}` : ` `;
      console.warn(message + '\r\n' + see);
      returnoldVal(... args); }; }; }class Calculator {
  @deprecate('stop using this', { url: 'http://www.baidu.com' })
  add(a: number, b: number) {
    returna + b; }}let calculator = new Calculator();
calculator.add(1.2);
Copy the code
AOP concepts

In the software industry, AOP stands for Aspect Oriented Programming. It means a technique whereby a program can be dynamically and uniformly added to without modifying the source code through precompiled methods and dynamic proxies at runtime

Buried point
  • Burial point analysis: it is a common data acquisition method for website analysis
  • Non-trace burial point: complete the statistical uploading of user behavior data without difference through technical means, and select appropriate data for statistical analysis during the later data analysis and processing by technical means

Project configuration

  1. Create a project
create-react-app james_tract
yarn add customize-cra react-app-rewired --dev
Copy the code
  1. config-overrides.js
const { override, addDecoratorsLegacy } = require('customize-cra');
module.exports = override(addDecoratorsLegacy());
Copy the code
  1. jsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true}}Copy the code

index.js

import React from 'react';
import { render } from 'react-dom';
import { before, after } from './track';

class App extends React.Component {
  @before((a)= > console.log('Click method before execution'))
  onClickBeforeButton() {
    console.log('beforeClick');
  }

  @after((a)= > console.log('Click after method execution'))
  onClickAfterButton() {
    console.log('afterClick');
  }

  @after((a)= > fetch('/api/report'))
  onClickAjaxButton() {
    console.log('ajaxClick');
  }

  render() {
    return (
      <div>
        <button onClick={this.onClickBeforeButton}>beforeClick</button>
        <button onClick={this.onClickAfterButton}>afterClick</button>
        <button onClick={this.onClickAjaxButton}>ajaxClick</button>
      </div>
    );
  }
}
render(<App />, document.getElementById('root'));
Copy the code

track.js

export const before = function(beforeFn) {
  return function(target, methodName, descriptor) {
    let oldMethod = descriptor.value;
    descriptor.value = function() {
      beforeFn.apply(this.arguments);
      return oldMethod.apply(this.arguments);
    };
  };
};

export const after = function(afterFn) {
  return function(target, methodName, descriptor) {
    let oldMethod = descriptor.value;
    descriptor.value = function() {
      oldMethod.apply(this.arguments);
      afterFn.apply(this.arguments);
    };
  };
};
Copy the code

Form validation

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, "> <meta http-equiv=" x-uA-compatible "content="ie=edge"> <title> User registration </title> </head> <body> <form Action =""> Username <input type="text" name="username" id="username"> password <input type="text" name="password" id="password"> < button id = "submit - BTN" > register < / button > < / form > < script >. The Function prototype. Before = Function (beforeFn) {let _this = this;  return function () { let ret = beforeFn.apply(this, arguments); if (ret) _this.apply(this, arguments); }} function submit() {alert(' submit form '); } submit = submit.before(function () { let username = document.getElementById('username').value; If (username. Length < 6) {return alert(' username must not be less than 6 digits '); } return true; }); submit = submit.before(function () { let username = document.getElementById('username').value; if (! Username) {return alert(' username cannot be empty '); } return true; }); document.getElementById('submit-btn').addEventListener('click', submit); </script> </body> </html>Copy the code

,

4. Adapter Pattern

  • The adapter pattern, also known as the wrapper pattern, converts the interface of a class into another interface that the user needs to solve the problem of incompatibility between the interfaces of classes (objects)
  • The old interface is not compatible with the user
  • An adapter conversion interface is added in the middle

The implementation code

class Socket {
  output() {
    return 'output 220 v.; }}abstract class Power {
  abstract charge(): string;
}
class PowerAdapter extends Power {
  constructor(public socket: Socket) {
    super(a); }// The converted interface is not the same as before
  charge() {
    return this.socket.output() + '24V output after conversion'; }}let powerAdapter = new PowerAdapter(new Socket());
console.log(powerAdapter.charge());
Copy the code

Usage scenarios

  1. axios
  • Axios
  • dispatchRequest
  • defaults
  • xhr
  • http

Each of these adapters takes a Config as an input and returns a promise

//let axios = require('axios');
let url = require('url');
function axios(config: any) :any {
  let adaptor = getDefaultAdapter();
  return adaptor(config);
}
axios({
  method: 'GET'.url: 'http://localhost:8080/api/user? id=1'
}).then(
  function(response: any) {
    console.log(response);
  },
  function(error: any) {
    console.log(error); });function xhr(config: any) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open(config.method, config.url, true);
    request.onreadystatechange = function() {
      if (request.readyState == 4) {
        if (request.status == 200) {
          resolve(request.response);
        } else {
          reject('Request failed'); }}}; }); }function http(config: any) {
  let http = require('http');
  let urlObject = url.parse(config.url);
  return new Promise(function(resolve, reject) {
    const options = {
      hostname: urlObject.hostname,
      port: urlObject.port,
      path: urlObject.pathname,
      method: config.method
    };
    var req = http.request(options, function(res: any) {
      let chunks: any[] = [];
      res.on('data', (chunk: any) => {
        chunks.push(chunk);
      });
      res.on('end', () => {
        resolve(Buffer.concat(chunks).toString());
      });
    });
    req.on('error', (err: any) => {
      reject(err);
    });
    req.end();
  });
}
function getDefaultAdapter() :any {
  var adapter;
  if (typeofXMLHttpRequest ! = ='undefined') {
    adapter = xhr;
  } else if (typeofprocess ! = ='undefined') {
    adapter = http;
  }
  return adapter;
}
Copy the code

server.js

let express = require('express');
let app = express();
app.get('/api/user', (req, res) => {
  res.json({ id: req.query.id, name: 'golderbrother' });
});
app.listen(8080);
Copy the code
  1. toAxiosAdapter
function toAxiosAdapter(options: any) {
  return axios({
    url: options.url,
    method: options.type
  })
    .then(options.success)
    .catch(options.error);
}

$.ajax = function(options: any) {
  return toAxiosAdapter(options);
};

$.ajax({
  url: '/api/user'.type: 'GET'.success: function(data: any) {
    console.log(data);
  },
  error: function(err: any) {
    console.error(err); }});Copy the code
  1. promisify
let fs = require('fs');
let Bluebird = require('bluebird');
let readFile = Bluebird.promisify(fs.readFile);

(async function() {
  let content = await readFile('./1.txt'.'utf8');
  console.log(content); }) ();Copy the code
function promisify(readFile: any) {
  return function(filename: any, encoding: any) {
    return new Promise(function(resolve, reject) {
      readFile(filename, encoding, function(err: any, data: any) {
        if (err) reject(err);
        else resolve(data);
      });
    });
  };
}
Copy the code
  1. The Vuecomputed

      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <script src="https://cdn.bootcss.com/vue/2.5.17/vue.js"></script>
    <title>vue</title>
  </head>
  <body>
    <div id="root">
      <p>{{name}}</p>
      <p>{{upperName}}</p>
    </div>
    <script>
      let vm = new Vue({
        el: '#root'.data: {
          name: 'golderbrother'
        },
        computed: {
          upperName() {
            return this.name.toUpperCase(); }}});</script>
  </body>
</html>
Copy the code
  1. tree

Tree data is displayed in the drop – down box


      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <select id="users"></select>
    <script>
      let tree = [
        {
          name: 'father'.key: '1'.children: [{name: 'son'.key: 1-1 ' '.children: [{name: 'son'.key: '1-1-1'}]}]}];function flattenAdapter(tree, flattenArray) {
        tree.forEach(item= > {
          if (item.children) {
            flattenAdapter(item.children, flattenArray);
          }
          flattenArray.push({ name: item.name, key: item.key });
        });
        return flattenArray;
      }
      let array = [];
      flattenAdapter(tree, array);
      array.reverse();
      let users = document.getElementById('users');
      let options = array.map(item= > `<option value="${item.key}">${item.name}</option>`).join(' ');
      users.innerHTML = options;
    </script>
  </body>
</html>
Copy the code
  1. Sequelize

sequelize

//cnpm i sequelize sqlite3 -S
const { Sequelize, Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model {}
User.init(
  {
    username: DataTypes.STRING
  },
  { sequelize, modelName: 'user'}); sequelize .sync() .then((a)= >
    User.create({
      username: 'golderbrother'
    })
  )
  .then(result= > {
    console.log(result.toJSON());
  });
Copy the code

2. Creational Patterns

1. Factory Pattern

In real life factories, products are made according to a set process, and the products are produced differently depending on the materials and processes used. Applied to software engineering, a factory can be thought of as an object that makes other objects, and the resulting objects differ depending on the parameters passed to the factory object.

What would be a good scenario to apply the factory pattern rather than simply new an object? The factory pattern is useful when there are too many constructors to manage, and objects that need to be created have some kind of association (having the same parent class, implementing the same interface, etc.). The factory pattern provides a centralized, unified way to avoid the problems of code duplication and poor flexibility caused by decentralized object creation.

Simple factory mode

The simple factory pattern is where a factory object determines which instances of a product class are created

In the example above, we construct a simple coffee factory to produce coffee:

abstract class Coffee {
  constructor(public name: string) {}}class AmericanoCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class LatteCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class CappuccinoCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class Cafe {
  static order(name: string) {
    switch (name) {
      case 'Americano':
        return new AmericanoCoffee(Americano);
      case 'Latte':
        return new LatteCoffee('Latte');
      case 'Cappuccino':
        return new LatteCoffee('Cappuccino');
      default:
        return null; }}}console.log(Cafe.order('Americano'));
console.log(Cafe.order('Latte'));
console.log(Cafe.order('Cappuccino'));
Copy the code

disadvantages

  • If the variety of products is very large, the judgment of switch case will become very large
  • This does not conform to the open-close principle. If you want to add or remove a product category, you need to modify the switch Case judgment code

Factory method pattern

  • Factory method patternFactory Method, also known as polymorphic factory pattern.
  • In the factory method pattern, the core factory class is no longer responsible for all product creation, but the factory subclass does the specific creation.

The implementation code

export {};
abstract class Coffee {
  constructor(public name: string) {}}abstract class Factory {
  abstract createCoffee(): Coffee;
}
class AmericanoCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class AmericanoCoffeeFactory extends Factory {
  createCoffee() {
    return new AmericanoCoffee(Americano); }}class LatteCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class LatteCoffeeFactory extends Factory {
  createCoffee() {
    return new LatteCoffee('Latte'); }}class CappuccinoCoffee extends Coffee {
  constructor(public name: string) {
    super(name); }}class CappuccinoFactory extends Factory {
  createCoffee() {
    return new CappuccinoCoffee('Cappuccino'); }}class Cafe {
  static order(name: string) {
    switch (name) {
      case 'Americano':
        return new AmericanoCoffeeFactory().createCoffee();
      case 'Latte':
        return new LatteCoffeeFactory().createCoffee();
      case 'Cappuccino':
        return new CappuccinoFactory().createCoffee();
      default:
        return null; }}}console.log(Cafe.order('Americano'));
console.log(Cafe.order('Latte'));
console.log(Cafe.order('Cappuccino'));
Copy the code

Abstract Factory Pattern (ultimate solution)

  • The abstract factory pattern provides an interface to the client that allows it to create product objects in multiple product families without specifying the specifics of the product
  • The factory method pattern is for the same class or level of products, while the abstract factory pattern is for multiple product designs
  • There are multiple product families in the system, and each specific factory is responsible for creating products of the same family but of different product grades (product categories)
  • A product family is a group of related or interdependent objects
  • The system can only consume one family of products at a time, that is, products of the same product family are used together
  • When the system needs to add a new product family, only need to add a new factory class, without modifying the source code; However, if a new class of products is added to the product family, all factory classes need to be modified

Role of

  • Abstract factory: Provides an interface to create a product, including multiple methods to create a product, that is, contains multiple methods similar to creating a product
  • Concrete factory: Implements the interface defined by the abstract factory to complete the creation of a concrete product
  • Abstract products: Abstract product definitions, typically as many abstract products as there are methods to create them in an abstract factory
  • Concrete product: An implementation class for an abstract product

The implementation code

export {};
abstract class AmericanoCoffee {}
abstract class LatteCoffee {}
abstract class CappuccinoCoffee {}

class StarbucksAmericanoCoffee extends AmericanoCoffee {}
class StarbucksLatteCoffee extends LatteCoffee {}
class StarbucksCappuccinoCoffee extends CappuccinoCoffee {}

class LuckinAmericanoCoffee extends AmericanoCoffee {}
class LuckinLatteCoffee extends LatteCoffee {}
class LuckinCappuccinoCoffee extends CappuccinoCoffee {}

abstract class CafeFactory {
  abstract createAmericanoCoffee(): AmericanoCoffee;
  abstract createLatteCoffee(): LatteCoffee;
  abstract createCappuccinoCoffee(): CappuccinoCoffee;
}
class StarbucksCafeFactory extends CafeFactory {
  createAmericanoCoffee() {
    return new StarbucksAmericanoCoffee();
  }
  createLatteCoffee() {
    return new StarbucksLatteCoffee();
  }
  createCappuccinoCoffee() {
    return newStarbucksCappuccinoCoffee(); }}class LuckinCafeFactory extends CafeFactory {
  createAmericanoCoffee() {
    return new LuckinAmericanoCoffee();
  }
  createLatteCoffee() {
    return new LuckinLatteCoffee();
  }
  createCappuccinoCoffee() {
    return newLuckinCappuccinoCoffee(); }}let starbucksCafeFactory = new StarbucksCafeFactory();
console.log(starbucksCafeFactory.createAmericanoCoffee());
console.log(starbucksCafeFactory.createCappuccinoCoffee());
console.log(starbucksCafeFactory.createLatteCoffee());

let luckinCafeFactory = new LuckinCafeFactory();
console.log(luckinCafeFactory.createAmericanoCoffee());
console.log(luckinCafeFactory.createCappuccinoCoffee());
console.log(luckinCafeFactory.createLatteCoffee());
Copy the code

2. Singleton Pattern

As the name implies, the number of instances of a Class in a singleton pattern is at most 1. The singleton pattern comes in handy when an object is needed to perform some task across the system. In other scenarios, try to avoid using singletons because singletons introduce global state, and a healthy system should avoid introducing too much global state.

Implementing the singleton pattern requires addressing the following issues:

  1. How do I determine if there is only one instance of Class?

  2. How to easily access a unique instance of a Class?

  3. How does Class control the instantiation process?

  4. How do I limit the number of instances of a Class to one?

We usually solve these problems by implementing the following two things:

  1. Hide the Class constructor to avoid multiple instantiations

  2. Create/get a unique instance by exposing a getInstance() method

The singleton pattern in Javascript can be implemented in the following ways:

// Singleton constructor
const FooServiceSingleton = (function() {
  // The hidden Class constructor
  function FooService() {}

  // Uninitialized singleton object
  let fooService;

  return {
    // Functions to create/get singletons
    getInstance: function() {
      if(! fooService) { fooService =new FooService();
      }
      return fooService;
    }
  };
})();
Copy the code

The key points are:

  1. Use IIFE to create local scopes and execute them instantly.
  2. GetInstance () is a closure that is used to hold a singleton in the local scope and return it.

We can verify that the singleton object was created successfully:

const fooService1 = FooServiceSingleton.getInstance();
const fooService2 = FooServiceSingleton.getInstance();

console.log(fooService1 === fooService2); // true
Copy the code

3. Behavioral Patterns

1. Strategy Pattern

The simple description of the policy pattern is that the object has a certain behavior, but in different scenarios, the behavior has different implementation algorithms. For example, everyone has to “pay personal income tax”, but “pay personal income tax in America” and “pay personal income tax in China” have different methods. In the most common login authentication scenarios, the authentication algorithm depends on the login mode of the user, such as mobile phone, email, or Third-Party wechat login, and the login mode can be obtained only during the running time. After the login mode is obtained, the authentication policy can be dynamically configured. All of these policies should implement a uniform interface, or have a uniform pattern of behavior. The well-known authentication library Passport. Js API in the Node ecosystem is designed to apply the policy pattern.

Using the login authentication example again, let’s use code to understand the policy pattern in the same way as passport. Js:

/** * Log in to the controller */
function LoginController() {
  this.strategy = undefined;
  this.setStrategy = function(strategy) {
    this.strategy = strategy;
    this.login = this.strategy.login;
  };
}

/** * User name and password login policy */
function LocalStragegy() {
  this.login = ({ username, password }) = > {
    console.log(username, password);
    // authenticating with username and password...
  };
}

/** * Mobile phone number and verification code Login policy */
function PhoneStragety() {
  this.login = ({ phone, verifyCode }) = > {
    console.log(phone, verifyCode);
    // authenticating with phone and verifyCode...
  };
}

/** * Third party social login policies */
function SocialStragety() {
  this.login = ({ id, secret }) = > {
    console.log(id, secret);
    // authenticating with id and secret...
  };
}

const loginController = new LoginController();

// Call the user name and password login interface, using LocalStrategy
app.use('/login/local'.function(req, res) {
  loginController.setStrategy(new LocalStragegy());
  loginController.login(req.body);
});

// Call the phone/captcha login interface, using PhoneStrategy
app.use('/login/phone'.function(req, res) {
  loginController.setStrategy(new PhoneStragety());
  loginController.login(req.body);
});

// Call the social login interface, using SocialStrategy
app.use('/login/social'.function(req, res) {
  loginController.setStrategy(new SocialStragety());
  loginController.login(req.body);
});
Copy the code

From the above examples, you can see that using the policy pattern has the following advantages:

  1. Easy to switch algorithms and policies at run time
  2. The code is more concise and avoids using a lot of conditional judgment
  3. Separation of concerns, each Strategy class controls its own algorithm logic, strategy and its users are also independent of each other

2. Iterator Pattern

The Iterator Pattern is used to access elements within an aggregate object sequentially without knowing the internal structure of the object. With iterators, consumers can access each element of an object in order without having to worry about its internal construction.

Usage scenarios

  1. Iterator

Iterators are used to iterate over a container (collection) and access its elements. The Iterator interface should be the same regardless of the data structure of the container (Array, Set, Map, etc.).

The iterator pattern solves the following problems:

  1. Provides a consistent way to traverse various data structures without knowing the internal structure of the data

  2. Provides the ability to traverse a container (collection) without changing the container’s interface

An iterator usually needs to implement the following interface:

  • HasNext () : Determines if the iteration is over. Boolean is returned

  • Next () : Finds and returns the next element

To implement an iterator for an array in Javascript, write:

const item = [1.'red'.false.3.14];

function Iterator(items) {
  this.items = items;
  this.index = 0;
}

Iterator.prototype = {
  hasNext: function() {
    return this.index < this.items.length;
  },
  next: function() {
    return this.items[this.index++]; }};Copy the code

Verify that the iterator works:

const iterator = new Iterator(item);

while (iterator.hasNext()) {
  console.log(iterator.next());
}
Copy the code

Output:

1, red, false, 3.14
Copy the code

ES6 provides simpler iterative loop syntax for… “Of” is used only if The object is implemented with The iterable Protocol. Simply put, The object has a method with a Key of Symbol.

Let’s say we implement a Range class for iterating over a Range of numbers:

function Range(start, end) {
  return {
    // Deploy the iterator method
    [Symbol.iterator]: function() {
      return {
        next() {
          if (start < end) {
            return { value: start++, done: false };
          }
          return { done: true.value: end }; }}; }}; }Copy the code

Verify:

for (num of Range(1.5)) {
  console.log(num);
}
Copy the code

Output:

1, 2, 3, 4
Copy the code
  1. forEach

You can implement a forEach yourself using a for loop

Array.prototype.forEach = function(cb) {
  for (var i = 0; i < this.length; i++) {
    cb.call(this.this[i], i, arr); }};let arr = [1.2.3];
arr.forEach(item= > {
  console.log(item);
});
Copy the code

each

  • jquery
  • underscore
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
  $.each(['1'.'2'.'3'].function(index, item) {
    console.log(index, item);
  })
  $.each({
    name: 'golderbrother'.age: 10
  }, function(index, item) {
    console.log(index, item);
  })

  function each(obj, callback) {
    if (Array.isArray(obj)) {
      let length = obj.length;
      for (let i = 0; i < length; i++) {
        if (callback.call(obj[i], i, obj[i]) === false) {
          break; }}}else {
      for (let i in obj) {
        if (callback.call(obj[i], i, obj[i]) === false) {
          break; }}}return obj;
  }
</script>

</body>
Copy the code
  1. yield*

Yield * is followed by a traversable structure that calls its traverser interface

let generator = function* () {
  yield 1;
  yield* [2.3];
  yield 4;
};

var iterator = generator();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Copy the code
  1. Binary tree traversal
  • A binary tree is a tree structure in which each node has at most two subtrees. Usually subtrees are called left subtrees and right subtrees
  • According to the order of the root nodes, the traversal can be divided into three kinds of preorder, middle order and postorder
    • First order traversal: root -> left subtree -> right subtree
    • In order traversal: left subtree -> root node -> right subtree
    • Post-order traversal: left subtree -> right subtree -> root node

class Tree {
  constructor(public left, public value, public right) {}}//['A', 'B', 'D','E', 'C', 'F','G']
function* leftOrder(tree) {
  if (tree) {
    yield tree.value;
    yield* leftOrder(tree.left);
    yield* leftOrder(tree.right); }}//['D', 'B', 'E','A', 'F', 'C','G']
function* inOrder(tree) {
  if (tree) {
    yield* inOrder(tree.left);
    yield tree.value;
    yield* inOrder(tree.right); }}//['D', 'E', 'B','F', 'G', 'C','A']
function* rightOrder(tree) {
  if (tree) {
    yield* rightOrder(tree.left);
    yield* rightOrder(tree.right);
    yieldtree.value; }}function make(array) {
  if (array.length === 1) return new Tree(null, array[0].null);
  return new Tree(make(array[0]), array[1], make(array[2]));
}
let tree = make([[['D'].'B'['E']], 'A'The [['F'].'C'['G']]]);
var result: any[] = [];
for (let node of rightOrder(tree)) {
  result.push(node);
}
console.log(result);
Copy the code

3. Observer Pattern

The observer Pattern, also known as the Publish/Subscribe Pattern, is a design Pattern that we are often exposed to. It is also widely used in daily life. For example, if you Subscribe to a blogger’s channel, you will receive push updates when there is content. Another example is the event subscription response mechanism in JavaScript. The idea of the observer pattern is described in one sentence: the subject maintains a set of observers internally, and when the state of the observed object changes, the observer is notified of the changes by calling one of the observer’s methods.

What’s the difference between it and the publish-subscribe model?

  • Observer mode: the observed (SubjectThe observer is stored in theObserver, the two have a relationship
  • Publish-subscribe pattern: Publishers (publish) and subscribers (subscribe) is through the middleDispatch centerTo communicate. It has nothing to do with it

For example, the addEventListener() method binds an event to a DOM element:

target.addEventListener(type, listener [, options]);
Copy the code

Implement observer mode manually in Typescript:

abstract class Student {
  constructor(public teacher: Teacher) {}
  public abstract update();
}
class Xueba extends Student {
  public update() {
    console.log(this.teacher.getState() + The top students raised their heads and raised their hands.); }}class Xuezha extends Student {
  public update() {
    console.log(this.teacher.getState() + 'Poor students bow their heads in prayer.'); }}class Teacher {
  private students: Student[] = new Array<Student>();
  public state: string = 'The teacher speaks';
  getState() {
    return this.state;
  }
  public askQuestion() {
    this.state = 'Teacher asks';
    this.notifyAllStudents();
  }
  attach(student: Student) {
    this.students.push(student);
  }
  notifyAllStudents() {
    this.students.forEach(student= >student.update()); }}abstract class GlobalObserver {
  constructor(public subject: Subject) {}
  public abstract update();
}

class ObserverA extends GlobalObserver {
  public update() {
    console.log(this.subject.getState() + 'Can't afford to drink'); }}class ObserverB extends GlobalObserver {
  public update() {
    console.log(this.subject.getState() + 'Let's have a drink.'); }}class Subject {
  private observers: GlobalObserver[] = new Array<GlobalObserver>();
  public state: string = 'Ding ~ new product of Fawn tea tomorrow! ';
  // Get the latest status message
  getState() {
    return this.state;
  }
  / / subscribe
  subscribe(observer: GlobalObserver): void {
    this.observers.push(observer);
  }
  // Unsubscribe
  unsubscribe(observerToRemove: GlobalObserver): void {
    this.observers = this.observers.filter((observer: GlobalObserver) = > {
      returnobserver ! == observerToRemove; }); }// The event is triggered
  notify(): void {
    this.observers.forEach((observer: GlobalObserver) = >{ observer.update && observer.update(); }); }}Copy the code

Verify that the subscription was successful

const subject = new Subject();
// Ding ~ xialu tea will have a new product tomorrow! And can't afford to drink
// Ding ~ xialu tea will have a new product tomorrow! Come and have a drink
subject.subscribe(new ObserverA(subject));
subject.subscribe(new ObserverB(subject));
subject.notify();
Copy the code

Usage scenarios

  1. DOM Event Binding

      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Observer mode</title>
  </head>

  <body>
    <button id="btn">click</button>
    <script>
      let btn = document.getElementById('btn');
      const handler1 = (a)= > {
        console.log(1);
      };
      const handler2 = (a)= > {
        console.log(2);
      };
      const handler3 = (a)= > {
        console.log(3);
      };
      btn.addEventListener('click', handler1);
      btn.addEventListener('click', handler2);
      btn.addEventListener('click', handler3);
    </script>
  </body>
</html>
Copy the code
  1. Promise
class Promise {
  private callbacks: Array<Function> = [];
  constructor(fn) {
    let resolve = (a)= > {
      this.callbacks.forEach(callback= > callback());
    };
    fn(resolve);
  }
  then(callback) {
    this.callbacks.push(callback); }}let promise = new Promise(function(resolve) {
  setTimeout(function() {
    resolve(100);
  }, 1000);
});
promise.then((a)= > console.log(1));
promise.then((a)= > console.log(2));
Copy the code
  1. callbacks
function Callbacks() {
  let observers = [];
  function add(observer) {
    observers.push(observer);
  }
  function remove(observer) {
    let index = observers.indexOf(observer);
    if(index ! =- 1) observers.splice(index, 1);
  }
  function fire() {
    observers.forEach(observer= > observer());
  }
  return {
    add,
    remove,
    fire
  };
}
Copy the code
  1. EventEmitter

Custom event

const EventEmitter = require('events');
let subject = new EventEmitter();
subject.on('click'.function(name) {
  console.log(1, name);
});
subject.on('click'.function(name) {
  console.log(2, name);
});
subject.emit('click'.'golderbrother');
Copy the code

events.js

class EventEmitter {
  constructor() {
    this._events = {};
  }
  on(type, listener) {
    let listeners = this._events[type];
    if (listeners) {
      listeners.push(listener);
    } else {
      this._events[type] = [listener];
    }
  }
  emit(type) {
    let listeners = this._events[type];
    let args = Array.from(arguments).slice(1);
    listeners.forEach(listener= > listener(...args));
  }
}
module.exports = EventEmitter;
Copy the code
  1. flow
let fs = require('fs');
let rs = fs.createReadStream('./1.txt', { highWaterMark: 3 });
rs.on('data'.function(data) {
  console.log(data.toString());
});
rs.on('end'.function() {
  console.log('end');
});
Copy the code
  1. httpThe server
let http = require('http');
let server = http.createServer();
server.on('request', (req, res) => {
  res.end('golderbrother');
});
server.listen(3000);
Copy the code
  1. Life cycle function

react

vue

  1. EventBus
  • If you feel like usingEmit is inconvenient and you don’t want to introduce VUEX, you can use EventBus instead
  • Sibling components that communicate with each other introduce a new instance of VUE, and then communicate and pass parameters by calling the event trigger and listener of that instance, respectively

EventBus.js

import Vue from 'vue';
export default new Vue();
Copy the code

Component A

import EventBus from './EventBus';
EventBus.$on('customEvent', name => {
  console.log(name);
});
Copy the code

The component B

import EventBus from './EventBus';
EventBus.$emit('customEvent'.'golderbrother');
Copy the code
<body>
  <script src="https://cdn.bootcss.com/vue/2.5.17/vue.js"></script>
  <script>
    let EventBus = new Vue();
    EventBus.$on('customEvent', name => {
      console.log(name);
    });
    EventBus.$emit('customEvent'.'golderbrother');
  </script>
</body>
Copy the code
  1. Vue response principle

In Vue, each component instance has a corresponding Watcher instance object, which records the properties as dependencies during component rendering, and then when the setters for the dependencies are called, informs watcher to recalculate so that its associated component can be updated


      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>vue</title>
  </head>

  <body>
    <div id="name"></div>
    <div id="age"></div>
    <script>
      let name = document.getElementById('name');
      let age = document.getElementById('age');
      class Dep {
        subs = [];
        addSub(sub) {
          this.subs.push(sub);
        }
        notify() {
          this.subs.forEach(sub= >sub()); }}function observe(target) {
        Object.keys(target).forEach(key= > {
          let val = target[key];
          const dep = new Dep();
          if (key == 'name') {
            name.innerHTML = val;
            dep.addSub((a)= > {
              name.innerHTML = val;
            });
          } else if (key == 'age') {
            age.innerHTML = val;
            dep.addSub((a)= > {
              age.innerHTML = val;
            });
          }
          Object.defineProperty(target, key, {
            get: function() {
              return val;
            },
            set: function(value) { val = value; dep.notify(); }}); }); }let obj = { name: 'name'.age: 'age' };
      observe(obj);
      setTimeout((a)= > {
        obj.name = 'New name';
      }, 3000);
      setTimeout((a)= > {
        obj.age = 'New age';
      }, 6000);
    </script>
  </body>
</html>
Copy the code
  1. redux
  • createStore
export default function createStore(reducer, preloadedState, enhancer) {
  if (enhancer) {
    return enhancer(createStore)(reducer, preloadedState);
  }
  let state = preloadedState;
  let listeners = [];
  function getState() {
    return state;
  }
  function subscribe(listener) {
    listeners.push(listener);
    return function() {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }
  function dispatch(action) {
    state = reducer(state, action);
    listeners.forEach(listener= > listener());
  }
  dispatch({ type: '@@redux/INIT' });
  return {
    dispatch,
    subscribe,
    getState
  };
}
Copy the code

4. Publish/Subscribe Patterns

The difference between

  • Subscribers register the events they want to subscribe to with the dispatch center
  • When the event is triggered, the publisher publishes the event to the scheduling center, which uniformly schedules the processing code that subscribers register with the scheduling center.
  • Although both models have subscribers and publishers (The observer may be regarded as the subscriber and the observed may be regarded as the publisher)
  • But the observer pattern is scheduled by the observer, while the publish/subscribe pattern is uniformly managed by the scheduling center
  • So in the observer pattern there is a dependency between the subscriber and the publisher, whereas in the publish/subscribe pattern there is not.

The class diagram

The implementation code

class Agency {
  _topics = {};
  subscribe(topic, listener) {
    let listeners = this._topics[topic];
    if (listeners) {
      listeners.push(listener);
    } else {
      this._topics[topic] = [listener]; } } publish(topic, ... args) {let listeners = this._topics[topic] || [];
    listeners.forEach(listener= > listener(...args));
  }
}
class Landlord {
  constructor(public agent: Agency) {}
  lend(topic, area, money) {
    this.agent.publish(topic, area, money); }}class Tenant {
  constructor(public agent: Agency, public name: string) {}
  order(topic) {
    this.agent.subscribe(topic, (area, money) = > {
      console.log(this.name, `${area}Square meters,${money}Yuan `); }); }}let agent = new Agency();
let rich = new Tenant(agent, 'big money');
let poor = new Tenant(agent, 'north drift');
let landlord = new Landlord(agent);
rich.order('home');
poor.order('single');
// 1 ring mansions per square meter 1 million
landlord.lend('home'.1.1000000);
// 5 ring single room 1000 per month
landlord.lend('single'.5.1000);
Copy the code

Usage scenarios

  1. RedisRelease subscription

Redis publish subscription is a messaging mode: the sender sends the message, the subscriber receives the message, and the client can subscribe to any number of channels.

SUBSCRIBE channel_a
PUBLISH channel_a golderbrother
Copy the code
let redis = require('redis');
let client1 = redis.createClient(6379.'127.0.0.1');
let client2 = redis.createClient(6379.'127.0.0.1');

client1.subscribe('channel_a');
client1.subscribe('channel_b');
client1.on('message', (channel, message) => {
  console.log('client1', channel, message);
  client1.unsubscribe('channel_a');
});
client2.publish('channel_a'.'a_hello');
client2.publish('channel_b'.'b_hello');

setTimeout((a)= > {
  client2.publish('channel_a'.'a_world');
  client2.publish('channel_b'.'b_world');
}, 3000);
Copy the code

5. Mediator Pattern

In the Mediator model, a Mediator wraps the way a series of objects interact so that they don’t have to interact directly, but instead have the Mediator mediate their interactions so that they can be loosely coupled. When the effects of some objects change, it does not immediately affect the effects of other objects, ensuring that these effects can change independently of each other.

The mediator pattern is similar to the Observer pattern in that both are one-to-many relationships and centralized communication. The difference is that the mediator pattern deals with the interaction between peer objects, while the Observer pattern deals with the interaction between the Observer and the Subject (at different levels). The intermediary model is a bit like the dating agency. The blind date cannot be directly communicated at the beginning, but is screened through the intermediary to decide who will meet whom. Intermediary mode is more common applications such as chat rooms, chat rooms between people can not directly talk to each other, but through the chat room this medium to forward. A simple chat room model can be implemented as follows:

Chat room members:


class Member {
  name: string;
  chatroom: Chatroom;
  constructor(name: string) {
    this.name = name;
    this.chatroom = null;
  }
  // Send the message
  send(message: string, toMember: Member) {
    this.chatroom.send(message, this, toMember);
  }
  // Receive the message
  receive(message: string, fromMember: Member) {
    console.log(`${fromMember.name} to ${this.name}: ${message}`); }}Copy the code

Chat room:


class Chatroom {
  public members: any;
  constructor() {
    this.members = {};
  }
  // Add members
  addMember(member: Member): void {
    this.members[member.name] = member;
    member.chatroom = this;
  }
  // Send the message
  send(message: string, fromMember: Member, toMember: Member): void{ toMember.receive(message, fromMember); }}Copy the code

Test it out:

const chatroom: Chatroom = new Chatroom();
const bruce: Member = new Member('bruce');
const frank: Member = new Member('frank');

chatroom.addMember(bruce);
chatroom.addMember(frank);

bruce.send('Hey frank', frank);
Copy the code

Output:

bruce to frank: Hey frank
Copy the code

This is only a simple chat room model, the real chat room can also add more functions, such as sensitive information interception, one-to-many chat, broadcast and so on. Thanks to the mediator pattern, the Member doesn’t have to deal with the complex chat-related logic, but instead leaves it all to the Chatroom, effectively separating concerns.

The last

If there are any inaccuracies or errors in this article, please feel free to point them out and keep an eye on Github