Design patterns are reusable solutions to common problems in software design. Design patterns are so fascinating that they have been explored in almost any programming language.

One reason is that it allows us to stand on the shoulders of giants, taking all the lessons of the past and making sure that we organize our code in an elegant way that meets the conditions we need to solve our problems. Design patterns also provide a common vocabulary for describing problems.

This is more convenient than us communicating syntactic and semantic descriptions to others through code.

The strategy pattern

Strategy Pattern, also known as policy Pattern, defines a series of algorithms, encapsulates them one by one, and makes them interchangeable. The encapsulated policy algorithm is generally independent, and the policy pattern adjusts which algorithm is used according to the input. The key is the separation of implementation and use of policies

A common implementation of the policy pattern

According to the above example, the Strategy model can be refined. The discount calculation method can be considered as Strategy, which can be replaced by each other, and the specific discount calculation process can be considered as the encapsulation Context, which can select different strategies according to needs.

There are mainly the following concepts:

  • Context : Encapsulates the context, invokes the required policy as required, shields the external direct call to the policy, only provides an external interface, and invokes the corresponding policy as required;
  • Strategy : policies, which contain specific algorithms whose methods have the same appearance and can therefore be replaced by each other;
  • StrategyMap : a collection of all policies to be invoked by the encapsulation context;

A program based on the policy pattern consists of at least two parts: a policy class and an environment class.

  • The policy class encapsulates the specific algorithm and is responsible for the specific calculation process, which can be understood as “executor”.
  • The Context class accepts the client’s request and delegates it to a policy class, understood as a “scheduler.”

Code implementation

Specific examples we use programming examples to demonstrate, better quantification.

The scenario is as follows: an e-commerce website hopes to hold an activity to sell inventory items through discount promotion. Some items can be reduced by 30 when they reach 100, some items can be reduced by 80 when they reach 200, and some items can be directly sold at a 20% discount (remembering the fear of being dominated by Double 11). This logic is given to us, how should we realize it?

Calculate the total price of goods by judging the input discount type, several if-else will satisfy the demand, but the disadvantages of this approach are also obvious:

  • As the number of discount types increases, if-else statements become more bloated;
  • If a new discount type is added or the algorithm for the discount type is changed, the implementation of the priceCalculate function needs to be changed, which violates the open-closed principle.
  • Poor reusability, if there is a similar algorithm in other places, but the rules are not the same, the above code can not be reused;

We can modify it to extract the part of the algorithm used to calculate the discount and save it as an object. The type of discount is used as key, so that the specific algorithm can be called through the key value index of the object when indexing:

This separates the implementation of the algorithm from the use of the algorithm and makes it very easy to add new algorithms:

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. That is, the second time you create a new object using the same class, you should get exactly the same object that you created the first time.

The singleton pattern is so called because it restricts a class to one instantiated object. The classic way to do this is to create 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.

In the JavaScript language, a singleton service is isolated from a code implementation of the global space shared to provide a single function access pointer.

Ensure that a class has only one instance and provide a global access point to access it.

A generic implementation of the singleton pattern

A variable is used to indicate whether an object has been created for a class, and if so, the next time an instance of the class is fetched, the previously created object is returned.

There are mainly the following concepts:

  • Singleton: specific class, this is the class we need to access, visitors to get an instance of it;
  • instance: singleton, which is an instance of a particular class. A particular class usually provides a getInstance method to get the singleton.
  • getInstance: a method to get singletons, or directly from the new operator;

There are a few implementation points to focus on:

  • Access always returns the same instance;
  • Instantiate itself, whether it is created as soon as it is loaded or when it is first accessed;
  • A getInstance method is typically provided to get an instance of it;

The bad:

Creating objects can only be created with getInstance, increasing the opacity of this class. Users of Singleton must be aware that this is a Singleton class, as opposed to fetching objects using new.

Code implementation

The window and Document global variables in the browser, both of which are singletons, are the same object whenever they are accessed. The window represents the window containing the DOM document, and the Document is the DOM document loaded in the window, providing their respective methods.

Suppose we have a need right now:

We need to manage a global data object. There can only be one data object. If there are more than one, the data will be out of sync.

This requirement requires that there be only one data store object globally, which is a typical scenario for the singleton pattern. We can directly apply the above code template, but the above code template must call getInstance to getInstance. If a user calls Singleton() or new Singleton(), this will cause problems. This time, we’ll write it in a way that makes it compatible with Singleton() and new Singleton(), which is more silly:

We use a static variable instance to record whether or not it was instantiated. If it was instantiated, we return the instance. If it wasn’t instantiated, we assign this to this static variable because we’re using a new call, This refers to the instantiated object and implicitly returns this.

If we also want to support direct calls to store(), we can use the same method we used in the previous factory pattern to check if this is an instance of the current class, and if not, we can use the new method instead:

Publish and subscribe model

