Design principles
Solid (stable)
-
Single Responsibility Principle
A class should have only one reason to change. In short, each class only needs to be responsible for its own part, and the complexity of the class will be reduced.
-
Open Closed Principle
- A software entity, such as classes, modules, and functions, should be open to extensions and closed to modifications
-
Liskov Substitution Principle
All places that reference the base class must be able to use its subclass objects transparently, that is, subclass objects can replace their parent objects, and the program execution effect remains unchanged.
-
Law of Demeter
The Law of Demeter is also called The Least Knowledge Principle. The less a class knows about other classes, the better. That is to say, an object should know as little about other objects as possible, only communicate with friends and don't talk to strangers.
-
Interface aggregation principle flow, ts
- Multiple specific client interfaces are better than a general interface
-
Dependency Inversion Principle
1. The upper modules should not rely on the lower modules, they should all rely on abstraction.
2. Abstraction should not depend on details, details should depend on abstraction
Design pattern
Design pattern is a solution to some representative problems faced by software developers in the process of software development. These solutions are summarized by many software developers after a long period of experiments and errors;
- Singleton Pattern, also known as monomer pattern, ensures that a class has only one instance and provides a global access point to access it. That is, when you create a new object using the same class for the second time, you should get the same object as the object created for the first time.
-
Create a singleton through static attributes
class Person{ static instance = null; constructor(name){ if(Person.instance){ return Person.instance; } Person.instance = this; this.name = name; } }
-
Creating a singleton through a function
function getSingle (fn) { let instance = null return function (...args) { if (!instance) { instance = new fn(...args) } return instance } }
-
- Create a singleton through static attributes
- Excellent: the singleton mode saves memory expenses and performance expenses during instantiation, saving performance;
- Lack of: the singleton mode is not scalable
- Creating a singleton through a function
- Excellent: the singleton mode has strong scalability
- Lack: if you create a large number of singletons, you will lose memory expenses (because the closures used are not conducive to memory release)
Factory mode
- Factory Pattern encapsulates the creation logic and process of specific instances. The external only needs to return different instances according to different conditions. (separate creation and Implementation)
- Advantages: code reusability, good encapsulation and abstract logic;
- Disadvantages: increased code complexity;
Decorator mode
- Decorator Pattern uses a more flexible way to dynamically add additional information to an object / function, etc
// Decorator mode function Hurt () { console.log('I play a decorative role') } class Person { constructor () { this.name = 'Richard' } getInfo () { console.log('I am a method') } } Function.prototype.Decorator = function (fn) { let _this = this return function () { _this() fn() } } let person = new Person() person.getInfo.Decorator(Hurt)
- Extended functionality is similar to inheritance
- Extend the functions of different classes, which are not associated with the original class;
Observer mode (custom events)
- Observer Pattern defines a dependency between an object and other objects. When an object changes, other objects that depend on it will be updated (decoupling, delayed execution, one to many dependency)
- Custom event binding addEvent
- Custom event trigger trigger
- Custom event remove removeEvent
- Implementation case GameEvent class
example:
// Observer mode class Event { constructor () { // Save event this.handles = {} } addEvent (eventName, fn) { if (typeof this.handles[eventName] === 'undefined') { this.handles[eventName] = [] } this.handles[eventName].push(fn) } trigger (eventName) { this.handles[eventName].forEach(fn => { fn() }) } } let obj1 = { fn () { console.log('fn') } } let myEvent = new Event() // Add event myEvent.addEvent('myEvent', obj1.fn) setTimeout(() => { myEvent.trigger('myEvent') }, 2000)
proxy pattern
- Proxy mode provides a proxy for other objects to control access to this object, similar to mediation in life.
// proxy pattern let zhangsan = { sellHouse (num) { console.log(`shattered ${num}My house`) } } // Use proxy mode let proxySeller = { sellHouse(hasSold, num) { if (hasSold) { zhangsan.sellHouse(num - 2) } else { zhangsan.sellHouse(num) } } } zhangsan.sellHouse(100) proxySeller.sellHouse(true, 100)
Adapter mode
- A bridge between two incompatible interfaces that converts the interface of one class into another that the customer wants. The adapter pattern allows classes that cannot work together because of interface incompatibility to work together.
// Adapter mode function getUser () { return [ { name: 'zhangSan', age: 18 }, { name: 'liSi', age: 17 }, { name: 'wangWu', age: 19 } ] } // Want to get [{zhangSan: 18}, {liSi: 17}, {wangWu: 19}] // Define an adapter for one layer conversion function adaptor (users) { const arr = [] for (let i = 0; i < users.length; i++) { arr[users[i].name] = users[i].age } return arr } let newArr = adaptor(getUser())