美文网首页
策略模式的学习

策略模式的学习

作者: 西瓜雪梨桔子汁 | 来源:发表于2017-09-03 16:39 被阅读0次

1. 问题来源

在年初看某业务线清结算部分代码时,遇到这么一串代码,大致如下:


//如果商户文件到达,使用ftp下载文件
if (map.get(SystemConstant.SYSTEM_TYPE).equals("2")) {
    try{
            ... ...

        ApacheFTPUtil plugins=new ApacheFTPUtil();
        plugins.execute(paramMap);//下载文件路径信息在paramMap中

        businessLogger1.info(... + "FTP文件下载结束");
    }catch(Exception e){
        ... ...
    }
}
//如果文件下载到文件核心完成
if (result.getIsSuccess() == TaskResult.TASK_EXEC_SUCCESS) {
    businessLogger1.info(... + "文件到达检测启动");

    BaseComponentPlugins plugins = new FileArrive();//调用文件级实现
    result = new TaskResult();
    plugins.execute(map, result);

    businessLogger1.info(... + "文件到达检测结束");
    
    //文件级检测完成且没有问题,开始进行记录级检查
    if (result.getIsSuccess() == TaskResult.TASK_EXEC_SUCCESS) {
        businessLogger1.info(... + "文件校验启动");
        result = new TaskResult();

        plugins = new FileValid();
        plugins.execute(map, result);//调用记录级实现

        businessLogger1.info(... + "文件校验结束");

        //文件记录入库
        if (result.getIsSuccess() == TaskResult.TASK_EXEC_SUCCESS) {
            businessLogger1.info(... + "异步入库启动");
            result = new TaskResult();

            plugins = new FileEnterDB();
            plugins.execute(map, result);//调用异步入库实现

            businessLogger1.info(... + "异步入库结束");

            //入库HDFS
            if (result.getIsSuccess() == TaskResult.TASK_EXEC_SUCCESS) {
                businessLogger1.info(map, "入库HDFS开始");
                result = new TaskResult();
                
                plugins = new HdfsLoad();
                plugins.execute(map, result);//调用入库HDFS实现

                businessLogger1.info(map, "入库HDFS结束");
            }else{
                ... ...//入库HDFS异常
            }           
        }else{
            ... ...//文件记录入库异常
        }
    }else{
        ... ...//文件记录级入库/异步校验异常
    }
}else{
    ... ...//文件级校验异常
}

额外说明的是:

  • 由于清算步骤前后因果关系,必须前一步执行成功才能继续下一步,所以代码结构是这样多个if嵌套的样子
  • 哪一步失败就需要立即停止并反馈到日志或者数据库,各个else的代码也就是处理这些异常的,此处也没有列出。

代码中多次使用到了这样的结构:

BaseComponentPlugins plugins = new ... ...
plugins.execute(map, result);

可能熟悉策略模式的人早就明白这是怎么回事了,但是我刚开始看得时候就只是觉得,简洁明了、维护和可读性都很高,再就是感叹接口原来是这么用的。

2. 策略模式是什么?

最近在看资料的时候,偶然看到几篇介绍策略模式的文章,才慢慢理解了。回顾到这段代码的时候,也才渐渐明白这就是策略模式的实现。
策略模式的理解可以参见这篇文章,例子生动形象:【行为型模式十五】策略模式(Strategy)
我的理解就是把一个个具体的算法和使用算法的类是进行解耦,比如结算业务的例子被拆分成:

  • FTP下载文件
  • 文件级校验
  • 记录及校验
  • 异步入库
  • 入库HDFS

每种操作都单独形成一个单独的算法类,它们共同实现同一个接口、接收相同参数结构的参数,而具体使用哪一个由使用算法的类来选择。
接口定义如下:


package com.xxxx.commonbase.plugins;

import java.util.Map;

import com.xxxx.platform.bean.TaskResult;

public interface BaseComponentPlugins {
    abstract int execute(Map<String, String> map, TaskResult result);
}

再通过类图关系可以更清楚的看到算法实现与策略接口的关系:

预处理

上图的关系可以看到,这种处理方式很接近策略模式了,但是PretreatmentDeal类中并没有持有策略(也就是BaseComponentPlugins接口)的引用,为了完成结算它还是通过先调用FileArrive、FileValid... ... 这样一步步完成的,只是实例化这些对象使用BaseComponentPlugins引用而已,如之前所述:

BaseComponentPlugins plugins = new ... ...
plugins.execute(map, result);

而真正的策略模式,除了策略接口规范一系列具体的策略算法所应该完成的事;还需要上下文context类要持有策略接口,当调用这个上下文类的对象时,会决定使用哪种策略实现。
如果真的要以策略模式的实现,这个PretreatmentDeal类就必须先有一个策略的成员变量,修改后的类关系图应该如下:


使用策略模式的预处理

当然原本PretreatmentDeal类的各种if条件需要抽取形成新的调用类,由这个调用类来判断每一步的结果并决定是否更换策略,PretreatmentDealContext只是简单调用excetu()方法并返回结果。

3.业务线强相关的数据源切换

最近集中管控台需要管理使用多个数据库,数据库的选择只与业务线相关联.在最开始的时候我能想到的代码可能也就是类似下面的if判断形式了.


