This is the 20th day of my participation in the August More Text Challenge
Author: Tom Brother wechat official account: Micro technology
In the face of complex business scenarios and ever-changing customer needs, how to respond to changes with one change and achieve rapid implementation with the minimum development cost, while ensuring the system has a low complexity, can ensure the continuous iteration ability of the system, so that the system has high scalability.
❝
These are the basic internal skills that a qualified architect must practice, but how to practice this magic skill??
❞
I have made a summary of commonly used software design patterns as follows:
(Considering the large content, in order to facilitate reading, the software design pattern series (a total of 23) is divided into four articles, each article explains six design patterns, using different colors to facilitate quick digestion and memory)
Previous review:
-
What do Ali architects do in the face of complex business architecture? (Issue 1)
-
Write bad code, often hate by colleagues, teach you a trick! (Issue 2)
This is the third installment of this article, which covers the template pattern, policy pattern, state pattern, observer pattern, visitor pattern, and memo pattern
1. Template mode
Definition:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. The template pattern allows subclasses to redefine specific steps of an algorithm without changing the structure of that algorithm.
Advantages: 1. Encapsulate the invariant part and expand the variable part. 2. Extract common code for easy maintenance. 3. The behavior is controlled by the parent class and implemented by the subclass. Disadvantages: Each different implementation requires a subclass to implement, resulting in an increase in the number of classes and high maintenance costs.
Core ideas:
-
AbstractTemplate: Defines a complete framework in which the order of method calls is defined, but some abstract methods are defined for subclasses to implement
-
AHandler: Concrete business subclass that implements abstract methods defined in AbstractTemplate to form a complete process logic
Code examples:
public TradeFlowActionResult execute(TradeFlowActionParam param, The Map context) throws ServiceException {try {/ / business logic to check this. ValidateBusinessLogic (param, context); } catch (ServiceException ex) { sendGoodsLog.info("SendGoodsAction->validateBusinessLogic got exception. param is " + param, ex); throw ex; } catch (RuntimeException ex) { sendGoodsLog.info("SendGoodsAction->validateBusinessLogic got runtime exception. param is " + param, ex); throw ex; } try {this.sendGoods(param, context); } catch (ServiceException ex) { sendGoodsLog.info("SendGoodsAction->sendGoods got exception. param is " + param, ex); throw ex; } catch (RuntimeException ex) { sendGoodsLog.info("SendGoodsAction->sendGoods got runtime exception. param is " + param, ex); throw ex; } try {// Add services (the result does not affect the core services) this.addition(param, context); } catch (ServiceException ex) { sendGoodsLog.info("SendGoodsAction->addition got exception. param is " + param, ex); throw ex; } catch (RuntimeException ex) { sendGoodsLog.info("SendGoodsAction->addition got runtime exception. param is " + param, ex); throw ex; } // return null; }Copy the code
The three abstract methods mentioned above (business logic validation, seller shipping business logic, and supplementary business) are all implemented in subclasses. It controls the main process structure without losing the flexibility to allow users to customize development on top of it. If new business gameplay comes in and the old process doesn’t meet the requirements, we can write new subclasses based on the template class.
Applicable scenarios:
-
Want to control the main flow of the algorithm, can not arbitrarily change the framework, but also want to retain the personality of the subclass business extension.
-
Remove duplicate code. Keep the parent class common code logic, so that the subclass does not need to repeat the common logic, only focus on specific logic, to remove the purpose of duplicate code in the subclass.
-
There are many cases, such as Jenkins’ process of pulling code, compiling, packaging, publishing and deploying as a general process, different systems (Java, Python, NodeJS, etc.) can develop and customize their own continuous publishing process according to their own needs.
2. Strategy mode
Definition:
Define a series of algorithms and place each algorithm in a separate class so that the objects of the algorithm can be replaced with each other.
It is up to the client to decide which specific policies to use in which situations.
Core ideas:
-
Context: Use different policy environments and select different policy implementation classes according to their own conditions to complete required operations. He holds a reference to a policy instance.
-
Abstract Policy Class (Strategy) : Abstract policies that define the methods to be implemented by each policy
-
A Realize policy class: Responsible for implementing policy methods defined in an abstract policy.
Code examples:
/** * @author */ public interface PromotionStrategy {// Activity type String promotionType(); Int recommand(String productId); } public class FullSendPromotion implements PromotionStrategy { @Override public String promotionType() { return "FullSend"; } @override public int recommand(String productId) {system.out.println (" Override "); return 0; } } public class FullReducePromotion implements PromotionStrategy { @Override public String promotionType() { return "FullReduce"; } @override public int recommand(String productId) {system.out.println ();} @override public int recommand(String productId) {system.out.println (); return 0; }} @author wechat official number: Public class Context {private static List<PromotionStrategy> promotionStrategyList = new ArrayList<>(); static { promotionStrategyList.add(new FullReducePromotion()); promotionStrategyList.add(new FullSendPromotion()); } public void recommand(String promotionType, String productId) { PromotionStrategy promotionStrategy = null; // Find the corresponding policy class for (PromotionStrategy Temp: promotionStrategyList) { if (temp.promotionType().equals(promotionType)) { promotionStrategy = temp; }} / / strategy called subclass promotionStrategy recommand (productId); }Copy the code
First, define the interface class PromotionStrategy for the activity strategy, and define the interface methods that each specific strategy algorithm implements, FullSendPromotion and FullReducePromotion.
Context is responsible for storing and consuming policies and brings together all policy subclasses. According to the parameters passed in, match the specific policy subclass, and then call the reCommand method to handle the specific business.
Benefits of using policies:
-
Improve code maintainability. Different policy classes are isolated and do not affect each other. Each new policy is isolated by a new class, avoiding if-ELSE large complex classes
-
Replace more algorithms dynamically and quickly. Due to the separation of scheduling policy and algorithm implementation and fixed interface specification, we can flexibly adjust and select different policy subclasses.
Applicable scenarios:
-
Compressed files can be in gzip, ZIP, and RAR formats. The client can select a compression policy. Different strategies can be interchangeable.
-
Marketing activities, according to the policy routing to choose different activities, different marketing activities isolated, to meet the open and closed principle.
-
The option is given to the client, which is suitable for those to C businesses that often adjust their policies, with high flexibility.
3. State mode
Definition:
A behavior design pattern that allows you to change the behavior of an object as its internal state changes, making it look as if it were changing its own class.
Changes in behavior are controlled by defining a series of state changes. Take e-commerce as an example, users’ orders will experience the following states: placed, paid, shipped, in delivery, to be picked up, received, transaction successful, transaction closed and so on.
Core ideas:
-
OrderContext: A class that stores the current state and provides methods to update the state.
-
Abstract State Class (OrderState) : Can be an interface or abstract class that declares what to do when a status update occurs
-
Concrete state classes (MakeOrderState, PayOrderState, ReceiveGoodOrderState) : A method that implements the abstract state class definition, specifying the code implementation logic after the corresponding state change according to the specific scenario.
Code examples:
*/ public interface OrderState {void handle(OrderContext context); } public class MakeOrderState implements OrderState {public static MakeOrderState = new MakeOrderState(); @override public void handle(OrderContext context) {system.out.println ("1, create order "); context.setCurrentOrderState(PayOrderState.instance); }} // Confirm receipt of goods Public class OrderContext {private OrderState currentOrderState; public OrderContext(OrderState currentOrderState) { if (currentOrderState == null) { this.currentOrderState = new MakeOrderState(); } else { this.currentOrderState = currentOrderState; } } public void setCurrentOrderState(OrderState currentOrderState) { this.currentOrderState = currentOrderState; } public void execute() { currentOrderState.handle(this); }}Copy the code
Running results:
1. Create an order 2. Pay by Alipay 3Copy the code
The core point of state pattern design is to find appropriate abstract states and transfer relations between states, and to change behavior by changing states.
Applicable scenarios:
-
Services need to perform different operations based on status changes. For example: the whole process of e-commerce ordering
-
You don’t want a lot of if-else code piled together, and you want the different state handling logic to be isolated, following the open and closed principle
4. Observer mode
Definition:
Also known as public-subscribe, it is a notification mechanism where all of an object’s dependencies are automatically notified and updated when it changes state. The idea that the sender (observed) and the receiver (observer, multiple) can be separated from each other is very popular in software development.
Message-oriented middleware such as Kafka and RocketMQ uses this architectural pattern, as well as Spring’s ApplicationEvent asynchronous event driver, which has a very low coupling characteristic.
Similar to other names:
-
Publisher — Subscriber
-
Producer — consumer
-
Event publishing – Event listening
Core ideas:
-
Publisher: Defines a set of interfaces to manage and trigger subscribers
-
PublisherImpl: A concrete implementation class that implements methods defined by the Publisher interface
-
Subscriber (Observer) : An Observer interface that notifies the subscriber when a publisher publishes a message or event.
-
Specific subscriber (WeixinObserver, EmailObserver) : a subclass of Observer that processes specific business logic
Code examples:
/** * @author 微信 号 * <p> */ public interface Publisher {void add(Observer Observer); void remove(Observer observer); void notify(Object object); } public class PublisherImpl implements Publisher { private List<Observer> observerList = new ArrayList<>(); @Override public void add(Observer observer) { observerList.add(observer); } @Override public void remove(Observer observer) { observerList.remove(observer); } @override public void notify(Object Object) {system.out.println (" create order and send notification "); observerList.stream().forEach(t -> t.handle()); Public interface Observer {public void handle(); Public class implements Observer {@override public void handle() { System.out.println(" Send email notification! ") ); }}Copy the code
The core essence of the Observer pattern is that the observed defines a notification list of all observer objects, and when the observed needs to send a notification, all observers in the list will be notified.
Applicable scenarios:
-
When a change in the state of one object requires changes to other objects. For example, after the successful payment of the order, the account balance needs to be deducted by notice
-
When an object changes, it only wants to send notifications without knowing who the recipient is. For example: microblog feed stream, fans can pull the latest microblog
-
The code is highly extensible. If you need to add an observer business process, you only need to add a subclass observer and inject it into the notification list of the observed. The coupling of the code is very low.
5. Visitor Pattern
Definition:
The visitor pattern is a software design pattern that separates operations from object structure, allowing one or more operations to be applied to a group of objects at run time.
Core ideas:
-
Behavior Interface (RouterVisitor) : Defines the behavior for accessing each Element. The method parameter is the Element to be accessed, and the number of methods is theoretically the same as the number of elements.
-
Behavior interface implementation classes (LinuxRouterVisitor, WindowRouterVisitor) : This needs to give the specific behavior generated when accessing each element class.
-
RouterElement: Defines an entry point to which a client object can “access” an interface that can get access operations.
-
Element interface implementation classes (JhjRouterElement, LyqRouterElement) : Pass the visitor RouterVisitor to this object as a parameter.
Code examples:
/** * @author Public interface RouterVisitor {// switch, sendData void sendData(JhjRouterElement JhjRouterElement); Void sendData(LyqRouterElement LyqRouterElement); } public class LinuxRouterVisitor implements RouterVisitor { @Override public void sendData(JhjRouterElement JhjRouterElement) {system.out.println (" In Linux, the switch sends data "); } @override public void sendData(LyqRouterElement LyqRouterElement) {system.out.println (" In Linux, the router sends data "); }} public class WindowRouterVisitor implements RouterVisitor {// delete... } /** * @author Public interface RouterElement {// sendData void sendData(RouterVisitor RouterVisitor); } public class JhjRouterElement implements RouterElement { @Override public void sendData(RouterVisitor routerVisitor) { System.out.println(" Switch starts working... ") ); routerVisitor.sendData(this); }} Public class LyqRouterElement implements RouterElement {// delete... }Copy the code
Applicable scenarios:
-
Dynamically bind different objects and object operations
-
By separating the behavior from the object structure, the responsibility of the object is separated and the code reusability is improved
6. Memo mode
Definition:
Also known as snapshot mode, used to store a snapshot of another object’s internal state for later recovery.
Core ideas:
-
Originator: In addition to creating its own required properties and business logic, it also saves and restores copies of objects by providing methods bak() and restore(memento).
-
Memento: Used to preserve the state of all attributes of the original object for future restoration operations.
Code examples:
/** * @author @data public class Originator {private Long id; private String productName; private String picture; Public Memento bak() {return new Memento(id, productName, picture); } public void restore(Memento m) {this.id = m.getid (); this.productName = m.getProductName(); this.picture = m.getPicture(); }} @data @allargsconstructor public class Memento {private Long constructor; private String productName; private String picture; }Copy the code
Applicable scenarios:
-
Online editor, accidentally close the browser, re-open the page, can recover previous content from the draft box
-
You do not want direct access to the internal state of an object, such as a logistics package
-
Other things like operating system automatic backup, database SAVEPOINT
Write in the last
A lot of people have learned design patterns, but they are always confused when they are working on projects, because they don’t understand what the core is, what the underlying logic is, as described in Design Patterns: Reusable Object-oriented Fundamentals.
Think about what should change in your design and encapsulate the concepts that will change.
The essence of software architecture is to find change and encapsulate it.
Business is changing, there is no fixed code answer, do not impose design patterns. Whichever design pattern you choose, try to meet SOLID principles and review yourself to see if it can meet the continuous scalability of the business. As the saying goes, “it doesn’t matter if a cat is black or white, it’s a good cat if it catches mice.”
More:
Github.com/aalansehaiy…
Author introduction: Tom brother, computer graduate student, the school recruited ali, P7 technical expert, has a patent, CSDN blog expert. Responsible for e-commerce transactions, community fresh, flow marketing, Internet finance and other businesses, many years of first-line team management experience