阅读 279

设计模式之策略模式的简单使用

这是我参与 8 月更文挑战的第3天,活动详情查看: 8月更文挑战

概念

定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。

关键点:定义、创建、使用

  • 定义策略接口,算法,一组实现该接口的策略类;
  • 策略的创建由工厂来完成,封装创建策略的细节;
  • 策略选择使用有两种方式:
  • 一种是静态确定,直接new 相应的策略接口;
  • 一种是动态确定,根据运行参数动态实例化策略接口;

下面以不同的优惠策略来实现策略模式。

策略定义

1. 定义策略接口

/**
 * 策略接口
 */
public interface Strategy {
​
    /**
     * 优惠算法
    */
    Object discount();
}
​
复制代码

2. 定义一组实现接口的策略类

  • 打折策略类
public class DiscountStrategy implements Strategy {
​
    @Override
    public Object discount() {
        System.out.println("打折");
        return null;
    }
}
复制代码
  • 减价策略类
public class ReducePriceStrategy implements Strategy {
​
    @Override
    public Object discount() {
        System.out.println("减价");
        return null;
    }
}
复制代码
  • 赠送优惠券策略类
public class FreeProductStrategy implements Strategy {
​
    @Override
    public Object discount() {
        System.out.println("赠送产品");
        return null;
    }
}
复制代码

策略工厂

1. 策略枚举类型

public enum DiscountEnum {
    DISCOUNT,   /** 打折 **/
    REDUCE_PRICE,     /** 减价 **/
    FREE_COUPON, /** 赠送优惠券 **/
    FREE_PRODUCT; /** 赠送产品 **/
}
复制代码

2. 策略工厂

2.1 无状态的策略工厂

当具体的优惠算法中是无状态的,那么可以用map将类型和策略相对应,在获取具体实现策略时通过 “查表法” 即可获取到相应的策略实现。

/**
 * 无状态的策略工厂
 * 无状态指的是纯算法,不需要每次调用都创建一个新的Strategy
 */
public class StrategyFactory {
​
    private static final Map<DiscountEnum,Strategy> strategies = new HashMap<>();
​
    static {
        strategies.put(DiscountEnum.DISCOUNT,new DiscountStrategy());
        strategies.put(DiscountEnum.REDUCE_PRICE,new ReducePriceStrategy());
        strategies.put(DiscountEnum.FREE_COUPON,new FreeCouponStrategy());
        strategies.put(DiscountEnum.FREE_PRODUCT,new FreeProductStrategy());
    }
​
    /**
     * 获取具体的实现策略
     * @param discountEnum
     * @return
     */
    public static Strategy getStrategy(DiscountEnum discountEnum) {
        if(discountEnum == null) {
            throw new IllegalArgumentException("discountNum should not be null");
        }
        return strategies.get(discountEnum);
    }
}
复制代码

2.2 有状态的策略工厂

当策略算法是有状态的,那么就需要在每次获取策略时重新创建策略类。可以通过反射的方式动态实例化出对象,使用map存储类型与策略Class的关系、或者通过注解动态创建。

这里使用map存储,一样通过map查表法创建;

/**
 * 有状态的策略工厂
 */
public class StrategyFactory2 {
​
    private static final Map<DiscountEnum,Class<? extends Strategy>> strategies = new HashMap<>();
​
    static {
        strategies.put(DiscountEnum.DISCOUNT, DiscountStrategy.class);
        strategies.put(DiscountEnum.REDUCE_PRICE, ReducePriceStrategy.class);
        strategies.put(DiscountEnum.FREE_COUPON, FreeCouponStrategy.class);
        strategies.put(DiscountEnum.FREE_PRODUCT, FreeProductStrategy.class);
    }
​
    /**
     * 每一次获取都重新创建一个 Strategy
     */
    public static Strategy getStrategy(DiscountEnum discountEnum) throws InstantiationException, IllegalAccessException {
        if(discountEnum == null) {
            throw new IllegalArgumentException("discountNum should not be null");
        }
​
        Class<? extends Strategy> strategyClass = strategies.get(discountEnum);
        return strategyClass.newInstance();
    }
​
}
复制代码

策略的使用

  • 运行时动态创建
 /**
     * 运行时动态确定
     */
    @Test
    public void testStrategy() {
        // 假设动态运行时获取的类型为减价类型
        DiscountEnum discountEnum = DiscountEnum.REDUCE_PRICE;
        Strategy strategy = StrategyFactory.getStrategy(discountEnum);
        strategy.discount();
    }
复制代码
  • 静态创建
/***
     * 静态确定策略
     */
    @Test
    public void testStrategy4Static() {
        // 假设知道要使用的算法为减价,直接new对应的策略实例
        Strategy strategy = new DiscountStrategy();
        strategy.discount();
    }
复制代码

总结

从上面的过程中可以发现,策略模式实际上就是定义算法接口 + 工厂模式的组合,通过定义算法接口规定算法签名,利用查表法在策略集合中找到符合条件的策略,从而实现不同策略的切换。从表现上看是抽离了if-else 的逻辑,实际上更多的作用是解耦了策略的定义、创建以及使用,使每个部分不至于过于复杂,从而最小化、集中化的管理代码。

文章分类
后端