What are design patterns?
A pattern is a reusable solution that can be applied to common problems in software design. Another explanation is a template for how we solve problems – templates that can be used in many different situations.
Classification of design patterns
1. Create the schema
The singleton pattern
The Singleton Pattern, also known as the Singleton Pattern, guarantees that a class has only one instance and provides a global access point to it
Implementation method:
Creates a class that contains a method that creates a new instance object if no object exists. This method simply returns a reference to the object if it exists.
Implementation scheme:
- Lazy: Create when you need it (scenario: not necessary, expensive to create, lazy to load, need to boot quickly)
- Hungry: created when the system starts (scenario: must be used, temporary creation affects response speed)
- Multiple cases: a fixed number of identical instances of the same class
Code implementation
let ProxyCreateSingleton = (function(){ let instance; Return function(name) {if (instance) {return instance; } return instance = new Singleton(name); }}) (); Let Singleton = function(name) {this.name = name; } Singleton.prototype.getName = function() { console.log(this.name); } let Winner = new PeozyCreateSingleton('Winner'); let Looser = new PeozyCreateSingleton('Looser'); console.log(Winner === Looser); // true console.log(Winner.getName()); // 'Winner' console.log(Looser.getName()); // 'Winner'Copy the code
Applicable scenario
- References to third-party libraries (multiple references will only use one library reference, such as jQuery)
- Popup window (login box, information promotion box)
- Shopping cart (only one shopping cart per user)
- Global State Management Store (Vuex/Redux)
The advantages and disadvantages
- Advantages: Applicable to a single object, only one object instance is generated, avoiding frequent creation and destruction of instances, reducing memory consumption.
- Disadvantages: Not applicable to dynamically extending objects or scenarios where multiple similar objects need to be created.
Factory method pattern
Returns instances of different classes based on different inputs, typically used to create objects of the same class
Code implementation
Constructor (name = ", viewPage = []) {if(new.target === User) {throw new Error(' Abstract class cannot be instantiated! '); } this.name = name; this.viewPage = viewPage; } } class UserFactory extends User { constructor(name, viewPage) { super(name, viewPage) } create(role) { switch (role) { case 'superAdmin': Return new UserFactory (' super administrator '[' front page', 'address book', 'found pages',' application data ', 'rights management']); break; Case 'admin': return new UserFactory(' normal user ', [' home ', 'address book ',' discover page ']); break; Case 'user': return new UserFactory(' normal user', [' home ', 'address book ',' discover page ']); break; Default: throw new Error(' superAdmin, admin, user')}}} let userFactory = new userFactory (); let superAdmin = userFactory.create('superAdmin'); let admin = userFactory.create('admin'); let user = userFactory.create('user');Copy the code
Applicable scenario
- When our object or component setup involves a high level of complexity.
- When we need to easily generate entities of different objects based on our environment.
- When we work on many small objects or components that share the same property.
- When a composite object works with other objects that only need to satisfy an API convention (aka a duck type). This is useful for decoupling.
The advantages and disadvantages
Advantages:
- Code coupling is reduced and object generation is left to subclasses
- The open closed principle is implemented – no changes to the original code are required each time a child product is added
Disadvantages:
- Increased code volume, each specific product requires a specific factory
- When adding an abstract product, that is, adding another product family requires modifying the factory to violate OCP
Abstract Factory pattern
The factory abstraction of a class enables its business to be used for creating clusters of product classes rather than being responsible for creating instances of a class of products
Code implementation
switch (type) { case 'wechat': return UserOfWechat; break; case 'qq': return UserOfQq; break; case 'weibo': return UserOfWeibo; break; default: Let WechatUserClass = getAbstractUserFactory('wechat'); throw new Error(' superAdmin ', admin, user'); let QqUserClass = getAbstractUserFactory('qq'); let WeiboUserClass = getAbstractUserFactory('weibo'); Let wechatUser = new WechatUserClass(' wechatUser '); let qqUser = new QqUserClass('QQ'); Let weiboUser = new WeiboUserClass('微博');Copy the code
Applicable scenario
A set of instances all have the same structure
The advantages and disadvantages
advantages
- Code decoupling
- Implementation of multiple product families (families of related products), while the factory approach pattern of a single product, can meet more production needs
- Well meet the OCP open and closed principle
- In the abstract factory pattern, we can define and implement more than one interface, and a factory can generate more than one product class, which is very flexible and extensible for the production of complex objects
disadvantages
- Extending a product family is cumbersome and would violate OCP because all factories need to be modified. For example, if we have a product family made up of a computer and a mouse, it would be cumbersome to add a keyboard after we write the code
- Because the abstract factory pattern is an extension of the factory method pattern, it is generally cumbersome
Builder model
By separating the construction layer of a complex object from its presentation layer, the same construction process can be represented differently.
Code implementation
{var Human = function (param). This skill = param && param. Skill | | 'secret'; This. Hobby = param && param. Hobby | | 'secret'; } Human.prototype = { getSkill: function () { return this.skill; }, getHobby: function () { return this.hobby; Var Named = function (name) {var that = this; Function (name, that) {that. WholeName = name; if (name.indexOf(' ') > -1) { that.FirstName = name.slice(0, name.indexOf(' ')); that.SecondName = name.slice(name.indexOf(' ')); } })(name, that); Var Work = function (Work) {let that = this; (function (work, that) {switch (work) {case 'code': that. Work = 'engineer '; That. WorkDescript = 'get addicted to programming '; break; Case 'UI': case 'UE': that. Work = 'designer '; That. WorkDescript = 'Design is more like art '; break; Case 'teach': that. Work = 'teacher '; That. WorkDescript = 'Sharing is also a kind of happiness '; break; default: that.work = work; That. WorkDescript = 'Sorry, we are not clear about your choice of job description' break; (work, that)}}}) / / change the desired position work. The prototype. ChangeWork = function (work) {this. Work = work; } / / add descriptions of Work Work. The prototype. ChangeDescript = function (setence) {enclosing workDescript = setence; Var Person = function (name, work) {var _person = new Human(); _person.name = new Named(name); _person.work = new Work(work); return _person; } var person = new person (' code'); console.log(person.skill); / / confidentiality console. The log (person. Name. FirstName); / / small console. The log (person. Work. The work); // Engineer console.log(person.work.workdescript); / / indulge in programming the person every day. The work. ChangeDescript (' change my job description '); console.log(person.work.workDescript); // Change the job descriptionCopy the code
Applicable scenario
- The same method, different execution sequence, produce different products
- The components of products are similar, and different products can be obtained by assembling different components
- There are many mandatory parameters to verify when creating.
- Parameter evaluation is sequential and interdependent during creation.
- There are many steps to creating an object, all of which are successful.
The advantages and disadvantages
Advantages:
- Using the Builder pattern decoupled the product’s build process from the product’s performance
- Convenient expansibility
- Better reusability
Disadvantages:
- The Builder mode is generally suitable for similar components between products. If there is a large difference between products and the reusability is not high, the builder mode should not be used.
- The creation of instances adds a lot of extra structure, which undoubtedly adds a lot of complexity. If the object granularity is small, then we are better off creating the object directly;
2. Structural mode
Adapter mode
Adapter Pattern, also known as wrapper Pattern, converts the interface (method, attribute) of a class (object) into another interface that users need to solve the problem of interface incompatibility between classes (object).
The main function is transformation matching, the purpose is to reuse the existing function
Code implementation
Show: function () {console.log(' Start rendering Google Maps '); }}; Const baiduMap = {display: function () {console.log(' start rendering baiduMap '); }}; const baiduMapAdapter = { show: function(){ return baiduMap.display(); }}; renderMap(googleMap); // Output: Start rendering Google Map renderMap(baiduMapAdapter); // Output: Start rendering Baidu mapCopy the code
Applicable scenario
When you want to use the functionality of an existing object but want to modify its interface
The advantages and disadvantages
- If existing functions are only incompatible with interfaces, using adapters to adapt existing functions can make the original logic get better reuse, which helps to avoid large-scale rewriting of existing code.
- Good scalability, in the realization of the adapter function, you can call their own developed functions, so as to easily expand the system function;
- Flexibility is good, because the adapter does not affect the function of the original object, if you do not want to use the adapter, then just delete it, there is no impact on the code using the original object;
The bridge model
Bridge Pattern, also known as Bridge Pattern, separates the abstract part from its implementation part so that they can all change independently
Code implementation
constructor(name) {
this.name = name
}
}
class Shape {
constructor(name, color) {
this.name = name
this.color = color
}
draw() {
console.log(`${this.color.name}-${this.name}`)
}
}
const red = new Color('red')
const yellow = new Color('yellow')
const blue = new Color('blue')
const line = new Shape('line', red)
const rectangle = new Shape('rectangle', yellow)
const circle = new Shape('circle', blue)
line.draw()
rectangle.draw()
circle.draw()
Copy the code
Applicable scenario
- If the components of the product have independent variation dimensions, bridge mode can be considered;
- Systems that do not want to use inheritance, or where the number of system classes increases dramatically because of multi-level inheritance;
- The finer the granularity of product components, the greater the necessity of component reuse, and the bridge mode can be considered.
The advantages and disadvantages
Advantages:
- Abstract and implementation parts are separated, which decouples the implementation layer (DOM element events trigger and execute specific modification logic) from the abstraction layer (element appearance and size modification function), facilitating layering.
- Improved scalability, free combination of multi-dimensional components, avoiding the strong coupling relationship brought by class inheritance, but also reduce the number of component classes;
- Users do not care about the details of the implementation, can be easily and quickly used;
Disadvantages:
- The bridge mode requires that the two components have no coupling relationship, otherwise they cannot change independently. Therefore, it requires correct identification of the dimension of system change, and the scope of use is limited.
- The introduction of bridge mode increases the system complexity.
Decorator pattern
The Decorator Pattern, also known as the Decorator Pattern, wraps and extends the original object by adding attributes or methods without changing the original object to make it more functional on the fly.
Code implementation
Function. Prototype. After = Function (afterfn) {var __self = this; return function () { var ret = __self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }}; Var showLogin = function () {console.log(' open login float '); } var log = function () {console.log(' + this.getattribute ('tag')); } showLogin = showLogin.after(log); Document.getelementbyid ('button'). Onclick = showLogin;Copy the code
Applicable scenario
- If you don’t want to add a lot of subclasses to your system
- When a lot of functions need to be generated by permutation and combination of a group of basic functions, it is difficult to achieve the inheritance relationship. At this time, decorator pattern can be very good to achieve;
- Consider the decorator pattern when an object’s functionality requires that it be dynamically added or destroyed
The advantages and disadvantages
Advantages:
- It allows users to dynamically modify objects without causing an explosion in the number of subclasses, add functions, loose coupling between decorator and decorator, and good maintainability;
- Decorators can be used to dynamically add and remove functions, and different decorators can be selected at run time to achieve different functions, with good flexibility
- Decorator mode scatters a series of complex functions into each decorator. Generally, a decorator only achieves one function. You can add more decorators to an object or use one decorator to decorate different objects, which is conducive to the reuse of decorator functions.
- Can choose the combination of different decorators, create the combination of different behavior and function, the original object code without change, can make the original object function becomes more powerful and more diverse, in line with the principle of open and closed
Disadvantages:
- The decorator pattern will generate many fine-grained decorator objects, which will increase the system complexity due to the diversity of interfaces and functions. The more complex the functions are, the more fine-grained objects are needed.
- Because of the greater flexibility, it is more prone to error, especially for multi-level decoration scenes, error location will be more complicated;