One, foreword

In the last chapter, we studied the application of factory model. In this chapter, we will continue to study the application of strategic model in actual projects.

First of all, before talking about the strategic model, let’s take a look at the membership strategies of large supermarkets in daily life: malls often make different commodity quotation strategies according to different customers, such as 9 discount for ordinary members, 20 discount for VIP members, and 50 discount for super members…

Now we are going to make a quotation management module, the brief point is to provide different discount quotation for different member customers.

Our typical code might look something like this:

package com.MyMineBug.demoRun.strategy;

import java.math.BigDecimal;

public class MemberManagement {

	public BigDecimal calculatePrice(String customType){
        if ("Regular Member User".equals(customType)) {
            System.out.println("I'm sorry! Regular members get 10% off!");
            return new BigDecimal("90");
        }else if ("VIP".equals(customType)) {
            System.out.println("Congratulations! 20% off for VIP members!");
            return new BigDecimal("80");
        }else if("Super Member User".equals(customType)){
            System.out.println("Congratulations! 50% off for super members!");
            return new BigDecimal("50");
        }
        // Ordinary users are the original price
        return new BigDecimal("100"); }}Copy the code

After testing, the above code works very well, but the above code is problematic. The problem above: put all the algorithms of different customers’ quotation in the same method, which makes the method very large (it’s just a demo, so it doesn’t look bloated yet).

If we continue to optimize it later, we may extract each member as a separate algorithm, but this will violate our open and closed principle.

1. Be 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.

2. 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.

Is there any way to make our quotation management can be expanded, maintainable, and can easily respond to changes? There is a solution, of course, and that is the strategic pattern that we will talk about next.

Second, a preliminary understanding of the strategic mode

2.1 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.

2.2 structure

1. Policy interface role IStrategy: Restrains a series of specific policy algorithms. StrategyContext uses this policy interface to invoke the algorithm implemented by the specific policy.

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.

2.3 Use strategy mode to rewrite the quotation management module

Here, we inject the implementation class of our strategy into map through spring annotation. When the project is started, the implementation class of IStrategy will be automatically injected into StrategyContext. The specific implementation is as follows:

Common Quotation Strategy Interface:

package com.MyMineBug.demoRun.strategy;

import java.math.BigDecimal;

public interface IStrategy  {

	/** * calculate the price *@return* /
	public BigDecimal calculatePrice(a);
}

Copy the code

Ordinary member user quotation strategy implementation:

package com.MyMineBug.demoRun.strategy;

import java.math.BigDecimal;

import org.springframework.stereotype.Component;
@Component("GeneralMember")
public class GeneralMember implements IStrategy{

	@Override
	public BigDecimal calculatePrice(a) {
		return new BigDecimal("90"); }}Copy the code

VIP member user strategy implementation:

package com.MyMineBug.demoRun.strategy;

import java.math.BigDecimal;

import org.springframework.stereotype.Component;

@Component("VipMember")
public class VipMember implements IStrategy{

	@Override
	public BigDecimal calculatePrice(a) {
		return new BigDecimal("80"); }}Copy the code

Super member user strategy implementation:

package com.MyMineBug.demoRun.strategy;

import java.math.BigDecimal;

import org.springframework.stereotype.Component;

@Component("SuperMember")
public class SuperMember implements IStrategy{

	@Override
	public BigDecimal calculatePrice(a) {
		return new BigDecimal("50"); }}Copy the code

Strategic quotation context:

package com.MyMineBug.demoRun.strategy;
/** * Policy manager *@author18360 * * /

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
public class StrategyContext {
	
	private final Map<String, IStrategy> strategyMap = new ConcurrentHashMap<String, IStrategy>();
	
	/** * Inject all beans that implement the IStrategy interface *@param strategyMap
	 */
	@Autowired
	public void StrategyInterface(Map<String, IStrategy> strategyMap) {
		this.strategyMap.clear();
		strategyMap.forEach((k, v)-> this.strategyMap.put(k, v));
	}
	