The publish-subscribe Pattern (pub-sub), also known as the Observer Pattern, defines a one-to-many relationship in which multiple subscriber objects, or subject objects, listen to a publisher at the same time. The topic object notifies all the subscriber objects that subscribe to it when its state changes, enabling them to update themselves automatically.

describe

  • The publisk-subscribe pattern, also known as the observer pattern, defines a one-to-many dependency between objects. When an object’s state changes, all dependent objects are notified.
  • The publish-subscribe pattern is widely used in asynchronous programming as an alternative to passing callbacks.
  • Publish-subscribe can replace hard-coded notification between objects, so that one object no longer explicitly calls an interface of another object.
  • The publish-subscribe pattern allows two objects to be loosely coupled together, not knowing much about each other’s details, but not preventing them from communicating with each other. When a new subscriber appears, the publisher’s code does not need to be modified; Also, if the publisher needs to change, it will not affect the previous subscribers. As long as the previously agreed event names have not changed, you are free to change them.

Usage scenarios: cross-level communication, event binding, etc

A generic implementation of the subscription pattern

There are mainly the following concepts:

  • Publisher: publisher, responsible for notifying corresponding subscribers when messages occur
  • Subscriber: subscriber, who is notified when a message occurs
  • SubscriberMap: holds arrays of different types, holding arrays of all subscribers
  • type: message types, different message types to which subscribers can subscribe
  • subscribe: This method adds subscribers to the corresponding array in the SubscriberMap
  • unSubscribe: This method deletes the subscriber from the SubscriberMap
  • notify: This method iterates through each subscriber of the corresponding type in the notification SubscriberMap

The current structure is shown below

Let’s generalize it.

First we use the Immediately Invoked Function IIFE (Immediately Invoked Function Expression) to hide the SubscriberMap that we do not want to expose, and then we change the registered subscription behavior into a callback Function. This allows us to attach parameter information to message notifications and gives us more flexibility in handling notifications:

Code implementation

Vue implements a set of event mechanisms, one of which we are familiar with is EventBus. $on, $emit, Vuex, EventBus, EventBus, $on, $emit, Vuex, $on, $EMIT

Implementation of messaging between components, but in medium to large projects, Vuex is recommended, because if too many events are mounted on the Bus and events fly around, the source and sequence of messages can be confused, which can damage maintainability.

The mediator pattern

Mediator Pattern, also known as Mediator Pattern, loosens the tight coupling between objects without explicitly referring to each other, so that they can be changed independently. The core is the encapsulation of complex interactions between multiple objects.

According to the least knowledge principle, one object should know as little as possible about other objects. If the coupling between objects is too high, changing one object will affect many other objects, making it less maintainable. In a complex system, the coupling relationship between objects will be more complex, and the intermediary pattern is born to solve this problem.

The intermediary pattern you’ve seen in a similar scenario has the following characteristics:

  • The relationship between blind date parties/house buyers and sellers (target objects) is complicated, and the introduction of matchmakers/intermediaries (intermediaries) will greatly facilitate the communication between all parties.
  • If there is any change of ideas and requirements between the blind parties/buyers and sellers (target objects), the matchmaker/intermediary (intermediary) can timely inform the relevant parties, while the target objects do not communicate with each other;

A generic implementation of the mediator pattern

In the above example, parents are likable objects (colleagues were described in one of the earliest design pattern books), while matchmakers are likable to mediators. In the mediator pattern, the peer objects do not communicate with each other, but only with the mediator, and the peer objects need only know about the mediator. The main concepts are as follows:

  • ColleaguePeer objects, which know only the mediator but not the other peer objects, communicate with other peer objects through the mediator;
  • Mediator: intermediary, responsible for communication with various colleagues;

The structure diagram is as follows:

As you can see in the figure above, the network structure between colleague objects becomes a star structure with the use of the mediator pattern. Colleague objects do not need to know each other, complying with the least knowledge principle. If peer objects need to communicate with each other, it can only be done in the manner of a mediator, so that the strong coupling between peer objects becomes weak coupling and strong interdependence becomes weak interdependence, allowing these peer objects to change and reuse independently. The original interaction logic between colleague objects is encapsulated by the mediator, and each colleague object only needs to care about itself.

A simple implementation of the man-in-the-middle pattern can be found below, with both publish() and subscribe() methods exposed for use:

Code implementation

The purchase of goods

Suppose we are developing a page for purchasing a mobile phone. In the purchase process, we can choose the color of the mobile phone and input the purchase quantity. Meanwhile, the input content can be displayed in the page. There is also a button to dynamically display the next step (the color is sufficient to display the next step; Otherwise, the inventory is insufficient.

Idea: Introduce the mediator pattern, where all node objects talk only to the mediator.

When event behavior occurs in the dropdown selColor, selMemory, or text box selNum, only the mediator is notified that they have been changed, passing itself in as a parameter to the mediator so that the mediator can identify who has changed, leaving the mediator object to do the rest.

summary

In the future, we will continue to update the design mode of appearance mode, state mode, adapter mode, responsibility chain mode, decoration mode, etc., you can like and follow! 👍