一、什么是策略模式
策略模式定义了一系列算法,封装了每个算法,并使它们可以互换。
怎么理解?比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。
UML图如下:
image.png- Strategy(抽象策略):特定策略的抽象。
- ConcreteStrategy(具体策略):实现抽象策略的类。
- Context(环境):运行特定策略的类,维护一个对Strategy对象的引用,用于定义所有支持算法的公共接口。
二、应用实例
以商场促销为例使用策略模式实现商场促销算法。
-
CashSuper
现金收费接口,抽象出收费的方法供实现类实现
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:03
* @description: 现金收费接口,抽象出收费的方法供实现类实现
*/
public interface CashSuper {
/**
* 抽象的收费方式,供实现
* @param money
* @return
*/
double acceptCash(double money);
}
- 上下文类
CashContext
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:03
* @description: 首先声明一个CashSuper对象,通过构造方法,传入具体的收费策略,getResult()方法的功能为根据收费策略的不同获得计算结果
*/
public class CashContext {
private CashSuper cashSuper;
public CashContext(CashSuper cashSuper) {
this.cashSuper = cashSuper;
}
public double getResult(double money) {
return cashSuper.acceptCash(money);
}
}
- 正常收费实现类
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:06
* @description: 正常收费实现类
*/
public class CashNormal implements CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
- 打折收费子类
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:07
* @description: 打折收费实现类
*/
public class CashRebate implements CashSuper {
/**
* 折扣,默认是1
*/
private double moneyRebate = 1;
public CashRebate(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double acceptCash(double money) {
return money * moneyRebate;
}
}
- 返利收费子类
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:09
* @description: 返利收费子类,返利活动,输入返利条件和返利值,比如满300返100,moneyCondition为300,moneyReturn为100
*/
public class CashReturn implements CashSuper {
/**
* 返利条件
*/
private double moneyCondition = 0.0d;
/**
* 返利值
*/
private double moneyReturn = 0.0d;
public CashReturn(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double acceptCash(double money) {
double result = money;
if (money >= moneyCondition) {
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
- 测试类
/**
* @author: Jay Mitter
* @date: 2020-09-02 22:23
* @description:
*/
public class StrategyTest {
public static void main(String[] args) {
CashContext cashContext = null;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入打折方式(1/2/3):");
int in = scanner.nextInt();
String type = "";
switch (in) {
case 1:
cashContext = new CashContext(new CashNormal());
type += "正常收费";
break;
case 2:
cashContext = new CashContext(new CashReturn(300, 100));
type += "满300返100";
break;
case 3:
cashContext = new CashContext(new CashRebate(0.8));
type += "打8折";
break;
default:
System.out.println("请输入1/2/3");
break;
}
double totalPrice = 0;
System.out.print("请输入单价:");
double price = scanner.nextDouble();
System.out.println("请输入数量:");
double num = scanner.nextDouble();
assert cashContext != null;
totalPrice = cashContext.getResult(price * num);
System.out.println("单价:" + price + ",数量:" + num + ",类型:" + type + ",合计:" + totalPrice);
scanner.close();
}
}
三、策略模式的应用
- 何时使用
一个系统有许多类,而区分它们的只是他们直接的行为时 - 方法
将这些算法封装成一个一个的类,任意的替换 - 优点
算法可以自由切换
避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护)
扩展性良好,增加一个策略只需实现接口即可 - 缺点
策略类数量会增多,每个策略都是一个类,复用的可能性很小
所有的策略类都需要对外暴露 - 使用场景
多个类只有算法或行为上稍有不同的场景
算法需要自由切换的场景
需要屏蔽算法规则的场景 - 应用实例
出行方式,自行车、汽车等,每一种出行方式都是一个策略
商场促销方式,打折、满减等
Java AWT中的LayoutManager,即布局管理器 - 注意事项
如果一个系统的策略多于四个,就需要考虑使用混合模式来解决策略类膨胀的问题
参考:https://www.cnblogs.com/adamjwh/p/11011095.html
SpringBoot基于注解实现策略模式
基本思路
根据不同的参数type走不同的策略,每个策略类上通过注解与参数类型绑定,项目启动时扫描所有的策略类型,存储到map中,运行时根据业务类的type获取到对应的策略类即可。
实现
模拟订单业务,根据订单的type,需要不同的处理逻辑、,比如:免费订单、半价订单、打折订单等。
一、业务实体Order
/**
* @author: Jay Mitter
* @date: 2020-09-06 09:58
* @description: 业务实体——订单
*/
@Data
@Builder
public class Order {
/**
* 免费订单
*/
public static final int FREE = 1;
/**
* 半价订单
*/
public static final int HALF = 2;
/**
* 打折订单
*/
public static final int DISCOUNT = 3;
private String name;
private Double price;
private Integer type;
}
二、策略接口OrderStrategy及其实现类
OrderStrategy
/**
* @author: Jay Mitter
* @date: 2020-09-06 10:09
* @description: 处理订单策略
*/
public interface OrderStrategy {
/**
* 处理订单
* @param order
*/
void handleOrder(Order order);
}
FreeOrderStrategy
/**
* @author: Jay Mitter
* @date: 2020-09-06 10:54
* @description: 免费订单
*/
@Component
@HandlerOrderType(value = Order.FREE) // 使用注解标明策略类型
public class FreeOrderStrategy implements OrderStrategy {
@Override
public void handleOrder(Order order) {
System.out.println("----处理免费订单----");
}
}
HalfOrderStrategy
/**
* @author: Jay Mitter
* @date: 2020-09-06 10:56
* @description: 半价订单处理类
*/
@Component
@HandlerOrderType(value = Order.HALF)
public class HalfOrderStrategy implements OrderStrategy {
@Override
public void handleOrder(Order order) {
System.out.println("----处理半价订单----");
}
}
DiscountOrderStrategy
/**
* @author: Jay Mitter
* @date: 2020-09-06 11:07
* @description: 打折订单处理类
*/
@Component
@HandlerOrderType(value = Order.DISCOUNT)
public class DiscountOrderStrategy implements OrderStrategy {
@Override
public void handleOrder(Order order) {
System.out.println("----处理打折订单----");
}
}
三、自定义策略注解
/**
* @author: Jay Mitter
* @date: 2020-09-06 09:47
* @description: 自定义注解策略
*/
@Target(ElementType.TYPE) // 作用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited // 子类可以继承此注解
public @interface HandlerOrderType {
/**
* 策略类型
* @return 策略类型
*/
int value();
}
四、核心功能实现
HandlerOrderContext
/**
* @author: Jay Mitter
* @date: 2020-09-06 11:10
* @description: 策略上下文
*/
@Component
public class HandlerOrderContext {
@Autowired
private ApplicationContext applicationContext;
/**
* 存放所有策略类Bean的map
*/
public static Map<Integer, Class<OrderStrategy>> orderStrategyBeanMap = new ConcurrentHashMap<>();
public OrderStrategy getOrderStrategy(Integer type) throws IllegalAccessException {
Class<OrderStrategy> orderStrategyClass = orderStrategyBeanMap.get(type);
if (null == orderStrategyClass) {
throw new IllegalAccessException("没有对应的订单类型");
}
return applicationContext.getBean(orderStrategyClass);
}
}
策略核心功能,获取所有策略注解的类型,并将对应的class初始化到HandlerOrderContext中
/**
* @author: Jay Mitter
* @date: 2020-09-06 15:32
* @description: 策略核心功能,获取所有策略注解的类型,并将对应的class初始化到HandlerOrderContext中
*/
@Component
public class HandlerOrderProcessor implements ApplicationContextAware {
/**
* 获取所有的策略BeanClass 加入HandlerOrderContext属性中
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> orderStrategyMap = applicationContext.getBeansWithAnnotation(HandlerOrderType.class);
orderStrategyMap.forEach((k, v) -> {
Class<OrderStrategy> orderStrategyClass = (Class<OrderStrategy>) v.getClass();
int type = orderStrategyClass.getAnnotation(HandlerOrderType.class).value();
// 将class加入map中,type作为key
HandlerOrderContext.orderStrategyBeanMap.put(type, orderStrategyClass);
});
}
}
五、业务接口及其实现类
OrderService
/**
* @author: Jay Mitter
* @date: 2020-09-06 15:41
* @description: 订单接口
*/
public interface OrderService {
/**
* 处理订单
* @param order
*/
void handleOrder(Order order);
}
OrderServiceImpl
/**
* @author: Jay Mitter
* @date: 2020-09-06 15:42
* @description:
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private HandlerOrderContext handlerOrderContext;
@Override
public void handleOrder(Order order) {
// 使用策略处理订单
OrderStrategy orderStrategy = null;
try {
orderStrategy = handlerOrderContext.getOrderStrategy(order.getType());
orderStrategy.handleOrder(order);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
六、测试
@Autowired
private OrderService orderService;
/**
* 行为型模式——boot策略模式
*/
@Test
public void testBehaviorBootStrategy() {
Order order = Order.builder().name("微信订单").price(99.9).type(Order.DISCOUNT).build();
orderService.handleOrder(order);
}
以后有新的类型的Order时,在Order类中添加新的type和实现对应的type的策略类即可
网友评论