Before I talk about the strategic pattern, LET me talk about an example that I came across in my work:

Demo

One project in my work is collaborative office software, which has a dynamic function, which needs to synchronize the dynamic information of different operations of customers on the project in real time. Assume the following four dynamics: xx project is added, XX project is updated, XX project is deleted, and XX project is restored.

What would you do if you had to do it?

We would most likely write the following code:

 
public class Message {
 
    public void changeMessage(int flag){
        if(flag.equals(MessageEnum. Project_update.getcode ())){// Dynamic display: Xx project messageEntity.setContent(userName+MessageEnum.project_update.getMessage ()+""+message.getName());
                messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
                messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
            }
            if(flag.equals(MessageEnum. Project_insert.getcode ())){// Dynamic display: Xx project messageEntity.setContent(userName+ messageenm.project_insert.getMessage ()+ is added""+message.getName());
                messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
                messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
            }
            if(flag.equals(MessageEnum. Project_delete.getcode ())){// Dynamic display: Xx project messageEntity.setContent(userName+MessageEnum.project_delete.getMessage ()+""+message.getName());
                messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
                messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
            }
            if(flag.equals(MessageEnum.project_back.getCode ())) {// Dynamic display: Xx project messageEntity.setContent(userName+MessageEnum.project_back.getMessage ()+""+message.getName()); messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode()); messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage()); }}}Copy the code

After testing, the above code works very well, but the above code is problematic. The problem: putting different algorithms for storing dynamic information in the same method makes this method very bulky (this is just a demo, so it doesn’t look bloated yet, if any).

Let’s look at the improvements above, where we treat the different dynamic algorithms as a single method

 
public class Message {
 
    public void changeMessage(int flag,MessageModel message){
        if(flag.equals(MessageEnum.project_update.getCode ())){// Dynamic display: xx project updateMessage(message)}if(flag.equals(MessageEnum. Project_insert.getcode ())){// Add xx item saveMessage(message)}if(flag.equals(MessageEnum. Project_delete.getcode ())){// Dynamic display: delete xx item deleteMessage(MessageModel message)}if(flag.equals(MessageEnum.project_back.getCode ())) {// Dynamic display: BackMessage (MessageModel message)}} private void saveMessage(MessageModel message) { messageEntity.setContent(userName+MessageEnum.PROJECT_INSERT.getMessage()+""+message.getName()); messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode()); messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage()); Private void updateMessage(MessageModel message) {private void updateMessage(MessageModel message) { messageEntity.setContent(userName+MessageEnum.PROJECT_UPDATE.getMessage()+""+message.getName()); messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode()); messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage()); Private void deleteMessage(MessageModel message) {private void deleteMessage(MessageModel message) { messageEntity.setContent(userName+MessageEnum.PROJECT_DELETE.getMessage()+""+message.getName()); messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode()); messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage()); } private void c {messageEntity.setContent(userName+MessageEnum.project_back.getMessage ()+""+message.getName()); messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode()); messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage()); }}Copy the code

The above code is a little better than the original one. It extracts each specific algorithm as a method, and when a specific algorithm changes, it only needs to modify the dynamic algorithm of the response.

But there are still problems with the improved code, so what’s the problem?

1. When we add a new dynamic message, first we need to add an algorithm method of the dynamic message, and then add an else if branch in the changeMessage method, is it very troublesome? It also violates the open-closed-principle, one of the design principles.

The open closed principle:

It is Open for extension. This means that the behavior of a module is extensible. As the requirements of the application change, we can extend the module to have new behaviors to meet those changes. That is, we can change the functionality of the module.

Closed for modification. To extend the behavior of a module, you do not have to change the source code or binary code of the module.

If we want to change the branch information in the if else, we need to find the branch information and know that it is modifying the code. Is there a way to make our dynamic modules scalable, maintainable, and easily responsive to change? There is a solution, of course, and that is the strategic pattern that we will talk about next.

define:

The policy pattern defines a series of algorithms, and encapsulates each algorithm, so that each algorithm can be replaced by each other, so that the algorithm itself and the client using the algorithm are separated and independent from each other.

structure:

1. Policy interface role IStrategy: Used to constrain a set of specific policy algorithms. The policy context role ConcreteStrategy uses this policy interface to invoke algorithms implemented by specific policies.

ConcreteStrategy implementation role: ConcreteStrategy implementation, i.e. concrete algorithm implementation.

3. StrategyContext role: The StrategyContext is responsible for interacting with specific policies. Usually, the policy context object holds a real policy implementation object, and the policy context allows the specific policy implementation to obtain relevant data from it and call back methods of the policy context object. A general implementation of policy pattern code:

Policy interface

package strategy.examp01; Public void algorithmMethod(); public void algorithmMethod(); }Copy the code

Specific strategy implementation:

package strategy.examp01; Public class ConcreteStrategy implements IStrategy {// Implements @override public voidalgorithmMethod() {
        System.out.println("this is ConcreteStrategy method..."); }}Copy the code
package strategy.examp01; Public class ConcreteStrategy2 implements IStrategy {// Implement @override public voidalgorithmMethod() {
        System.out.println("this is ConcreteStrategy2 method..."); }}Copy the code

Policy context:

package strategy.examp01; /** * strategy context */ public class StrategyContext {// Hold a strategy implementation reference private IStrategy strategy; Public StrategyContext(IStrategy Strategy) {this.strategy = strategy; } public voidcontextMethod(){// Call strategy implementation method strategy.algorithmMethod(); }}Copy the code

External client:

package strategy.examp01; Public class Client {public static void main(String[] args) {//1. IStrategy strategy = new ConcreteStrategy2(); StrategyContext CTX = new StrategyContext(strategy); StrategyContext CTX = new StrategyContext(strategy); //2. //3. Call the method of the context object to complete the callback ctx.contextMethod() for the implementation of the specific policy; }}Copy the code

policy-patternedadvantages:

  • The function of policy pattern is to define a series of algorithms through abstraction and encapsulation, so that these algorithms can be replaced with each other, so that a common interface is defined for these algorithms to restrict the function implementation of these algorithms. If these algorithms have common functionality, you can turn the interface into an abstract class and put the common functionality into an abstract superclass.

  • If there are conditional statements in the algorithm implementation, it constitutes a multi-conditional statement. Policy mode can be used to avoid such multi-conditional statements.

  • Better scalability: It is very easy to extend policy implementations in policy patterns by adding a new policy implementation class and then using the new policy implementation where it is used.

policy-patterneddisadvantages:

  • The client must understand all the policies and be aware of their differences:

If it is up to the client to decide which algorithm to use, the client must know all the policies, their functions and differences in order to make the right choice, but this exposes the implementation of the policy.

  • Increased the number of objects:

Since the policy pattern encapsulates each specific algorithm as a separate policy class, the number of objects can be large if there are many alternative policies.

  • Only suitable for flat algorithm structure:

Since the policy implementation of the strategy pattern is equal (interchangeable), it actually forms a flat algorithm structure. That is, there are multiple equal policy implementations under a policy interface (multiple policy implementations are siblings), and only one algorithm can be used at runtime. This limits the level of use of the algorithm and cannot be nested.

In field

Then we modify the way of adopting policy mode for dynamic module (taking dynamic display [newly added XX project] as an example)

Common dynamic policy interface

/** * @className MessageStrategy * @description Dynamic policy interface * @author HeX * @date 2020/2/26 15:23 * @version 1.0 Public interface MessageStrategy {/** * new dynamic * @param messageModels * @return
     * @throws Exception
     */
    int batchSaveMessage(List<MessageModel> messageModels) throws Exception;
}
Copy the code

[Add XXX project] Policy implementation

/** * @className SaveProjectStrategy * @description Saves the new project dynamic policy * @author HeX * @date 2020/2/26 15:27 * @version 1.0 **/ public class SaveProjectMessageStrategy implements MessageStrategy { @Override public int BatchSaveMessage (MessageModel Message) throws Exception {// The new item messageEntity.setContent(userName +) is added MessageEnum.PROJECT_INSERT.getMessage() +"" + message.getName());
            messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
            messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
        return0; }}Copy the code

Dynamic policy context

/** * @className MessageContext * @description Dynamic context role * @author HeX * @date 2020/2/26 15:58 * @version 1.0 **/ public Class MessageContext {/** * hold a specific dynamic strategy */ private MessageStrategy MessageStrategy; @param messageStrategy */ public MessageContext(messageStrategy messageStrategy){this.messageStrategy = messageStrategy; } /** * a method for calling back specific dynamic policies * @param messageModels * @return
     * @throws Exception
     */
    public int saveMessage(List<MessageModel> messageModels) throws Exception {
        returnmessageStrategy.batchSaveMessage(messageModels); }}Copy the code

At this point, it only needs to be called in the specific business logic, as shown in the figure:

Well, that’s all for strategic patterns, and I’ll see you next time