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;
    }
}
相关推荐
Miraitowa_cheems17 分钟前
LeetCode算法日记 - Day 64: 岛屿的最大面积、被围绕的区域
java·算法·leetcode·决策树·职场和发展·深度优先·推荐算法
Lisonseekpan38 分钟前
Spring Boot 中使用 Caffeine 缓存详解与案例
java·spring boot·后端·spring·缓存
为java加瓦40 分钟前
Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
java·服务器·rust
SimonKing44 分钟前
分布式日志排查太头疼?TLog 让你一眼看穿请求链路!
java·后端·程序员
消失的旧时光-19431 小时前
Kotlin 判空写法对比与最佳实践
android·java·kotlin
小许学java1 小时前
Spring AI快速入门以及项目的创建
java·开发语言·人工智能·后端·spring·ai编程·spring ai
一叶飘零_sweeeet1 小时前
从 “死锁“ 到 “解耦“:重构中间服务破解 Java 循环依赖难题
java·循环依赖
Asort1 小时前
JavaScript设计模式(七)——桥接模式:解耦抽象与实现的优雅之道
前端·javascript·设计模式
whltaoin2 小时前
Java 后端与 AI 融合:技术路径、实战案例与未来趋势
java·开发语言·人工智能·编程思想·ai生态