Practice of primary code optimization (template method + strategy + factory method mode)

preface

I haven't shared the work summary for a long time. Today, I'd like to make a code optimization summary. Using template method + strategy + factory method mode to optimize the code, read it patiently, which should be helpful to you~

This article has been included in github

https://github.com/whx123/JavaHome

Official account: little boy picking up snails

Before optimizing code

Let's take a look at similar business scenarios. In short, multiple merchants access our system through a similar process and request through http.

Before optimization, each company corresponds to a handle service, and the pseudo code is as follows:

// Merchant A processing handle
CompanyAHandler implements RequestHandler {
   Resp hander(req){
   //Query merchant information
   queryMerchantInfo();
   //Countersign
   signature();
   // http request (proxy)
   httpRequestbyProxy()
   // Signature verification
   verify();
   }
}
// Merchant B processing handle
CompanyBHandler implements RequestHandler {
   Resp hander(Rreq){
   //Query merchant information
   queryMerchantInfo();
   //Countersign
   signature();
   // http request (no proxy)
   httpRequestbyDirect();
   // Signature verification
   verify(); 
   }
}
// Merchant C processing handle
CompanyBHandler implements RequestHandler {
   Resp hander(Rreq){
   //Query merchant information
   queryMerchantInfo();
   // webservice mode call
   requestByWebservice();
   }
}

Optimize code ideas

My idea of optimizing code is to extract duplicate code first, or public variables or public methods, and extend public classes. Therefore, query the merchant information, add the signature and http request, and draw them into a public method first. If you are more careful, you will find that the processing process of each Handler is very similar. It is probably to query merchant information + add signature + http request + check signature. Therefore, you can directly Abstract them into a public class ~ here we need to introduce the template method mode

Template method mode

In template mode( Template Pattern)In, an abstract class publicly defines how its methods are executed/Template. Its subclasses can override the method implementation as needed, but the call will be made in the way defined in the abstract class.
This type of design pattern belongs to behavioral pattern.

Since each Handler process is a similar process, define an abstract class to put the query of merchant information, signature addition, http request and signature verification into it, just like a template. Then, because some merchants use http proxy and some don't, what should we do? Define an abstract method and implement it for subclasses. If it can be shared, it will be placed in the parent class (the current abstract class). If it cannot be shared, it will be placed in the subclass. The code is as follows:

abstract class AbstractCompanyCommonService implements ICompanyCommonService { 
     //Template method
     Resp handlerTempPlate(req){
            //Query merchant information
           queryMerchantInfo();
           // Countersign
           signature();
           //http request
           if(isRequestByProxy()){
              httpProxy();
            }else{
              httpDirect();
             }
            // Signature verification
            verifySinature();
     }
      // Whether Http acts as an agent
      abstract boolean isRequestByProxy();
}

Subcategory merchant A realizes:

CompanyAServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //Company A is an agent
    boolean isRequestByProxy(){
       return true;
    }

Subcategory merchant B realizes:

CompanyBServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //Company B does not act as an agent
    boolean isRequestByProxy(){
       return false;
    }

Strategy mode

Careful readers will find, or even ask questions, that the service implementation of your merchant C is different from the public template you defined. Of course, in actual development, it is not as common as the template you defined. The requirements are provided by the product, not according to your template, but by the code. Well, not to mention, I used the strategy mode to optimize this problem.

In policy mode( Strategy Pattern)The behavior of a class or its algorithm can be changed at run time. This type of design pattern belongs to behavioral pattern.

The strategy pattern is good at abstraction, isn't it? I personally understand that the policy pattern is to define a method (so-called algorithm) and implement it for subclasses. In fact, it is to define a method / interface and let the subclass implement it by itself. Look at the code:

// Define a method and give the policy to subclasses to implement.
interface ICompanyCommonService{
  Resp hander(req);
}

Merchant A and merchant B remain the same. The template of the abstract class AbstractCompanyCommonService is used. The template does not meet merchant C, and merchant C can only implement it by itself. The behavior of each subclass to implement it by itself is the embodiment of the policy mode, as follows:

CompanyCServiceImpl extends AbstractCompanyCommonService{
    Res hander(req){
       //Query merchant information
       queryMerchantInfo();
       requestByWebservice();    
    }
    //Whatever. You're not going to take the template
    boolean isRequestByProxy(){
       return false;
    }

Factory method model

How can merchant A, B and C services be managed? Previously, they implemented handler for A, B and C services respectively. Now, I don't know how to manage them. How can I know which one to call? Don't panic, the solution is the factory method model.

In factory mode, when creating objects, we do not expose the creation logic to the client, and point to the newly created objects by using a common interface.

The specific implementation of the factory method mode is to define an enumeration for the interface, re implement the enumeration for each service implementation, set the unique flag enumeration, and then hand it over to the spring container for management. Look at the code:

interface ICompanyCommonService{
  Resp hander(req);
  CompanyEnum getCompanyEnum();
}

CompanyAServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //Company A is an agent
    boolean isRequestByProxy(){
       return true;
    }
    CompanyEnum getCompanyEnum(){
     return CompanyEnum.A;
    } 
    
CompanyBServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //Company B does not act as an agent
    boolean isRequestByProxy(){
       return false;
    }
    CompanyEnum getCompanyEnum(){
     return CompanyEnum.B;
    } 

Here comes the factory method mode:

@Component
public class CompanyServiceFactory implements ApplicationContextAware {

    private static Map<CompanyEnum, ICompanyCommonService> map = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, ICompanyCommonService> tempMap = applicationContext.getBeansOfType(ICompanyCommonService.class);
        tempMap.values().forEach(iCompanyCommonService ->
                map.put(iCompanyCommonService.getCompanyEnum(), iCompanyCommonService));
    }

    public Resp handler(req) {
        return map.get(CompanyEnum.getCompanyEnum(req.getCompanyFlag()).hander(req);
    }
}

Final recommendations

Finally, don't use design patterns by rote, but use them when you find that the design pattern is just applicable in the process of optimizing the code. Attach the final code:

@Service
public class CompanyHandler implements RequestHandler  {
   @Autowire
   private CompanyServiceFactory companyServiceFactory;
   
   Resp hander(req){
    return companyServiceFactory.handler(req);
  }
}

Personal official account

Posted by sri.sjc on Sun, 15 May 2022 11:45:44 +0300