策略模式定义了一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。也就是可以解决一堆ifelse难以维护的问题。
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为,然后我们对具体行为进行包装,对用户提供行为接口,一方面对我们的内部算法进行了保密封装,另一方面减少了用户端的if-else判断。
一般策略模式分为三类:
- 抽象策略角色: 这个是一个抽象的角色,通常情况下使用接口或者抽象类去实现。对比来说,就是我们的Comparator接口。
- 具体策略角色: 包装了具体的算法和行为。对比来说,就是实现了Comparator接口的实现一组实现类。
- 环境角色: 内部会持有一个抽象角色的引用,给客户端调用。
=========================================================================
举个例子:
假设我们要定义一个双十一折扣方法,我们可以看到很多不同的折扣方式:
- 满减:满了固定数额就减去输入的金额
- 直减:直接扣掉输入的金额
- 百分比折扣:直接打输入的折扣
这种情况就很适合使用策略模式来实现:
首先我们实现一个抽象的策略角色接口:
package StrategyPattern;
import java.math.BigDecimal;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName coupon.java
* @Description 优惠券接口
* @createTime 2022年03月09日 11:21:00
*/
public interface Coupon<T> {
/**
* 优惠券金额计算
* @param couponInfo 券折扣信息;直减、满减、折扣
* @param skuPrice 原价
* @return 优惠后金额
*/
BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
然后在此之上实现三个不同的减价策略:
满减:
package StrategyPattern.Coupons;
import StrategyPattern.Coupon;
import java.math.BigDecimal;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName DiscountByMoneyOff.java
* @Description 具体策略角色:
* 满减策略
* @createTime 2022年03月09日 15:30:00
*/
public class DiscountByMoneyOff implements Coupon<Map<String,Double>> {
/**
*
* @param couponInfo 对应了满减底价和满减折扣金额
* 如果小于满减底价就直接返回原价
* 如果大于满减价格就用现在的价格减去满减折扣金额
* @param price 总价
* @return 折扣后的价格
*/
@Override
public BigDecimal discountAmount(Map<String,Double> couponInfo, BigDecimal price) {
//通过字段获取底价
Double priceLine = couponInfo.get("greatThan");
//通过字段获取满减金额
Double priceOff = couponInfo.get("priceOff");
//总价是否大于满减底价
if (price.compareTo(new BigDecimal(priceLine))<1){
return new BigDecimal(priceOff);
}else {
return price.subtract(BigDecimal.valueOf(priceOff));
}
}
}
直减:
package StrategyPattern.Coupons;
import StrategyPattern.Coupon;
import java.math.BigDecimal;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName DiscountByPriceOff.java
* @Description 具体策略角色:
* 直减策略
* @createTime 2022年03月09日 15:35:00
*/
public class DiscountByPriceOff implements Coupon<Double> {
/**
* 直减:原价直接减去直减的金额
* @param couponInfo 券折扣信息;直减金额
* @param skuPrice 原价
* @return 折扣价
*/
@Override
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
if (discountAmount.compareTo(BigDecimal.ZERO)<1){
return skuPrice;
}else {
return discountAmount;
}
}
}
比例折扣:
package StrategyPattern.Coupons;
import StrategyPattern.Coupon;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName DiscountByRate.java
* @Description 具体策略角色:
* 折扣策略
* @createTime 2022年03月09日 13:58:00
*/
public class DiscountByRate implements Coupon<Double> {
/**
*
* @param couponInfo 券折扣信息;折扣额度(比例)
* @param skuPrice 原价
* @return 折扣之后的价格
*/
@Override
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
//折扣算法:总价*折扣额度
BigDecimal decimal = skuPrice.multiply(new BigDecimal(couponInfo));
return decimal.setScale(2, RoundingMode.HALF_DOWN);
}
}
创建环境角色进行策略的控制:
package StrategyPattern;
import java.math.BigDecimal;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Environment.java
* @Description 环境角色:
* 进行策略的控制
* @createTime 2022年03月09日 15:23:00
*/
public class Environment<T> {
private Coupon<T> coupon;
public Environment (Coupon<T> coupon){
this.coupon = coupon;
}
public BigDecimal discountAmount(T couponInfo ,BigDecimal price){
return coupon.discountAmount(couponInfo,price);
}
}
测试一下:
package StrategyPattern;
import StrategyPattern.Coupons.DiscountByMoneyOff;
import StrategyPattern.Coupons.DiscountByRate;
import java.math.BigDecimal;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName CouponTest.java
* @Description 策略测试
* @createTime 2022年03月09日 14:03:00
*/
public class CouponTest {
public static void main(String[] args) {
//根据策略选择创建策略对象
Environment<Double> context = new Environment<>(new DiscountByRate());
BigDecimal decimal = context.discountAmount(0.7D, new BigDecimal(500));
System.out.println(decimal);
}
}
优点:
- 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
- 策略模式提供了管理相关的算法族的办法。
- 策略模式提供了可以替换继承关系的办法。
- 使用策略模式可以避免使用多重条件转移语句。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
- 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。