Policy pattern structure diagram

The strategic pattern is mainly composed of the above three identities. Here we will not do more than the basic knowledge of the strategic pattern. By default, we have already had a basic understanding of the strategic pattern.

The business requirements

There is a demand for advertising click data buried point reporting, and the buried point data reported is differentiated according to the different advertising position, and the data of each advertising position is stored in separate tables. (Eg: We don’t have to go into the details of why we do this, we’ll just talk about the practical application of the policy pattern.)

Code implementation

Because it is the actual case, so we are based on the SpringBoot framework, mainly to use some of the Spring functions, so we should pay attention to.

Step 1: Define the policy class

First we define an interface for reporting

public interface AdvertisingDataReported {

    String advertisingDataReported(Object param);
}
Copy the code

Step 2: Define concrete policy implementation classes

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {
    
    @Override
    public String advertisingDataReported(Object param) {
      	// The specific business logic is omitted
        return null; }}Copy the code

Step 3: Policy control class

Since there are many specific strategies to implement the strategy mode, which strategy to use needs to be judged according to our input, that is, the type of advertising in our business, so how should we make an elegant judgment?

So let’s look at it this way

public static void main(String[] args) {
        
  String advertisingType = "1";

  if (advertisingType.equals("1")) {
    // Execute policy A
  } else if (advertisingType.equals("2")) {
    // Execute policy 2}}Copy the code

There are plenty of people who have written that way, and we’re not going to talk about that here. So let’s see what the problems are with writing this way?

Existing problems:

1. Violates the open and close principle, adding an if judgment every time a new policy implementation class is added; 2. With the increase of policy implementation classes, the code becomes bloated and difficult to maintain;Copy the code

Based on this situation, can we initialize all policy implementation classes and store them in Map when the project is started, with advertising type as key and implementation class as Value? Let’s see the following code:

@Component
public class StrategyFactory implements ApplicationContextAware {

    private final Map<String, AdvertisingDataReported> STRATEGY_MAP = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      	// Return all implementation classes of this interface
        Map<String, AdvertisingDataReported> tempMap = applicationContext.getBeansOfType(AdvertisingDataReported.class);
        tempMap.values().forEach(strategyService -> STRATEGY_MAP.put(strategyService.getClass().getName(), strategyService));
    }

    public <T extends AdvertisingDataReported> AdvertisingDataReported getInstance(Class<T> clazz) {
        returnSTRATEGY_MAP.get(clazz.getName()); }}Copy the code

Our policy control class implements ApplicationContextAware, which you can think of as getting the context of ApplicationContext, and since we’re talking about SpringBoot, Our policy implementation classes are annotated with the @service annotation into the Spring container, so we can get all of the policy implementation classes directly from the container.

After we got all the policy implementation classes, we stored the classpath as the key and the class implementation as the value in the map, and at that point I thought we were done.

What other problems do you think exist?

How do we know which specific policy class this entry needs to go to? You also need to define a separate class to map the advertising type to the policy class. Isn’t that the same logic as judging? You have to maintain this mapping all the time.

transform

If you don’t want to separate the definition of a class to one-to-one mapping of advertising type and strategy, then we can to solve in the policy class, each policy implementation class know what kind of type to deal with it, so we can put the Key in the map classpath value replacement for advertising type, so you can according to the report interface into the type of advertising, Get directly from the Map.

There are two ways to implement this, you can customize annotations by annotating them, or you can use methods, so we’ll just use methods here.

The code after transformation:

Strategy:

public interface AdvertisingDataReported {

  	// Add a method
    AdvertisingTypeEnum advertisingType(a);

    String advertisingDataReported(Object param);
}
Copy the code

Policy implementation class:

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {

    @Override
    public AdvertisingTypeEnum advertisingType(a) {
        return AdvertisingTypeEnum.BOTTOM;
    }

    @Override
    public String advertisingDataReported(Object param) {
        return null; }}Copy the code

Policy control class:

@Component
public class StrategyFactory implements ApplicationContextAware {

  	// Map's Key is changed to the AD type enumeration class
    private final Map<AdvertisingTypeEnum, AdvertisingDataReported> STRATEGY_MAP = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, AdvertisingDataReported> tempMap = applicationContext.getBeansOfType(AdvertisingDataReported.class);
        tempMap.values().forEach(strategyService -> STRATEGY_MAP.put(strategyService.advertisingType(), strategyService));
    }

  	// Get the corresponding policy class based on the type of advertisement
    public <T extends AdvertisingDataReported> AdvertisingDataReported getInstance(AdvertisingTypeEnum advertisingTypeEnum) {
        returnSTRATEGY_MAP.get(advertisingTypeEnum); }}Copy the code

Advertising enumeration class:

public enum AdvertisingTypeEnum {

    BOTTOM, TOP;

    private String advertisingType;

    AdvertisingTypeEnum() {}
  
  	// set get ellipsis
}
Copy the code

Specific use of policy classes

@RestController
public class AdvertisingDataReportedController {

    @Resource
    private StrategyFactory strategyFactory;

    @RequestMapping(value = "/reported/data", method = RequestMethod.POST)
    public String reportedData(AdvertisingTypeEnum advertisingTypeEnum, Object obj) {

        AdvertisingDataReported dataReported = strategyFactory.getInstance(advertisingTypeEnum);

        String result = dataReported.advertisingDataReported(obj);

        return "SUCCESS"; }}Copy the code

Summary:

Here the case even if the end of our strategy pattern, there are a few problems don’t know if you have doubts, why do I want to use the Object as a method of the parameter, we have this kind of case, seem each policy type into as if is the same, but is also likely to be the same strategy implementation class, but may not be completely the same, so this time, You can convert a method by passing in an Object. Of course, if this is too rigid for the policy method, you can also convert a method by passing in a generic.

After this transformation, the two problems we just encountered are no longer a problem, and we want to create a new policy implementation class that simply implements the defined policy class without adding any additional code.