附链
你也可以在这些平台阅读本文:
定义
定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
三个角色
策略模式中主要有三个角色:
- 抽象策略类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();
}
}
测试类的输出结果如下:
黄金会员所有商品打九五折
类结构图
以上示例类的结构图如下所示
问题所在
上述示例代码的测试类即我们的客户端,我们可以看到,我们并未完全消除 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
条件转义语句,使客户端变得相对简单。
总结
策略模式的使用方式一般并不是独立使用的,有可能需要结合工厂模式、单例模式、享元模式等设计模式来一起实现。
适用场景
- 多个类的区别仅仅在于表现行为的不同。
- 一个系统需要动态地在几种算法中选择一种。
优点
- 可以避免使用多重条件语句。
- 符合开闭原则,可以在不修改原有代码的情况下,灵活增加新算法。
- 各算法彼此独立,且对客户端隐藏具体的算法实现,提高了算法的保密性和安全性。
缺点
- 客户端必须理解所有策略算法的区别,并且自行决定使用哪一个策略类。
- 会造成很多的策略类。