introduce
The basic theory of software development has always been a weak point for me who is not a professional. Some time ago, I set up a flag by organizing the design patterns and design principles in the book “JavaScript Design Patterns and Development Practices” into an easy-to-understand note π
One is to deepen their understanding, forced output, two can ask you to help correct errors, to prevent their own understanding deviation;
Brush like boiling point friends may have seen recently the design pattern of hair sticky notes π, due to see her in the morning and evening, there are few blocks of time, only a few minutes a day to sort out one or two notes, well this flag is only just completed, harvest a lot of really, this is a list of the finishing notes, if there are any errors, please clap brick, em… , clap hard πππ.
Perhaps many excellent patterns patterns are imperceptisibly in your code and implementation ideas, but not the name, if a rough understanding of the design pattern, at least and others can discuss the implementation of the idea of a few nouns (joke)π΄, after all, as a programmer these knowledge is very important.
Three design principles
Single Responsibility Principle (SRP)
For a class, there should be only one cause for it to change.
The responsibility of the Single Responsibility Principle (SRP) is defined as “cause of change”. If we have two motivations to rewrite a method, then the method has two responsibilities. Each responsibility is an axis of change, and if a method takes on too many responsibilities, the more likely it is that it will need to be rewritten as requirements evolve.
At this point, this method is usually an unstable, modify the code is always a dangerous thing, especially when the two duties coupled together, a job change might affect the realization of other responsibilities, unexpected damage, the coupling is low cohesion and delicate design.
The SRP principle is: one object/method that does only one thing.
The difficulty in applying the SRP principle is how to separate responsibilities
The SRP principle is one of the simplest of all principles and one of the most difficult to apply correctly. To be clear, not all responsibilities should be separated.
- On the one hand, if two responsibilities always change at the same time as requirements change, there is no need to separate them. For example, in Ajax requests, creating an XHR object and sending an XHR request almost always go together, so there is no need to separate the responsibilities of creating an XHR object and sending an XHR request.
- On the other hand, the axis of change of responsibilities only makes sense if they are certain to change, and even if two responsibilities have been coupled together and they have not yet shown signs of change, then it may not be necessary to actively separate them, even if the code needs to be refactoring.
Advantages and disadvantages of the SRP principle The advantage of the SRP principle is that it reduces the complexity of a single class or object, and breaks objects down into smaller granularity according to their responsibilities, which facilitates code reuse and unit testing. When one responsibility needs to be changed, the other responsibilities will not be affected.
The most obvious drawback is the complexity of writing code. When we break objects down into smaller granularity by responsibility, we actually make it harder for those objects to relate to each other.
Principle of Least Knowledge (LKP)
The principle of least knowledge (LKP) states that a software entity should interact with as few other entities as possible. Here, software entity is a broad concept, including not only objects, but also systems, classes, modules, functions, variables and so on.
Reduce the connections between objects
The least knowledge principle requires that we design programs to minimize interactions between objects. If two objects do not have to communicate directly with each other, then the two objects do not have to communicate directly with each other.
Law of Demeter (LoD)
The Least knowledge principle is also known as the Law of Demeter (LoD), a name derived from a 1987 Northeastern University research project called Demeter. In actual development, the choice to make code conform to the least-knowledge principle depends on the specific environment.
Open – closed principle
The open-closed principle was first proposed by Bertrand Meyer, designer of Eiffel language, in his work Object-oriented Software Construction. It is defined as follows:
Software entities (classes, modules, functions), etc., should be extensible, but not modifiable.
The idea of the open-close principle:
When you need to change the functionality of a program or add new functionality to the program, you can use the method of adding code, but you are not allowed to change the source code of the program.
practice
- Eliminate conditional branches with object polymorphism
- Identify the areas of the program where change is going to occur, encapsulate the changes, and isolate the stable parts from the parts that are likely to change. In the evolution of the system, we only need to replace the parts that are easy to change, using the following method to change the parts.
- Place link
- Use the callback function
Open-close principles and design patterns
Both concrete design patterns and more abstract object-oriented design principles, such as the single responsibility principle, the least knowledge principle, and the dependency inversion principle, have emerged to make programs conform to the open-close principle.
Stack design pattern and overdesign
Keeping a program completely closed is not easy. Even if technically possible, it would take too much time and effort. Here’s a quote from Uncle Bob’s Agile Software Development Principles, Patterns, and Practices:
There is an old saying, “Fool me once, shame on you. Fool me again, shame on me.” This is also an effective approach to software design. We allow ourselves to be fooled once in a while to prevent the software from being saddled with unnecessary complexity.
It’s a bit like Seiya saying: “A saint cannot be knocked down twice by the same move.”
14 design patterns
The singleton pattern
That is, a class is guaranteed to have only one instance and a global point of access to it.
The singleton pattern is a common pattern, and there are some objects that we usually only need one, such as thread pools, the global cache, window objects in browsers, and so on. In JavaScript development, the singleton pattern is also very versatile. Imagine that when you click the login button, a login float window will appear in the page. The login float window is unique and will only be created once, no matter how many times you click the login button. The login float window is suitable for the singleton mode.
The strategy pattern
Define a set of algorithms, encapsulate them one by one, and make them interchangeable.
A program based on the policy pattern consists of at least two parts.
- The first is a set of policy classes that encapsulate specific algorithms and are responsible for specific computing processes.
- The second part is the Context class, which accepts requests from customers and then delegates them to one of the policy classes.
The proxy pattern
The proxy pattern provides a surrogate for an ontology object in order to control access to the ontology.
The key to the proxy pattern is to provide a surrogate object to control access to an object when it is inconvenient for us to access it directly or when we do not need it. We are actually accessing the surrogate object. After the surrogate object does some processing on the request, it passes the request to the ontology object
It can help objects filter out requests that don’t meet certain criteria, defer costly requests until they are really needed, and so on.
Iterator pattern
Providing a way to access the elements of an aggregate object sequentially without exposing the object’s internal representation.
The iterator pattern separates the process of iterating from the business logic. After using the iterator pattern, you can access each element in order, even if you don’t care about the internal construction of the object, such as the $. Each function in jQuery.
Internal and external iterations
An internal iteration calls all elements at once, while an external iteration needs to manually trigger the next element iteration, as shown in the figure:
Iterable characteristic
Whether it is an internal iterator or an external iterator, the aggregate object being iterated over can be iterated as long as it has the length property and can be accessed with a subscript.
The iterative sequence
The iterator pattern provides a way to loop through each element of an aggregate object, but it does not specify whether we should loop through the aggregate object in order, in reverse order, or in order.
Termination iterator
Iterators can provide a way out of a loop, just like breaks in a normal for loop.
summary
The iterator pattern is a relatively simple pattern, so simple that many times we don’t consider it a design pattern. Most current languages have iterators built in.
Publish-subscribe pattern
The publis-subscribe pattern, similar to the observer pattern, defines a one-to-many dependency between objects, so that all dependent objects are notified when an object’s state changes.
The publis-subscribe pattern is widely used in asynchronous programming as an alternative to passing callbacks.
role
Instead of a hard-coded notification mechanism between objects, one object no longer explicitly calls an interface of another.
Two objects are loosely coupled together and can communicate with each other without knowing much about each other’s details.
When a new subscriber appears, the publisher’s code does not need to be modified; Similarly, when the publisher needs to change, it will not affect the previous subscribers. As long as the convention event names do not change, you are free to change them.
Key points of subscription implementation
- The publisher
- The subscription list
- The subscribe method
- Release method
- Unsubscribe method
The advantages and disadvantages
The benefits of the publis-subscribe pattern are obvious. The decoupling between time and objects, and from an architectural point of view, both MVC and MVVM involve the publis-subscribe pattern.
Creating the subscriber itself takes a certain amount of time and memory. After subscribing to a message, the message may never happen, but the subscriber is always in memory. In addition, the publish-subscribe pattern weakens the connections between objects, and when overused, the necessary connections between objects are buried in the background, making it difficult to keep track of, maintain, and understand the application.
Command mode
Command mode is one of the simplest and most elegant. A command in command mode refers to an instruction that performs something specific.
The origin of the command pattern is an object-oriented alternative to the callback function. Unlike many other languages, JavaScript can easily implement the command pattern with higher-order functions.
Encapsulating command class
Encapsulated in ordinary functions
JavaScript, as a language that treats functions as first-class objects, has long incorporated the command pattern as well as the policy pattern. Operator blocks do not have to be wrapped in command classes, but can also be wrapped in normal functions.
Undo command: If a command needs to run for a long time, you can add an undo operation.
Command queue: We store commands in a queue, which can be easily implemented such as “playback”, “back” function.
Macro command: A set of commands, executed one at a time.
Command mode is an invisible mode in the JavaScript language.
Portfolio model
The combinatorial pattern is to build larger objects out of smaller child objects, which may themselves be made up of smaller “grandchildren.”
The composite pattern groups objects into a tree structure to represent a partial-whole hierarchy. In addition to being used to represent tree structures, another benefit of the composite pattern is that it allows users to use individual and composite objects consistently through the polymorphic representation of objects.
advantages
It provides a scheme for traversing the tree structure, and the composite pattern can describe the hierarchy of objects very conveniently.
All objects in a composite structure are used uniformly, regardless of whether it is a composite object or a single object.
The composite pattern is not a parent-child relationship
Sometimes the parent and parent objects are referred to as the parent node, but it is important to know that they are not really a parent.
A necessary condition for
The composition pattern is appropriate only if each leaf object in the list is treated in a consistent manner.
summary
We can apply the same operations to both composite objects and individual objects. For the most part, we can ignore the differences between composite objects and individual objects and treat them in a consistent way.
Template method pattern
Design patterns that rely heavily on abstract classes, using some methods of inheriting and overriding the superclasses to implement functionality.
Made up of two parts
- Abstract superclass: encapsulates a subclass’s algorithm framework, public methods, and the execution order of all methods in the subclass.
- Subclass: inherits from the abstract class, choosing to override some methods of the superclass in terms of the overall algorithm structure.
What about abstract classes that JavaScript doesn’t provide syntactically?
- The interface checks to ensure that a subclass overrides a method of its parent class.
- Throws an exception without overriding a method.
summary
The template method pattern is a typical design pattern that encapsulates changes to improve system scalability. The class and execution order of methods of subclasses are defined in the abstract class and are immutable. New functions can be implemented by adding subclasses without changing the abstract parent class or other subclasses, which also conforms to the open-close principle.
The flyweight pattern
The Share element pattern is a pattern for performance optimization that uses sharing techniques to efficiently support a large number of fine-grained objects.
The enjoy element pattern requires that the attributes of an object be divided into internal and external states, with the goal of minimizing the number of shared objects.
Internal state is stored inside the object
- Internal state can be shared by several objects.
- The internal state is independent of the specific scenario and usually does not change.
- The external state is scenario-dependent and varies according to the scenario. The external state cannot be shared
Enjoy the scenarios where the meta pattern applies
- Most of the state of an object can become an external state.
- A large number of similar objects are used in a program.
- Due to the large number of objects used, there is a large memory overhead.
- Stripping out an object’s external state, you can replace a large number of objects with a relatively small number of shared objects.
Object pooling
The object pool maintains a pool of free objects. When an object is needed, it is retrieved from the pool instead of new. If there are no free objects in the pool, a new object is created, and when the acquired object has completed its duties, it enters the pool and waits for the next acquisition.
summary
Enjoy meta patterns are created to solve performance problems, and most patterns are created for different reasons. In a system with a large number of similar objects, the enjoy element pattern is a good way to solve the performance problems caused by a large number of objects.
Chain of Responsibility model
Avoid coupling between the sender and receiver of a request by giving multiple objects a chance to process the request, chain the objects and pass the request along the chain until one object processes it.
Disadvantages of traditional implementations
A traditional implementation is like a chain with a dead knot in it. To add, remove, or move a node, the chain must first be broken.
Chain of Responsibility pattern benefits
The biggest advantage of the chain of responsibility pattern is that it decouple the complex relationship between the request sender and N receivers. The request sender only needs to know the first node in the chain, which weakens the strong connection between the sender and a group of receivers.
Chain of Responsibility pattern shortcomings
There is no guarantee that a request will be processed by the nodes in the chain, and most nodes do nothing but pass the request along. From a performance perspective, we want to avoid the performance cost of a long chain of responsibilities.
summary
The chain of responsibility pattern is one of the most overlooked patterns in JavaScript development. When used properly, you can reduce the coupling between the object that initiates the request and the object that processes it, and you can freely vary the number and order of nodes in the chain of responsibility.
The mediator model
To reduce the complexity of communication between multiple objects and classes, it provides a mediation class that handles communication between different classes and supports loose coupling, making the code easy to maintain.
Object-oriented design encourages the distribution of behavior across objects, dividing objects into smaller granularity, which helps to increase the reusability of objects, but may in turn reduce the reusability of these fine-grained objects as the connections between them proliferize.
The role of the mediator model
The role of the mediator pattern is to break the tight coupling between objects.
When a mediator object is added, all related objects communicate through the mediator object rather than referencing each other, so when an object changes, the mediator object only needs to be notified.
Intermediaries make objects loosely coupled and can change their interactions independently. The intermediary model turns a netted many-to-many relationship into a relatively simple one-to-many relationship.
The downside of the intermediary model is that it adds a new intermediary object to the system, because the complexity of the interaction between objects shifts to the complexity of the intermediary object, making the intermediary object often large. The intermediary object itself is often a difficult object to maintain.
Decorator mode
The way in which responsibility is dynamically added to an object while the program is running without changing the object itself is called decorator mode.
In program development, you don’t want a class to be inherently large, with many responsibilities at once; The decorator pattern allows you to dynamically add additional responsibilities to an object without affecting other objects derived from that class.
Decorators are also wrappers
Before Design Patterns was written, GoF wanted to call the decorator pattern the wrapper pattern.
Functionally, decorator describes this pattern well, but structurally, wrapper is more appropriate. Decorator patterns embed an object within another object, effectively wrapping the object around another object, forming a chain of wrappers. Requests are passed along the chain to all objects, and each object has a chance to process the request
The difference between decorator and proxy patterns
The most important difference between the proxy and decorator patterns is their intent and design purpose.
Both patterns describe how to provide some degree of indirect reference to an object, and both implementations retain a reference to another object and send a request to that object.
The purpose of the proxy pattern is to provide an alternative to an ontology when direct access to the ontology is not convenient or desirable. The ontology defines key functions, while the Proxy provides access to the ontology by doing something extra, or by denying access to it. The Proxy pattern emphasizes a relationship (the relationship between the Proxy and its entities) that can be expressed statically and determined from the start.
Decorator patterns are used to dynamically add behavior to an object, and decorator patterns are used when the full functionality of an object is not initially known. Whereas the proxy pattern usually has only one layer of agents, the decorator pattern often forms a long chain of decorations.
The state pattern
Allows an object to change its behavior when its internal state changes.
State pattern is the key to distinguish between the internal state of things, things changes will bring things behavior changes, each state are encapsulated into a separate class, related to the state of encapsulated inside the class, when a behavior of the request object, entrusting the request to the behavior of the current state of the object.
advantages
- A state pattern defines the relationship between state and behavior and encapsulates them in a class. By adding new state classes, it is easy to add new states and transitions.
- Using an object instead of a string to record the current state makes the state switch more clear. The request action in the Context and the behavior encapsulated in the state class can very easily change independently of each other.
- The disadvantage of the state pattern is that many state classes are defined in the system
Policy mode and state mode
- Each has a context, some policy or state class, to which the context delegates the request for execution.
- When using policy mode, customers must be familiar with the role of these policy classes so that they can switch algorithms at any time.
- In state mode, the behavior of the state and the behavior of the state are already encapsulated, the transition between states is already defined, and the “changing behavior” happens within the state mode.
Javascript-state-machine Finite state machine
Github.com/jakesgordon…
Adapter mode
The purpose of the adapter pattern is to solve the problem of incompatible interfaces between two software entities.
When we try to call an interface of a module or object and find that the format of the interface does not match the current requirements, we create an adapter and convert the original interface to another interface that the customer wants, and the customer only needs to deal with the adapter. With the adapter pattern, two software entities that would otherwise not work due to interface incompatibilities can work together.
The adapter pattern is a “better late than never” pattern that no one uses from the beginning of the program design.
summary
The adapter pattern is a relatively simple pair of patterns. There are patterns that are very similar in structure to the adapter pattern, such as decorator, proxy, and facade patterns, which are all “wrapper patterns,” where one object wraps another. The key to distinguishing them is still the intent of the pattern.
Code refactoring tips
Here’s what it said:
In some ways, the purpose of design patterns is to provide goals for many refactoring behaviors.
Refining function
If there is a piece of code in a function that can be isolated, it is best to put that code in a separate function.
- Avoid large functions.
- Independent functions facilitate code reuse.
- Independent functions are easier to override.
- Independent functions, if well named, are themselves annotations.
Merge repeated conditional fragments
If a function has conditional branch statements, and the conditional branch statements spread duplicate code, it may be necessary to do a merge and deduplication.
Extract conditional branch statements into functions
In programming, complex conditional branch statements are an important reason that programs are difficult to read and understand, and can easily lead to a large function.
Rational use cycle
In the body of a function, if some code is actually doing some repetitive work, the proper use of loops can not only accomplish the same function, but also make the code less.
Instead of nesting conditional branches, let functions exit early
In the words of Refactoring:
Nested conditional branches are often written by programmers who believe that there is only one outlet per function. But in fact, if you’re not interested in the rest of the function, you should quit immediately. Directing the reader to see some useless else snippet will only hinder their understanding of the program.
Pass object arguments instead of a long argument list
The more arguments a function receives, the harder it is to understand and use. When using, it is also necessary to avoid missing a parameter or having two parameters in the wrong position. With an object, you don’t have to worry about the number and order of arguments, as long as you keep the corresponding key values unchanged.
Minimize the number of parameters
Functions that do not require any arguments to be passed in are favored. In real development, passing arguments to functions is inevitable, but we should try to minimize the number of arguments that functions receive.
Use less triadic operators
Using a triplex operator is at the same level of time as using if and else code a million times. If the conditional branching logic is simple and clear, you can use triplet operators. If the logic is complex, it is recommended to use if and else.
The downside of chained calls is that debugging is very inconvenient, and if the chain is prone to change, the normal call form is recommended.
Exit the multiloop with return
Given that there is a double loop in the body of the function, either using the control marker variable or setting the loop marker is a bit confusing. The problem with using return to exit the method directly is that the code after the loop cannot be executed in the future. You can return the function instead.
The end of the
If you can see, this is really too much, this is a list of summary of knowledge points, itself is boring, when I was sorting summary has almost lost patience π€§, can finish must be very powerful, also want to please recommend a few book about software design theory, thank dadada ~ ~ ~, oh, yes π€, liao SAO, If there is a harvest, give a careful heart πππ.