策略模式是我们 Java 开发中最最基础的开发模式,我们在日常生活中都可以找到与之相关。现实中我们各种为实现某种目标而选择的具体某种行为,都可以归纳为之策略。
我们选择去旅行,对于交通工具的选择上,我们有飞机、客车、高铁、自驾等做种选择,每一种选择都代表一种策略,都为了我们去旅行这个目标而服务。
而在软件开发中我们也常遇到类似的情况,当为实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
所以这里给策略( Strategy
)模式下个定义,模式定义一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
总之,所以在本质上策略的设计模式属于行为型模式一种,通过定义的一套算法类,进行封装,让算法类之间可以相互替换,从而达到一种按照调用侧/实现侧去决定目标实现。策略模式可以最大程度的避免代码中 if else
以及 switch
语句。
策略模式 的实现包含三个组成部分,分别为 策略类
、 具体策略类
、 上下文
。
Strategy
。ConcreteStrategy
来配置,维护一个对 Strategy
对象的引用此处假定我们在商场购物,选购的商品为 桌面电脑,那么在用户支付的环节,对支付的手段,可以有多种选择,例如 现金
、信用卡
、积分
等。那么我们在设计过程中,就遵循这种这种实现。
抽象一个付款方式,我们没有指明具体是哪一种付款方式,留给实现类去具体说明。
public interface Payment {
void payment();
}
public class Cash implements Payment{
@Override
public void payment() {
System.out.println("现金付款");
}
}
public class Credit implements Payment{
@Override
public void payment() {
System.out.println("信用卡付款");
}
}
public class Points implements Payment{
@Override
public void payment() {
System.out.println("积分付款");
}
}
public class Shopping {
private Payment payment;
public Shopping(Payment payment) {
this.payment = payment;
}
public void buyDesktop(){
System.out.println("购买台式机");
payment.payment();
}
}
@DisplayName("策略应用")
@Test
public void testStrategy() {
Payment payment = new Cash();
Shopping shopping = new Shopping(payment);
shopping.buyDesktop();
}
在我们上面使用 策略模式
的设计中,如果当存在的具体策略很多时,策略的客户端在管理所有策略将变得很臃肿且复杂,那如果在环境类中使用一个策略工厂模式来管理具体策略类,那么将大大减少客户端的工作复杂度。
package io.github.rothschil.design.strategy.factory;
import cn.hutool.core.util.ClassUtil;
import io.github.rothschil.design.strategy.Payment;
import io.github.rothschil.design.strategy.StrategyEnum;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class StrategyFactory {
private static Map<String, Payment> PAYMENT_MAP = new HashMap<>();
public StrategyFactory() {
// 反射出子类
Set<Class<?>> sets = ClassUtil.scanPackageBySuper("io.github.rothschil", Payment.class);
for (Class<?> it : sets) {
try {
Payment payment = (Payment)it.newInstance();
PAYMENT_MAP.put(it.getSimpleName(), payment);
} catch (InstantiationException | IllegalAccessException ignored) {
}
}
}
public Payment get(StrategyEnum keyEnum) {
return PAYMENT_MAP.get(keyEnum.getKey());
}
}
那我们对我们上面的测试类也进行改造下,
@DisplayName("策略应用")
@Test
public void testStrategy2() {
StrategyFactory strategyFactory = new StrategyFactory();
Payment payment = strategyFactory.get(StrategyEnum.CASH);
Shopping shopping = new Shopping(payment);
shopping.buyDesktop();
}