zoukankan      html  css  js  c++  java
  • 一次代码优化的实践(模板方法+策略+工厂方法模式)

    前言

    好久没分享工作总结啦,今天来一份代码优化总结。用模板方法+策略+工厂方法模式优化了代码,耐心点看完,应该对大家有帮助的~

    本文已经收录到github

    https://github.com/whx123/JavaHome

    公众号:捡田螺的小男孩

    优化代码前

    先来了解一下类似的业务场景,简言之,就是:多个商户接入我们系统,都是走一个类似的流程通过http请求出去的。

    优化前,每个公司对应一个句柄服务,伪代码如下:

    // 商户A处理句柄
    CompanyAHandler implements RequestHandler {
       Resp hander(req){
       //查询商户信息
       queryMerchantInfo();
       //加签
       signature();
       // http请求(走代理)
       httpRequestbyProxy()
       // 验签
       verify();
       }
    }
    // 商户B处理句柄
    CompanyBHandler implements RequestHandler {
       Resp hander(Rreq){
       //查询商户信息
       queryMerchantInfo();
       //加签
       signature();
       // http请求(不走代理)
       httpRequestbyDirect();
       // 验签
       verify(); 
       }
    }
    // 商户C处理句柄
    CompanyBHandler implements RequestHandler {
       Resp hander(Rreq){
       //查询商户信息
       queryMerchantInfo();
       // webservice 方式调用
       requestByWebservice();
       }
    }
    

    优化代码思路

    我的优化代码思路,是有重复代码,先把它抽出来,或者公用变量,或者公用方法,伸着公用类。所以呢,查询商户信息呀,加签呀,http请求呀先全部各抽成一个公用方法。你细心点会发现,连每个Handler处理过程都很类似的,大概都是查询商户信息+加签+http请求+验签,于是呢,可以直接把它们抽象成一个公用类呀~在这里就要引入模板方法模式咯

    模板方法模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
    这种类型的设计模式属于行为型模式。
    

    既然每个Handler处理,都是类似的流程,那定义一个抽象类,把查询商户信息,加签,http请求,验签什么的,都放到里面去,俨然一个模板一样。然后,因为有些商户走http代理,有些又没走代理,怎么办呢? 定义一个抽象方法,给子类实现嘛,因为能共用就放到父类(当前的抽象类),不能共用就放到子类嘛~代码如下:

    abstract class AbstractCompanyCommonService implements ICompanyCommonService { 
         //模板方法
         Resp handlerTempPlate(req){
                //查询商户信息
               queryMerchantInfo();
               // 加签
               signature();
               //http 请求
               if(isRequestByProxy()){
                  httpProxy();
                }else{
                  httpDirect();
                 }
                // 验签
                verifySinature();
         }
          // Http是否走代理
          abstract boolean isRequestByProxy();
    }
    

    子类商户A实现:

    CompanyAServiceImpl extends AbstractCompanyCommonService{
        Resp hander(req){
          return handlerTempPlate(req);
        }
        //公司A是走代理的
        boolean isRequestByProxy(){
           return true;
        }
    

    子类商户B实现:

    CompanyBServiceImpl extends AbstractCompanyCommonService{
        Resp hander(req){
          return handlerTempPlate(req);
        }
        //公司B是不走代理的
        boolean isRequestByProxy(){
           return false;
        }
    

    策略模式

    心细的读者会发现,甚至提出疑问,你的商户C的服务实现跟你定义的公用模板,不太一样呢,那当然,实际开发中,不跟你定义的模板一样太常见了,需求是产品提的嘛,又不是根据你模板提的,是代码服务于需求的。好了,不多说啦,我使用了策略模式,来优化这个问题。

    在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
    

    策略模式理解起来其好抽象对不对?我个人理解,其实策略模式就是定义一个方法(所谓算法),给子类自己去实现。实际上就是定义个方法/接口,让子类自己去实现。看代码吧:

    // 定义一个方法,把策略交给子类去实现。
    interface ICompanyCommonService{
      Resp hander(req);
    }
    

    前面商户A和商户B还是不变,使用抽象类AbstractCompanyCommonService的模板,模板不满足商户C,商户C只能自己去实现咯,各个子类自己去实现的行为,就是策略模式的体现呢,如下:

    CompanyCServiceImpl extends AbstractCompanyCommonService{
        Res hander(req){
           //查询商户信息
           queryMerchantInfo();
           requestByWebservice();    
        }
        //随意了,你都不走模板了
        boolean isRequestByProxy(){
           return false;
        }
    

    工厂方法模式

    商户A、B、C服务怎么被管理呢,之前分别给A,B,C服务实现handler的,现在好了,都不知道怎么管理了,怎么知道调用哪个呢?别慌,解决方案是工厂方法模式

    在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
    

    工厂方法模式具体实现就是:接口定义一个枚举,每个服务实现都重新实现枚举,设置唯一标志枚举,再交给spring容器管理。看代码咯:

    interface ICompanyCommonService{
      Resp hander(req);
      CompanyEnum getCompanyEnum();
    }
    
    CompanyAServiceImpl extends AbstractCompanyCommonService{
        Resp hander(req){
          return handlerTempPlate(req);
        }
        //公司A是走代理的
        boolean isRequestByProxy(){
           return true;
        }
        CompanyEnum getCompanyEnum(){
         return CompanyEnum.A;
        } 
        
    CompanyBServiceImpl extends AbstractCompanyCommonService{
        Resp hander(req){
          return handlerTempPlate(req);
        }
        //公司B是不走代理的
        boolean isRequestByProxy(){
           return false;
        }
        CompanyEnum getCompanyEnum(){
         return CompanyEnum.B;
        } 
    

    来来来,工厂方法模式出炉咯:

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

    最后建议

    最后,不要为了使用设计模式生搬硬套,而是优化代码过程中,发现这个设计模式刚好适用,才去用的哈。附上最后的代码咯:

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

    个人公众号

  • 相关阅读:
    【转】常见经济类名词解释
    Linux parted命令详解
    【转】Linux下从TCP状态机,三次握手判断DDOS攻击
    【转】Java学习---HashMap和HashSet的内部工作机制
    【转】Redis学习---阿里云Redis多线程性能增强版详解
    改变自己,改变世界
    对话任正非两万字实录:最重要的是要沉着
    qt手写输入法资料
    Qt框架及模块认识
    哲学必读10本经典著作
  • 原文地址:https://www.cnblogs.com/jay-huaxiao/p/13716072.html
Copyright © 2011-2022 走看看