策略模式(Strategy Pattern)

策略模式是一种行为设计模式,它将一组行为转换为对象, 并使其在原始上下文对象外部可能互相替换。大白话就是比方我写一个登录业务,目前须要满足能通过零碎内、微信等平台进行登录,将来还有可能引入其余的平台,这个时候就能够采纳策略模式,来让不同的平台的登录都有对应的策略门路。

此外对于不同类型的交易方式(信用卡、支付宝、微信),生成惟一ID的策略(UUID、雪花算法、Leaf算法)等,咱们都能够先用策略模式对其进行行为包装,而后提供给外界进行调用。

一、 策略模式介绍

在策略模式中,次要有两个局部:

  • 示意各种策略的对象Strategy
  • 行为随着策略对象扭转而扭转的原始对象Context,它次要用于散发不同策略对象

留神,如果一个零碎中的策略多于四个,就须要思考应用混合模式,解决策略类收缩的问题。上面来看看对应的UML结构图:

  • Stategy:形象策略构造,定义不同策略须要执行的对立步骤和办法
  • ConcreteStrategy1、ConcreteStrategy2:实现形象策略定义的接口,提供具体的算法实现
  • Context:上下文类,是外界获取不同策略的接口

二、策略模式利用

2.1 Java Comparator中的策略模式

java.util.comparator 中,comparator作为比拟的接口,能够实现具体的比拟策略。而java.util.Collections 中的sort(List<T> list, Comparator<? super T> c) 作为context类,执行不同的比拟逻辑

能够做一个排序的demo来演示:

 public static void main(String[] args) {        ArrayList<Integer> integers = new ArrayList<>();        integers.add(1);        integers.add(3);        integers.add(5);        integers.add(4);        integers.add(2);        for (Integer integer : integers) {            System.out.print(integer);        }        System.out.println("程序后~");        Collections.sort(integers,new AscComparator());        for (Integer integer : integers) {            System.out.print(integer);        }        System.out.println("逆序后~");        Collections.sort(integers,new DescComparator());        for (Integer integer : integers) {            System.out.print(integer);        }        InstantiationStrategy    }    static class DescComparator implements Comparator<Integer> {        @Override        public int compare(Integer o1, Integer o2) {            return o2 - o1;        }    }    static class AscComparator implements Comparator<Integer> {        @Override        public int compare(Integer o1, Integer o2) {            return o1 - o2;        }    }

最初输入:

13542程序后~54321逆序后~12345

2.2 Spring Bean实例化中的策略模式

其中InstantiationStrategy 作为实例化策略接口,AbstractAutowireCapableBeanFactory 作为上下文,创立策略并调用

三、 策略模式实战

3.1 生成不同的ID策略

就拿生成惟一ID业务来举例子,比方在雪花算法提出之前,咱们个别应用的是UUID 来确认惟一ID。然而如果须要有序的生成ID,这个时候就要考虑一下其余的生成办法,比方雪花、Leaf等算法了。

可能刚开始咱们是间接写一个类,在类外面调用UUID算法来生成,然而须要调用其余办法时,咱们就必须在这个类外面用if-else等逻辑判断,而后再转换成另外的算法中。这样的做法和后面提到的工厂模式一样,会进步类之间的耦合度。所以咱们能够应用策略模式将这些策略抽离进去,独自实现,避免前期若须要扩大带来的凌乱。

首先,定义一个ID生成的接口IIdGenerator

public interface IIdGenerator {    /**     * 获取ID, 目前有三种实现形式     * 1.雪花算法,次要用于生成单号     * 2.日期算法,用于生成流动标号类,个性是生成数字串较短,然而指定工夫内不能生成太多     * 3.随机算法,用于生成策略ID     * @return ID 返回ID     */    long nextId();}

让不同生成ID策略实现该接口:

上面是雪花算法的具体实现 :

public class SnowFlake implements IIdGenerator {    private Snowflake snowflake;    @PostConstruct    public void init() {        //总共有5位,部署0~32台机器        long workerId;        try {            workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());        } catch (Exception e) {            workerId = NetUtil.getLocalhostStr().hashCode();        }        workerId = workerId >> 16 & 31;        long dataCenterId = 1L;        snowflake = IdUtil.createSnowflake(workerId, dataCenterId);    }    @Override    public long nextId() {        return snowflake.nextId();    }}

其次还要定义一个ID策略管制类IdContext ,通过内部不同的策略,利用对立的办法执行ID策略计算,如下所示:

@Configurationpublic class IdContext {    @Bean    public Map<Constants.Ids, IIdGenerator> idGenerator(SnowFlake snowFlake, ShortCode shortCode, RandomNumeric randomNumeric) {        Map<Constants.Ids, IIdGenerator> idGeneratorMap = new HashMap<>(8);        idGeneratorMap.put(Constants.Ids.SnowFlake, snowFlake);        idGeneratorMap.put(Constants.Ids.ShortCode, shortCode);        idGeneratorMap.put(Constants.Ids.RandomNumeric, randomNumeric);        return idGeneratorMap;    }}

所以在最初测试时,间接调用idGeneratorMap就能够实现不同策略服务的调用:

 @Test public void init() {     logger.info("雪花算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.SnowFlake).nextId());     logger.info("日期算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.ShortCode).nextId());     logger.info("随机算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.RandomNumeric).nextId()); }
  1. 2 实现不同平台登录零碎

正如前言提到的,在同样的登录过程中,须要实现不同平台的登录策略,这里就单列出微信登录零碎的实现逻辑来展现策略模式:

  1. 登录接口

该局部提供给前端进行调用,通过前台传递不同的平台参数,来执行不同的登录策略:

public Result<JSONObject> login(@RequestBody LoginRequestModel loginRequest) {    Result<JSONObject> result = new Result<JSONObject>();    if (loginRequest.getThirdPlatform() == null) {        result.error500("找不到该平台,请配置后再登录!");        return result;    } else {        LoginStrategy loginStrategy = loginStrategyContext.getLoginStrategy(loginRequest.getThirdPlatform());        result = loginStrategy.login(loginRequest);        return result;    }}
  1. 登录策略context

该局部次要通过创立登录策略来进行调用, 这里是利用spring来将不同策略对象提前注入,方便管理和调用

@Componentpublic class LoginStrategyContext{    private Map<String, LoginStrategy> strategies = new ConcurrentHashMap<>();    /**     * 将所有策略注入springBean中     * @param strategies     */    public LoginStrategyContext(Map<String, LoginStrategy> strategies) {        strategies.forEach(this.strategies::put);    }    public LoginStrategy getLoginStrategy(String strategyName) {        LoginStrategy loginStrategy = strategies.get(strategyName);        return loginStrategy;    }}
  1. 策略接口和具体实现

该局部实现登录策略接口和具体的登录实现策略

public interface LoginStrategy {    /**     * 解决具体登录逻辑     * @return     */    Result<JSONObject> login(LoginRequestModel loginRequest);}
public class WeChatLoginStrategy implements LoginStrategy {    @Override    public Result<JSONObject> login(LoginRequestModel loginRequest) {        //解决具体的登录逻辑    }}

以上就是对于策略设计模式的内容,其实在日常业务逻辑中对于设计模式的应用,并不是非得肯定要代码中有设计模式才行,简略的逻辑就用if-else即可。如果有简单的业务逻辑,而且也合乎对应的设计模式,这样应用模式能力真正进步代码的逻辑性和可扩展性。