Good code has several characteristics
- Robustness,
- readability
- scalability
- reusability
There are several principles to follow
- The open closed principle
- Interface segregation
- Single responsibility
- Demeter’s rule
- Replacement on the Richter scale
There are different ways in which design patterns can solve certain problems
The factory pattern
Purpose: Let’s not worry about object creation
1. Simple factory function applicable scenarios * Correctly pass parameters, you can obtain the required object, without knowing the internal implementation details; * Internal logic (factory functions) determines which classes to instantiate or use by passing in arguments; * The number of objects created is small (stable), and the creation logic of objects is not complex; 2. Simple factory functions are not applicable * When new classes need to be added, factory methods need to be modified, which violates the open closed principle (OCP, open to extension, closed to source modification). Is the so-called success is also a failure. The function 'create' contains all the judgment code for creating objects (constructors). Adding new constructors requires modifying the function 'create' (judgment code). When the optional parameter 'role' becomes larger, the judgment code becomes bloated. Difficult to maintain. * Not suitable for creating multi-class objectsCopy the code
Builder model
The Builder pattern, also known as the generator pattern, is a more complex and less frequently used builder pattern. The Builder pattern does not return a simple product for the client, but a complex product composed of multiple parts
Separating the construction of a complex object from its representation allows the same construction process to create different representations. The Builder pattern is an object creation pattern.
var Dialog = function (){
this.type = ' ';
this.name = ' ';
this.element = ' ';
this.show = function(){
console.log(this.name + ':' + this.type + this.element); }}var noticeBuilder = function(){
this.dialog = new Dialog();
this.setType = function(){
this.dialog.type = 'notice';
}
this.setName = function(){
this.dialog.name = 'notice';
}
this.setElement = function(){
this.dialog.element = '<div>notice</div>';
}
this.getDialog = function(){
return this.dialog; }}var toastBuilder = function(){
this.dialog = new Dialog();
this.setType = function(){
this.dialog.type = 'toast';
}
this.setName = function(){
this.dialog.name = 'tip';
}
this.setElement = function(){
this.dialog.element = '<div>toast</div>';
}
this.getDialog = function(){
return this.dialog; }}function construct(ab){
ab.setType();
ab.setName();
ab.setElement();
return ab.getDialog();
}
var notice = new noticeBuilder();
var toast = new toastBuilder();
var noticeIns = construct(notice);
var toastIns = construct(toast);
noticeIns.show(); Notice
notice
toastIns.show();
toast
Copy the code
Summary of builder model:
** Advantages: In builder mode, the client does not need to know the details of the internal composition of the product, decoupling the use of the product from its creation, so that the same creation process can create different product objects * Each specific build class is relatively independent, easy to replace and add, meeting the switching principle
Disadvantages: * Builder mode requires multiple products to have the same creation process, if the products are very different, builder mode is not suitable. * If the internal structure of the product is complex and variable, many build classes need to be defined to implement this change, which can lead to large systems
A single mode
Purpose: A class can only be instantiated once
The practice uses an identifier to determine whether it has been instantiated
Application direction: For example, vuex vue-Router
var mySingleton=(function(){
// The constructor function
function singleton(options){
options=options || {}
this.name='SingletonTestor';
this.pointX=options.pointX || 6
this.pointY=options.pointY || 10;
}
function init(){
let now=new Date(a)// Private methods
this.name='Public properties'
this.getISODate()=function(){
returnnow.toISOString(); }}// Cache variables for singletons
var instance;
var _static={
name:'SingletonTestor'.getInstance:function(options){
if(! instance){ instance=new singleton(options)
}
return instance
}
}
return _static
})()
var singletonTest=mySingleton.getInstance({
pointX:5.pointY:5
})
console.log(singletonTest) //singleton { name: 'SingletonTestor', pointX: 5, pointY: 5 }
var singletonTest1=mySingleton.getInstance({
pointX:10.pointY:10
})
console.log(singletonTest1) // singleton { name: 'SingletonTestor', pointX: 5, pointY: 5 }
// The singleton creates the object only once, so the second time returns the previously created object
Copy the code
Global object storage
function store(){
if(store.instance){
return store.instance
}
store.instance=this
}
/ / with
function store(){
if(! (this instanceof store)){
return new store()
}
}
Copy the code
The above code can be used either as new or directly, so we use a static variable instance to keep track of whether or not it’s been instantiated, and if it’s instantiated it returns the instance, and if it’s not instantiated the first time we call it, we assign this to the static variable, because we’re using a new call, This refers to the instantiated object and will implicitly return this
var a=new store()
var b=store()
a==b
Copy the code
Decorator pattern
Purpose helps us to better extend methods instead of inheritance to better extend
Application scenario When we want to expand the content but not easy to modify
1. Rewrite the new method. 2. Call the old method. 3
class Circle {
draw(){
console.log("Draw a circle."); }}class Decorator {
constructor(circle){
this.circle = circle;
}
draw(circle){
this.circle.draw();
this.setRedBorder(circle);
}
setRedBorder(circle){
console.log("Set red border"); }}// Test the code
let circle = new Circle();
circle.draw(); // Draw a circle
// The function of the decorator
let decorator = new Decorator(circle);
decorator.draw(); // Draw a circle with a red border
Copy the code
Adapter mode
The Adapter Pattern acts as a bridge between two incompatible interfaces. In a nutshell, just two points:
- Old interface format or user incompatible.
- Add an adaptive conversion interface in the middle
var a = function () {
b()
}
// We used to use method A, but now method A has been renamed to method B, we use the adapter method
Copy the code
The bridge model
Objective: To extract the common part of the code and improve the code reuse rate
Scenario: Find a consistent way to write a bunch of core code, but some details differ
Advantages:
* Bridge mode improves the scalability of the system, any extension of one of the two change dimensions does not need to modify the original system, in line with the switching principle. * In most cases, the bridge pattern can replace the multi-tier integration scheme. Separate the interface from its implementation so that the implementation can vary along the respective dimensions.
Disadvantages:
* The use of bridge mode can increase the difficulty of understanding and designing the system. * The bridge pattern requires the correct identification of two independently varying dimensions in the system, so its scope of use is somewhat limited.
A typical use of the bridge pattern in JavaScript is the forEach function on Array objects.
This function is responsible for iterating through each element of a group. The callback function is the implementation part.
1. The simplest forEach simulation method:
let forEach = (arr, callbak) = > {
if (Object.prototype.toString.call(callbak) ! ="[object Function]" || !Array.isArray(arr))
return;
for (let i = 0; i < arr.length; i++) { callbak(arr[i], i); }}let arr = [1.2.3];
forEach(arr, (v, i) = > {
arr[i] *= 2;
})
Copy the code
2. Application of bridge mode in event monitoring
function getBeerById(id, callback) {
asyncRequest('GET'.'beer.url? id=' + id, function(resp) {
callback(resp.responseText)
});
}
elem.addEventListener('click', getBeerByIdBridge, false);
// Create a bridge to get the current context object this
function getBeerByIdBridge(e) {
console.log(this) // The currently clicked node node
getBeerById(this.id, function(beer) {
console.log('Requested Beer: ' + beer);
});
}
Copy the code
The appearance model
The goal simplifies the invocation of multiple subsystems
The facade provides a common external interface to the subsystem. The client object reads and writes data resources of each interface in the subsystem through a facade interface. A customer “you” obtains complex system information inside the computer through the appearance interface “Computer”.
The client shields subsystem components, reducing the number of objects the client needs to process and making the subsystem easier to use. The loose coupling between subsystem and client is realized.
Consider using facade mode when:
(1) At the initial stage of design, different layers should be consciously separated and an appearance pattern should be established between layers.
(2) In the development stage, the subsystem becomes more and more complex, and the appearance mode is added to provide a simple call interface.
(3) When maintaining a large legacy system, which may already be very difficult to maintain and extend, but contains very important functionality, develop a facade class for it so that the new system can interact with it.
Observer mode and publish/subscribe mode
Observer pattern: The comparison concept is explained by the fact that the target and observer are base classes, the target provides a set of methods to maintain the observer, and the observer provides an update interface. The specific observer and the specific target inherit their respective base classes, and then the specific observer registers itself with the specific target, scheduling the update method of the observer when the specific target changes.
Publish/subscribe mode: subscribers register the event they want to subscribe to the dispatch center. When the event is triggered, the publisher publishes the event to the dispatch center (with context), and the dispatch center schedules the processing code that subscribers register with the dispatch center.
Although the two modes are subscribers and publishers (specific observer can be regarded as the subscriber, specific goals can be considered a publisher), but the observer pattern is scheduled by specific goals, and publish/subscribe pattern is unified by the dispatching center, so the observer pattern between subscribers and publishers are dependent, and publish/subscribe pattern will not.