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;
    }
}
相关推荐
技术无疆17 分钟前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
架构文摘JGWZ3 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
拾光师4 小时前
spring获取当前request
java·后端·spring
aPurpleBerry4 小时前
neo4j安装启动教程+对应的jdk配置
java·neo4j
我是苏苏4 小时前
Web开发:ABP框架2——入门级别的增删改查Demo
java·开发语言
xujinwei_gingko4 小时前
Spring IOC容器Bean对象管理-Java Config方式
java·spring
2301_789985944 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习
IT学长编程4 小时前
计算机毕业设计 教师科研信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·毕业设计·springboot·毕业论文·计算机毕业设计选题·计算机毕业设计开题报告·教师科研管理系统
m0_571957585 小时前
Java | Leetcode Java题解之第406题根据身高重建队列
java·leetcode·题解
程序猿小D5 小时前
第二百三十五节 JPA教程 - JPA Lob列示例
java·数据库·windows·oracle·jdk·jpa