Java设计模式——策略

前言

策略模式是平时Java开发中常用的一种,虽然已有很多讲解设计模式的文章,但是这里还是写篇文章来从自己理解的角度讲解一下。

使用场景

我们不妨进行场景假设,要对我们的软件进行授权管理:在启动我们的软件之前先要校验是否存在合法的授权,如果授权不合法则要求用户进行激活操作。作为例子,我们就简单地实现一下授权校验功能:分发的授权文件中内容是一个四位随机数,并且最后一位是数字且为0。我们只要校验授权文件中内容的最后一位是数字0即可。

java 复制代码
public class LicenseService {
    public boolean checkLicense() {
        boolean result = false;
        // abc0
        File file = Path.of("./secret").toFile();
        String content = "";
        try{
            // 读取文件内容
            BufferedReader br = new BufferedReader(new FileReader(file));
            content = br.readLine();
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
		// 末尾字符是0即认为校验通过
        if (content.endsWith("0")) {
            result = true;
        }

        return result;
    }
}

需求变更

现在需求进行了变更,不再校验末尾字符为0了,而是校验开头字符是0,因此我们需要对程序进行修改。并且,我们在调整程序的过程中将读取文件内容和授权校验的逻辑进行分离,将授权校验的逻辑抽到一个单独的方法中。

java 复制代码
public boolean checkLicense() {
        ...

        result = checkInternal(content, result);

        ...
    }

    private static boolean checkInternal(String content, boolean result) {
        if (content.startsWith("0")) {
            result = true;
        }
        return result;
    }

改完之后又接到了最新通知,还有可能改回原来末尾字符的判断方式,于是我们又对方法进行了调整。通过方法传入一个参数来决定使用哪种方式判断:

java 复制代码
public boolean checkLicense() {
        ...

        result = checkInternal(content, result, 1);

        ...
    }

    private static boolean checkInternal(String content, boolean result, int choose) {
    	// 通过方法传入的choose来决定使用哪种算法
        if (choose == 0) {
            if (content.endsWith("0")) {
                result = true;
            }
        } else if (choose == 1) {
            if (content.startsWith("0")) {
                result = true;
            }
        }
        return result;
    }

策略模式

上面我们的例子是比较简单的,但是达到了演示的效果:校验授权 的实现可能有多个版本,并且不同版本的实现都有可能被使用。为了后续方便扩展和维护,我们把checkInternal 方法中的两个if判断中的逻辑再抽离出来。

首先定义一个策略接口:

java 复制代码
public interface CheckStrategy {
    boolean check(String content);
}

然后将两个if中的逻辑转到接口的实现类中:

java 复制代码
public class CheckStart implements CheckStrategy {
    @Override
    public boolean check(String content) {
        boolean result = false;
        if (content.startsWith("0")) {
            result = true;
        }
        return result;
    }
}
java 复制代码
public class CheckEnd implements CheckStrategy {
    @Override
    public boolean check(String content) {
        boolean result = false;
        if (content.endsWith("0")) {
            result = true;
        }
        return result;
    }
}

接下来再调整一下LicenseService中方法的调用,把原来的checkInternal方法中的if语句进行调整,改为调用CheckStrategy中的方法:

java 复制代码
public boolean checkLicense() {
        ...

        result = checkInternal(content, new CheckStart());

        ...
}

private static boolean checkInternal(String content, CheckStrategy strategy) {
        return strategy.check(content);
}

更多思考

有说一种对于策略的说法是替换满屏的if else,我认为这不能算是策略模式的使用目的,只能算是应用了策略模式后的副产物。

它更多的使用场景是这样:有某一大方法,其中的一个环节可以有不同实现,并且进行环节的算法替换时不影响原大方法的功能(或受到预期的控制)。这里再举一些应用场景的例子,不够精准但重在体会其中的思想:

  • 实现游戏可以用不同的底层引擎,引擎之间的替换可以应用策略模式
  • 程序和数据库交互时可能用到不同的数据库产品如mysql、sqllite,对不同数据库的交互操作可以应用策略模式
  • 别的我暂时想不起来了

Spring中实战

现在java web应用里Spring算是事实上的标准了,在Spring中用策略模式还是有一些小技巧的。下面直接给出代码请同学们品。

java 复制代码
@Service
public class LicenseService {
    // 注入strategy manager
    @Autowired
    private StrategyManager strategyManager;

    public boolean checkLicense() {
        boolean result = false;
        // abc0
        File file = Path.of("./secret").toFile();
        String content = "";
        try {
            // 读取文件内容
            BufferedReader br = new BufferedReader(new FileReader(file));
            content = br.readLine();
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 由manager作为策略类实现的提供者
        result = strategyManager
                .pickCheckStrategy(CheckStrategyEnum.START.toString())
                .check(content);

        return result;
    }
}
java 复制代码
@Service
public class StrategyManager {
    // 注入CheckStrategy list
    @Autowired
    private List<CheckStrategy> checkStrategyList;

    public CheckStrategy pickCheckStrategy(String type) {
        // 根据传入的type从上面list中取出对应的策略实现类并返回给调用者
        return checkStrategyList
                .stream()
                .filter(s -> s.type().equals(type))
                .findFirst()
                .orElseThrow();
    }
}

enum CheckStrategyEnum {
    END, START;
}
java 复制代码
public interface CheckStrategy {
    /**
     * 返回策略实现类的类型,用于为manager提供实现类的标识
     *
     * @return 自定义的枚举类型 {@link CheckStrategyEnum}
     */
    String type();

    /**
     * 判断授权。算法由实现类确定
     *
     * @param content 判断的内容
     * @return 是否判断成功
     */
    boolean check(String content);
}
java 复制代码
@Service
public class CheckStart implements CheckStrategy {
    @Override
    public String type() {
        // 返回对应的枚举type
        return CheckStrategyEnum.END.toString();
    }

    @Override
    public boolean check(String content) {
        boolean result = false;
        if (content.startsWith("0")) {
            result = true;
        }
        return result;
    }
}
java 复制代码
@Service
public class CheckEnd implements CheckStrategy {
    @Override
    public String type() {
        // 返回对应的枚举type
        return CheckStrategyEnum.START.toString();
    }

    @Override
    public boolean check(String content) {
        boolean result = false;
        if (content.endsWith("0")) {
            result = true;
        }
        return result;
    }
}
相关推荐
武子康13 分钟前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人3 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.3 小时前
Mybatis-Plus
java·开发语言
不良人天码星3 小时前
lombok插件不生效
java·开发语言·intellij-idea
守护者1703 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云3 小时前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台