7

Strategy mode structure diagram

The strategy mode is mainly composed of the above three identities. Here we will not have too much basic knowledge of the timely strategy mode. By default, everyone already has a basic understanding of the strategy mode.

Business needs

There is a requirement for the reporting of advertising click data burying points. The reported burying point data is differentiated and reported according to the clicked advertising position, and the data of each advertising position is stored in a table. (Eg: Here, you don’t have to go deep into why you want to do this for sub-table storage, we only talk about the practical application of the strategy mode)

Code

Since it is a practical case, we are based on the SpringBoot framework, and we mainly need to use some features of Spring, so everyone should pay attention.

Step 1: Define the strategy class

First, we define an interface for reporting

public interface AdvertisingDataReported {

    String advertisingDataReported(Object param);
}

Step 2: Define a specific strategy implementation class

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {
    
    @Override
    public String advertisingDataReported(Object param) {
          // 具体的业务逻辑略
        return null;
    }
}

The third step: strategy control class

Since the strategy model has many specific strategies to achieve, which strategy to use needs to be judged based on our input parameters, that is, the type of advertising in our business, then how do we elegantly judge?

Let's take a look at this way

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

  if (advertisingType.equals("1")) {
    // 执行策略A
  } else if (advertisingType.equals("2")) {
    // 执行策略2
  }
}

There are many people who have written this way, and we will not discuss these issues here. Let's first take a look at what are the problems with this writing?

:

1. 违反开闭原则,每次增加新的策略实现类,都要加一个if判断;
 2. 随着策略实现类的增加,代码变的臃肿,越来越难以维护;



Based on this situation, can we initialize all the strategy implementation classes when the project starts and store them in the Map, with the advertising type as the key and the implementation class as the value, we look at 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 {
          // 返回该接口所有的实现类
        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) {
        return STRATEGY_MAP.get(clazz.getName());
    }
}

Our strategy control class implements ApplicationContextAware, you can understand this class, it can get the context of ApplicationContext, because we are talking about this case in SpringBoot, our strategy class implementation class is annotated with @Service and injected into the Spring container , So we can get all the implementation classes of the strategy class directly from the container.

After obtaining all the strategy implementation classes, we used the class path as the key and the class implementation as the value stored in the map. At this point, I thought it was done.

you think there are any problems?

How do we know which specific strategy class needs to be taken for this entry? It is also necessary to define a separate class to to map the advertising type and strategy class . Isn’t this the same logic as the judgment? Still have to maintain this mapping relationship.

Retrofit

If you don’t want to define a separate class to map the advertisement type and the strategy class one by one, can we solve it in the strategy class? Each strategy class implementation class knows which type it is dealing with, so that we can put it in the map The value of the Key class path is replaced with the advertisement type, so that you can get directly from the Map according to the advertisement type entered by the reporting interface.

There are two specific implementations. You can customize annotations, distinguish them by adding annotations, or use methods, so we directly use methods for processing here.

after 16183c8a084996 transformation:

Strategy:

public interface AdvertisingDataReported {

      // 新增方法
    AdvertisingTypeEnum advertisingType();

    String advertisingDataReported(Object param);
}

Strategy implementation class:

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {

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

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

Policy control category:

@Component
public class StrategyFactory implements ApplicationContextAware {

      // Map的Key改为广告类型枚举类
    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));
    }

      // 根据广告类型获取相应的策略类
    public <T extends AdvertisingDataReported> AdvertisingDataReported getInstance(AdvertisingTypeEnum advertisingTypeEnum) {
        return STRATEGY_MAP.get(advertisingTypeEnum);
    }
}

Advertising enumeration class:

public enum AdvertisingTypeEnum {

    BOTTOM, TOP;

    private String advertisingType;

    AdvertisingTypeEnum() {}
  
      // set get省略
}

Specific use of strategy

@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";
    }
}

A little summary:

At this point, our strategy mode case is over. I have a few questions. I don’t know if you have any doubts. Why should I use Object as the input parameter of the method? In our case, it seems that the input parameters of each strategy class seem to be all. It is the same, but it is also possible that the implementation class of the same strategy may appear, but the input parameters may be completely different. Then at this time, we can convert within the method by passing in Object. Of course, if you think so, The strategy method is too rigid, so you can also add generics to the method, and the specific conversion type is converted by the caller passing in the generic type.

After such a transformation, the two problems we encountered just now are not problems. We want to add a new strategy implementation class. We only need to implement the defined strategy class without adding any additional code.

Read the original text


一个程序员的成长
308 声望7.3k 粉丝

日拱一卒,功不唐捐