👉文章示例代码👈

定义

定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

三个角色

策略模式中主要有三个角色:

  • 抽象策略类Strategy:定义所有支持的算法的公共接口,可以是接口也可以是抽象类。
  • 具体策略类Concrete Strategy:封装具体的算法或者行为。
  • 环境类Context:维护一个策略类的引用,提供给客户端调用。

场景示例

笔者这里针对某超市的会员体系为例。不同等级的会员拥有不同的打折力度,具体情况如下表。

会员等级打折力度
普通会员不打折
黄金会员九五折
铂金会员八八折
钻石会员八折

创建抽象策略

/**
 * @author zhh
 * @description 会员策略
 * @date 2020-02-28 16:56
 */
public interface MemberStrategy {

    /**
     * 打折
     */
    void discount();
}

创建具体策略

/**
 * @author zhh
 * @description 普通会员策略
 * @date 2020-02-28 16:58
 */
public class OrdinaryMemberStrategy implements MemberStrategy {

    public void discount() {
        System.out.println("普通会员不打折");
    }
}


/**
 * @author zhh
 * @description 黄金会员策略
 * @date 2020-02-28 16:59
 */
public class GoldMemberStrategy implements MemberStrategy {

    public void discount() {
        System.out.println("黄金会员所有商品打九五折");
    }
}


/**
 * @author zhh
 * @description 铂金会员策略
 * @date 2020-02-28 17:00
 */
public class PlatinumMemberStrategy implements MemberStrategy {

    public void discount() {
        System.out.println("铂金会员所有商品打八八折");
    }
}


/**
 * @author zhh
 * @description 钻石会员策略
 * @date 2020-02-28 17:01
 */
public class DiamondMemberStrategy implements MemberStrategy {

    public void discount() {
        System.out.println("钻石会员所有商品打八折");
    }
}

创建环境类

/**
 * @author zhh
 * @description 打折活动
 * @date 2020-02-28 17:03
 */
public class DiscountActivity {

    private MemberStrategy memberStrategy;

    public DiscountActivity(MemberStrategy memberStrategy) {
        this.memberStrategy = memberStrategy;
    }

    /**
     * 打折
     */
    public void discount() {
        memberStrategy.discount();
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-28 17:05
 */
public class Test {

    public static void main(String[] args) {
        String member = "黄金会员";
        DiscountActivity discountActivity;

        if ("黄金会员".equals(member)) {
            discountActivity = new DiscountActivity(new GoldMemberStrategy());
        } else if ("铂金会员".equals(member)) {
            discountActivity = new DiscountActivity(new PlatinumMemberStrategy());
        } else if ("钻石会员".equals(member)) {
            discountActivity = new DiscountActivity(new DiamondMemberStrategy());
        } else {
            discountActivity = new DiscountActivity(new OrdinaryMemberStrategy());
        }

        discountActivity.discount();
    }
}

测试类的输出结果如下:

黄金会员所有商品打九五折

类结构图

以上示例类的结构图如下所示

image.png

问题所在

上述示例代码的测试类即我们的客户端,我们可以看到,我们并未完全消除 ​if...else 这种条件语句,而且每次方法执行到此处时,都需要创建一个策略对象和一个活动对象,会存在重复创建的现象。

方案改进

为了避免上述的问题,我们结合策略模式和工厂模式在原有的示例代码上进行改造。

新增工厂类

/**
 * @author zhh
 * @description 会员策略工厂
 * @date 2020-02-28 17:24
 */
public class MemberStrategyFactory {

    private static Map<String, MemberStrategy> MEMBER_STRATEGY_MAP = new HashMap<String, MemberStrategy>();

    static {
        init();
    }

    private MemberStrategyFactory() {}

    /**
     * 获取会员策略
     * @param member 会员等级
     */
    public static MemberStrategy getMemberStrategy(String member) {
        MemberStrategy memberStrategy = MEMBER_STRATEGY_MAP.get(member);
        return memberStrategy == null ? MEMBER_STRATEGY_MAP.get(MemberKey.ORDINARY) : memberStrategy;
    }

    /**
     * 初始化操作
     */
    private static void init() {
        MEMBER_STRATEGY_MAP.put(MemberKey.ORDINARY, new OrdinaryMemberStrategy());
        MEMBER_STRATEGY_MAP.put(MemberKey.GOLD, new GoldMemberStrategy());
        MEMBER_STRATEGY_MAP.put(MemberKey.PLATINUM, new PlatinumMemberStrategy());
        MEMBER_STRATEGY_MAP.put(MemberKey.DIAMOND, new DiamondMemberStrategy());
    }

    /**
     * 会员键值
     */
    private interface MemberKey {
        String ORDINARY = "普通会员";
        String GOLD = "黄金会员";
        String PLATINUM = "铂金会员";
        String DIAMOND = "钻石会员";
    }
}

调整测试类

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-28 17:05
 */
public class Test {

    public static void main(String[] args) {
        String member = "黄金会员";
        DiscountActivity discountActivity = new DiscountActivity(MemberStrategyFactory.getMemberStrategy(member));
        discountActivity.discount();
    }
}

测试类的输出结果如下:

黄金会员所有商品打九五折

这里我们可以看到,通过策略模式和工厂模式的组合使用,我们消除了客户端的 ​if...else 条件转义语句,使客户端变得相对简单。

总结

策略模式的使用方式一般并不是独立使用的,有可能需要结合工厂模式、单例模式、享元模式等设计模式来一起实现。

适用场景

  • 多个类的区别仅仅在于表现行为的不同。
  • 一个系统需要动态地在几种算法中选择一种。

优点

  • 可以避免使用多重条件语句。
  • 符合开闭原则,可以在不修改原有代码的情况下,灵活增加新算法。
  • 各算法彼此独立,且对客户端隐藏具体的算法实现,提高了算法的保密性和安全性。

缺点

  • 客户端必须理解所有策略算法的区别,并且自行决定使用哪一个策略类。
  • 会造成很多的策略类。

参考