    /** * calculate the price *@paramMemberLevel memberLevel *@returnPrice * /
    public BigDecimal calculatePrice(String memberLevel) {
    	if(! StringUtils.isEmpty(memberLevel)){return strategyMap.get(memberLevel).calculatePrice();
    	}
		return null; }}Copy the code

Since it is a Spring Boot project, we can simulate the quotation of different external member users in the Controller layer, with the code as follows:

package com.MyMineBug.demoRun.controller;

import java.math.BigDecimal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.MyMineBug.demoRun.strategy.StrategyContext;

@RestController
@RequestMapping
public class StrategyController {

	@Autowired
	private StrategyContext strategyContext;

	@RequestMapping("/calculatePrice")
	public BigDecimal calculatePrice(@RequestParam("memberLevel") String memberLevel) {
		return strategyContext.calculatePrice(memberLevel);
	}
	
	@RequestMapping("/whello")
	public String hello(a) {
		return "hello run"; }}Copy the code

When you start the Spring Boot project with an error, add a scan annotation to the boot entry: @componentScan (basePackages = {” com.myMinebug.demorun “})

Start the service, enter http://localhost:8080/calculatePrice? in interface memberLevel=GeneralMember

Output:

Third, in-depth understanding of strategic patterns

3.1 Role of policy Mode

It is to separate the implementation of a specific algorithm from the business logic into a series of independent algorithm classes, so that they can be replaced with each other.

3.2 Emphasis of the policy pattern

It doesn’t matter how algorithms are implemented, but how they are organized and invoked to make our program structures more flexible and extensible.

In our first example of quote management, we found that each implementation of the policy algorithm corresponds to the if else statement in the calculatePrice method in MemberManagement. We know that the code in the if else if statement is equal in probability of execution. You can either do if, or else, or else if. The strategy pattern is to abstract and encapsulate each equal implementation into an independent algorithm class, and then interact with the specific algorithm class through the context. Each strategy algorithm is equal and has the same status. It is because of the equality of each algorithm that they can be replaced with each other. Although we can switch between policies dynamically, only one policy can be used at a time.

Application of policy mode in JDK

In multithreaded programming, we often use thread pools to manage threads to reduce the waste of resources caused by frequent creation and destruction of threads. When creating thread pools, Often use a factory class to create thread pool Executors, but actually use the ThreadPoolExecutor class inside Executors. It has a final constructor as follows:

CorePoolSize: The number of core threads in the thread pool that are not destroyed even if they have no work to do.

MaximumPoolSize: Maximum number of threads that can be created in the thread pool.

KeepAliveTime: The maximum amount of time that extra threads wait for a new task when the number of threads in the thread pool is greater than corePoolSize.

Unit: keepAliveTime Time unit.

WorkQueue: A queue of tasks that are stored in the workQueue before a thread in the thread pool has completed a task. (While all threads in the thread pool have completed a task, the task is still submitted and stored in the workQueue.)

ThreadFactory: A factory for creating threads in the thread pool.

Handler: The processing strategy for tasks that are still being submitted to the thread pool when there are no additional threads in the thread pool to execute tasks and the columns holding tasks are full (referring to bounded queues).

RejectedExecutionHandler is a policy interface for handling tasks that are still being submitted to the thread pool when there are no more threads in the pool to execute the task and the number of columns holding the task is full (referring to bounded queues).

Common Policy Interface:

This policy interface has four implementation classes:

AbortPolicy: the task of this strategy is submitted directly to the abandoned, and throw RejectedExecutionException anomalies.

DiscardPolicy: This policy also discards the task (don’t ask, don’t do anything about the submitted task), but doesn’t throw an exception.

DiscardOldestPolicy: Removes the first task from the workQueue when the actuator is not closed and discards the first task so that there is space to store the newly committed task. Use this strategy with extreme caution, as it directly abandons the previous task.

CallerRunsPolicy: This policy does not discard any task, since there are no extra threads in the thread pool to assign the task, this policy executes the task directly in the current thread (the caller thread).

The ThreadPoolExecutor class holds a reference to the RejectedExecutionHandler interface so that specific policies can be specified and injected by external clients themselves in the constructor. Take a look at the class diagram below:

Five, the summary

Advantages of the strategic pattern:

1. The function of policy mode is to define a series of algorithms through abstraction and encapsulation, so that these algorithms can be replaced with each other. Therefore, 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.

2. A series of algorithms in the strategy mode can be replaced with each other and are equal, which is the if-else organization structure written together. If there are conditional statements in the algorithm implementation, they constitute multiple conditional statements, which can be avoided by the strategy mode.

3. Better scalability: It is very easy to extend the policy implementation in the policy pattern, just add a new policy implementation class, and use the new policy implementation where it is used.

Disadvantages of the strategic pattern:

1. The client must know all the policies and be aware of their differences: if the client decides which algorithm to use, the client must know all the policies and the functions and differences of each policy in order to make the right choice, but this exposes the implementation of the policy.

2. Increased number of objects: Since the policy pattern encapsulates each specific algorithm as a separate policy class, the number of objects will be large if there are many policies to choose from.

3. Only suitable for partial flat algorithm structure: Since the implementation of each policy in the strategy mode is equal (interchangeable), it actually constitutes 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.

The essence of the strategy pattern: separate the algorithm and select the implementation.

If you like it, please give it a thumbs up!!

Share Technology And Love Life