//依据产品和业务线取不同的数据库资源
if (paramMap.getProduct.equals(SystemConstant.PRODUCT_A)) {
    if (paramMap.getBussinessLine.equals(SystemConstant.BUSSINESS_LINE_A)) {
        ... ...//取A业务线对应的数据库
    }else if(paramMap.getBussinessLine.equals(SystemConstant.BUSSINESS_LINE_B)) {
        ... ...//取B业务线对应的数据库
    }else if(paramMap.getBussinessLine.equals(SystemConstant.BUSSINESS_LINE_C)) {
        ... ... //取C业务线对应的数据库
    }
}else if(paramMap.getProduct.equals(SystemConstant.PRODUCT_B) {
    if (paramMap.getBussinessLine.equals(SystemConstant.BUSSINESS_LINE_A)) {
        ... ...//取A业务线对应的数据库
    }else if(paramMap.getBussinessLine.equals(SystemConstant.BUSSINESS_LINE_B)) {
        ... ...//取B业务线对应的数据库
    }   
}

这样的写法缺点在策略模式的讲解文章已经谈了无数遍了,所以这个可以考虑修改下代码,抽象出数据源的获取方式/算法,新增一个contex类拿到获取数据源的接口引用。
这样,当service层代码获取数据源的时候,只需要依据业务现选择哪种策略/数据源就可以了,大致的思路如下:

使用策略模式的多数据源获取

剩下的工作就是service层依据controller层的参数,做出选择:

  • 依据产品product选择使用哪种策略(即哪种获取数据库资源的方式,比如oracle、mysql),
  • 依据具体业务现选择哪个数据源(比如同一个数ip下不同数据库用户名管理的表等等)
    这样,可以避免大量的判断条件。上下文类DataSourceContext代码写法较为固定,类似这样:

/**
* 数据源管理,完成向调用者返回匹配的数据库资源
*/
public class DataSourceContext {
  /**
   * 持有一个具体的策略对象(数据库资源接口)
   */
  private AccessDataSource AccessDataSource = null;
  
  /**
   * 构造方法,传入一个具体业务线数据库资源对象
   */
  public DataSourceContext(Strategy aStrategy){
      this.strategy = aStrategy;
  }  
  
  /**
   * 获取数据库资源
   * @param bussinessChannel业务线编码
   * @return 业务线关联的数据库资源
   */
  public double getDataSource(String bussinessChannel){
      return this.strategy.getDataSource(bussinessChannel);
  }
}

这么做就具备了很强的灵活性,比如配置不同类型数据库、同一个数据库多个数据库用户的管理;尤其是新增一个业务现,只需新增业务线

4.一点感受

凡事有利有弊,策略模式要求调用者必须清楚地知道每种策略实现,这样才能在选择的时候传递给context类具体的策略。比如这个数据源的例子调用类就必须的写成:


public class ProductAUserServiceImpl {
    
    //选择并创建需要使用的数据源,需要显示声明使用哪一种
    AccessDataSource strategy = new ProductADataSource();
    
    //创建上下文对象
    DataSourceContext context =  new DataSourceContext(strategy);
    
    /**
    * 依据业务线bussinessChannel拿到具体mybatis dao
    */
    public UserMapperDao UserMapperDao(String bussinessChannel){
        return context.strategy.getDataSource(bussinessChannel);
    }
    
    /**
    * 具体查询实现,供给controller层使用
    */
    public List<User> queryByName(String bussinessID, String name) {
        this.UserMapperDao(bussinessID).queryByName(name);
    }
    
}

也有说法可以调用者不需要知道具体哪些策略,完全有context类依据参数控制,没有在继续看,但感觉如果处理不好、context类又要出现一堆if判断,到时又得想办法处理掉这些判断。

相关文章

  • 第十三章学习策略的教学

    (一)通用学习策略的教学模式 (二)学科学习策略教学模式 (三)交又式学习策略教学模式 三、学习策略的训练 策略的...

  • 十三章 学习策略的教学

    学习策略的教学模式 (一)通用学习策略教学模式 (二)学科学习策略教学模式 专门传授语文或数学学科的学习方法与技...

  • Java设计模式之行为型模式

    策略模式【strategy】(接口主要) 【学习难度:★☆☆☆☆,使用频率:★★★★☆】 定义:策略模式定义了一系...

  • 二语习得:学习策略

    学习策略 O'Malley&Chamot根据信息处理模式将学习策略分为元认知策略(metacognitivestr...

  • 29 Java设计模式系列-策略模式

    策略模式 策略模式是非常常见的设计模式之一,写个笔记,记录一下我的学习过程和心得。 首先了解一些策略模式的定义。 ...

  • 设计模式(java)-观察者模式

    之前学习的是策略模式,复习一下之前的策略模式 策略模式一般在哪方面使用 “策略”百科中指[计策]。一般是指:1. ...

  • 设计模式之——策略模式

    前言:本文仅作为第一次学习设计模式的参考和笔记。初探策略模式: 策略模式:Strategy Pattern 又名:...

  • 策略模式

    简介 1.学习本篇博文,我们知道在什么场景下使用策略模式。2.策略模式的优缺点。3.策略模式的思想。 场景 某公司...

  • 018--IDEA王者荣耀学习设计模式

    1、写在前面 通过【王者荣耀】学习设计模式 2、核心操作 进行代码编写(策略模式+单例模式) 理解策略模式 3、具...

  • 11.7设计模式-策略模式-详解

    设计模式-策略模式 策略模式详解 策略模式在android中的实际运用 1.策略模式详解 2.策略模式在andro...

网友评论

      本文标题:策略模式的学习

      本文链接:https://www.haomeiwen.com/subject/odtjjxtx.html