preface

The policy pattern is one of the most commonly used design patterns, especially when eliminating if-else statements. It converts a set of behaviors into objects that are interchangeable within the original context object

The problem

One day, you create a guided tour program for tourists. The first version of the program supports planning driving routes, and motorists are very happy with it. As the program became popular, in the next version you added the ability to plan for walking. There are only two ways to do it and it’s really easy to write. But the next version added cycling planning, then bus route planning, and after a while you had to plan a route for all the sights in the city.

From a business point of view, your program becomes very successful as it grows in popularity. But from a technical point of view, each time a new route planning algorithm is added, the main class in the program doubles in size.

Eventually, at some point in the future, you won’t be able to maintain the program. Any change to an algorithm, whether it’s a simple BUG fix or tweaking the weighting of a street in the algorithm, affects the entire class. This increases the risk of introducing errors into otherwise normal code. Teamwork becomes inefficient, and no one wants to maintain a long piece of code. Even new members of the team complain that merging code conflicts takes too much time.

The solution

The strategy pattern suggests identifying classes responsible for different ways of doing a particular task and then extracting the algorithms in those classes into separate classes called policies.

The original class called context contains a member variable to store references to each policy. The primitive class does not execute the task, but delegates it to the connected policy object.

The context is not responsible for choosing which algorithm is appropriate for the task — the client passes the required policies to the context. In fact, the context is indifferent to the policy. It simply interacts with all policies through the same common interface. This generic interface only needs to expose a method to trigger the method encapsulated in the selected policy.

Therefore, context is independent of policy. This allows you to add new algorithms or modify existing algorithms without changing context code or other policies.

For the policy pattern structure shown below

structure

  1. Context maintains references to specific policies and communicates with that object only through the policy interface.
  2. The Strategy interface is a common interface for all specific policies and declares a context for methods to enforce the policy.
  3. Concrete Strategies implement various variations of the algorithms used in context.
  4. When the context needs to run the algorithm, it calls the execution method on its attached policy object. The context is unclear about the type of policy involved and how the algorithm is executed.
  5. The Client creates a specific policy object and passes it to the context. The context provides a setter for the client to replace the associated policy at run time.

Implementation steps

  1. Identify algorithms that change more frequently from context classes (primitive classes) (or complex conditional operators used to select an algorithm variant at run time)
  2. Declare a common policy interface for all variants of an algorithm
  3. The algorithm is extracted into each class to realize the common policy interface.
  4. Add a member variable to the context class to hold a reference to the policy object. Provides a setter to modify the member variable (policy). The upper and lower levels interact with policy objects only through policy interfaces.
  5. The client associates the context with the appropriate policy so that the context can do its job as expected.

Applicable scenario

When there are many similar classes that are only slightly different when performing certain behaviors

If the algorithm is not particularly important in the logic of the context, you want to isolate the context from the algorithm implementation details.

When complex conditional operators are used in a class to switch between different variants of the same algorithm.

An example of the policy pattern in the core Java library

  1. Java.util.Com parator#compare() is called from Collctions#sort()
  2. Javax.mail. Servlet. HTTP. HttpServlet# service () method
  3. javax.servlet.Filter#doFilter()

Actual combat scene

The strategy mode is especially suitable for e-commerce applications to realize various payment methods. Customers need to choose a payment method in a certain payment scenario: wechat Pay or AliPay.

At the same time, we take the mail sending of different mail service providers as an example:

To eliminate if-else statements, we can do this in Spring applications

Public interface MailStrategyService {/** * service provider name ** @return */ public String getServiceProviderName(); /** * @param message */ public void send(String message); }Copy the code
@Service @Slf4j public class QQMailStrategyServiceImpl implements MailStrategyService { @Override public String getServiceProviderName() { return "qq"; } @override public void send(String message) {log.info(" send message :{}",message); }}Copy the code

The core needs to learn:

@component @slf4j @getter Public Class MailStrategyContext {/** * policy * KEY for business code * VALUE for concrete implementation class */ private final ConcurrentHashMap<String, MailStrategyService> strategy = new ConcurrentHashMap<>(); /** * inject all classes that implement the MailStrategyService interface ** @param mailServiceList */ public MailStrategyContext(List<MailStrategyService> mailServiceList) {log.info(" Start injection policy "); Mailservicelist.foreach (mailServiceImpl -> {log.info(" Current policy class :{}", mailServiceImp.getClass ().getName())); strategy.put(mailServiceImpl.getServiceProviderName(), mailServiceImpl); }); Log.info (" Injection policy completed "); } /** * @param strategyName Policy name * @param message To send message */ public void send(String strategyName, String message) { MailStrategyService mailStrategyService = strategy.get(strategyName); mailStrategyService.send(message); }}Copy the code
@RestController @RequestMapping("/mail") public class StrategyController { @Autowired private MailStrategyContext mailStrategy; @PostMapping("/{bussinessCode}/send") public void sendMail(@PathVariable(value = "bussinessCode") String bussinessCode, @RequestParam(value = "message") String message) { mailStrategy.send(bussinessCode, message); }}Copy the code

Strongly recommend an online design mode of learning website: refactoringguru. Cn/design – patt…