策略模式的定义
策略模式是指定义了算法策略,分别封装起来,让它们之间可以互相替换,让算法的变化不会影响到使用算法的用户。
可以避免多重分支的if else 和 switch 语句。
策略模式的适用场景
1.假如系统中有很多类,而它们的区别仅在于它们的行为不同
2.一个系统需要动态地在几种算法中选择一种
3.需要屏蔽算法规则
示例1
电商经常会在节日做活动,需要根据各种节日选择不同的优惠策略:
/**
* 优惠策略
*/
public interface IPromotionStrategy {
/** 做优惠 */
void doPromotion();
}
/**
* @Description: 无优惠策略
*/
public class EmptyStrategy implements IPromotionStrategy {
@Override
public void doPromotion() {
System.out.println("无活动优惠");
}
}
/**
* @Description: 优惠券优惠
*/
public class CouponStrategy implements IPromotionStrategy {
@Override
public void doPromotion() {
System.out.println("领取优惠券,根据优惠券进行抵扣");
}
}
/**
* @Description: 返现优惠
*/
public class CashBackStrategy implements IPromotionStrategy {
@Override
public void doPromotion() {
System.out.println("返现,直接返现到支付宝余额");
}
}
/**
* @Description: 团购优惠
*/
public class GroupBuyStrategy implements IPromotionStrategy {
@Override
public void doPromotion() {
System.out.println("拼团成功,进行打折");
}
}
/**
* @Description: 优惠活动
*/
public class PromotionActivity {
private IPromotionStrategy promotionStrategy;
public PromotionActivity(IPromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
//执行活动
public void execute(){
promotionStrategy.doPromotion();
}
}
public class PromotionActivityTest {
public static void main(String[] args) {
//六一八活动-优惠券策略
PromotionActivity activity618 = new PromotionActivity(new CouponStrategy());
//双十一活动-返现策略
PromotionActivity activity1111 = new PromotionActivity(new CashBackStrategy());
activity618.execute();
activity1111.execute();
}
}
当然,实际开发中并不会像上面一样写定优惠策略,而是动态地根据活动选择优惠策略。
进行优化:
public static void main(String[] args) {
PromotionActivity activity=null;
//假如现在的活动是优惠券优惠的活动
String activityKey = "Coupon";
if (StringUtils.equals(activityKey,"Coupon")){
activity=new PromotionActivity(new CouponStrategy());
}else if (StringUtils.equals(activityKey,"CashBack")){
activity=new PromotionActivity(new CashBackStrategy());
}
//......
activity.execute();
}
现在倒是动态分配了,但是每多一种新的优惠策略,都需要去这里的业务逻辑加if-else,还得将整套业务逻辑重新测试。程序猿又要加班了。
再次优化:
/**
* @Description: 结合策略模式和注册式单例,将业务代码中的多个if-else判断进行优化
*/
public class PromotionStrategyFactory {
//定义一个Map,在工厂类内部使用静态代码块在类加载时就对map进行初始化 -> 注册式单例的特征
private static Map<String, IPromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<String, IPromotionStrategy>();
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON, new CouponStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.CASH_BACK, new CashBackStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUP_BUY, new GroupBuyStrategy());
}
//构造方法私有化并提供公共的静态方法访问 -> 单例的特征
private PromotionStrategyFactory() {
}
//根据优惠标识获得优惠策略
public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
IPromotionStrategy strategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return strategy == null ? NON_PROMOTION : strategy;
}
//优惠标识Key
private interface PromotionKey {
String COUPON = "COUPON";
String CASH_BACK = "CASH_BACK";
String GROUP_BUY = "GROUP_BUY";
}
//常量-无优惠策略
private static final IPromotionStrategy NON_PROMOTION = new EmptyStrategy();
}
public class PromotionActivityTest {
public static void main(String[] args) {
String promotionKey = "COUPON";
PromotionActivity activity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
activity.execute();
}
}
可以发现,再次优化后,每次增加新的优惠策略只需要增加注册式单例的私有Map,在Map中添加响应的优惠策略即可,不需要再去修改调用处的业务代码。
这种实现将原本繁杂的多重判断进行了简化,也使日后程序中业务逻辑的维护变得简单。
示例二
网购下单时我们需要选择不同的支付方式进行支付:
/**
* @Description: 支付的抽象
*/
public abstract class Payment {
//支付类型
public abstract String getName();
//查询余额
protected abstract double queryBalance(String uid);
//扣款支付
public MsgResult pay(String uid,double amount){
if (queryBalance(uid)<amount){
return new MsgResult(500,"支付失败","余额不足");
}
return new MsgResult(200,"支付成功","支付金额"+amount);
}
}
/**
* @Description: 阿里支付
*/
public class AliPay extends Payment {
@Override
public String getName() {
return "支付宝";
}
@Override
protected double queryBalance(String uid) {
return 900;
}
}
/**
* @Description: 京东支付
*/
public class JDPay extends Payment {
@Override
public String getName() {
return "京东白条";
}
@Override
protected double queryBalance(String uid) {
return 500;
}
}
/**
* @Description: 银联支付
*/
public class UnionPay extends Payment {
@Override
public String getName() {
return "银联支付";
}
@Override
protected double queryBalance(String uid) {
return 200;
}
}
/**
* @Description: 微信支付
*/
public class WechatPay extends Payment {
@Override
public String getName() {
return "微信支付";
}
@Override
protected double queryBalance(String uid) {
return 700;
}
}
/**
* @Description: 支付状态
*/
public class PayState {
private int code;
private String msg;
private Object data;
public PayState(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
@Override
public String toString() {
return "支付状态:["+code+"]," + msg + ", 交易详情: " + data;
}
}
/**
* @Description: 支付策略管理器
* 注册式单例,提前注册好各式支付方式的实例
*/
public class PayStrategy {
public static final String ALI_PAY = "ALI_PAY";
public static final String JD_PAY = "JD_PAY";
public static final String UNION_PAY = "UNION_PAY";
public static final String WECHAT_PAY = "WECHAT_PAY";
public static final String DEFAULT_PAY = "ALI_PAY";
private static Map<String,Payment> payStrategy = new HashMap<>();
static{
payStrategy.put(ALI_PAY,new AliPay());
payStrategy.put(JD_PAY,new JDPay());
payStrategy.put(UNION_PAY,new UnionPay());
payStrategy.put(WECHAT_PAY,new WechatPay());
}
public static Payment get(String payKey){
if (!payStrategy.containsKey(payKey)){
return payStrategy.get(DEFAULT_PAY);
}
return payStrategy.get(payKey);
}
}
public class Order {
private String uid;
private String orderId;
private double amount;
public Order(String uid, String orderId, double amount) {
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
/**
* 使用PayStrategy根据支付的类型key选择支付策略
* 不需要再在业务中写多个条件判断了
*/
public PayState pay(String payKey){
Payment payment = PayStrategy.get(payKey);
System.out.println("开始使用:" + payment.getName());
System.out.println("本次支付金额:" + amount);
return payment.pay(uid,amount);
}
}
/**
* @Auther: jesses
* @Date: 2021/4/12
* @Description: 下单测试
*/
public class PayTest {
public static void main(String[] args) {
Order order = new Order("1", "202104121042001", 322.55);
//在使用时才确定支付方式的key
PayState payState = order.pay(PayStrategy.ALI_PAY);
System.out.println(payState);
}
}
策略模式在JDK中的应用
如较常用的比较器Comparator接口,就是策略模式的抽象实现:
Comparator接口有许多的实现类,在使用时常常把Comparator作为参数传入作为排序策略,比如Arrays.sort():
以及TreeMap的构造方法:
策略模式在Spring中的应用:
比如Resource接口及其子类实现:
还有Spring的初始化也使用到了策略模式,如InstantiationStrategy类,可以发现它的一个实现类CglibSubclassingInstantiationStrategy 是继承 另一个实现类SimpleInstantiationStrategy的,
所以说策略之间还可以继承使用。
策略模式的优点
1,策略模式符合开闭原则
2,避免多重条件判断if-else语句,提高了程序的可维护性
3,策略模式对算法进行封装,提高了算法的保密性和安全性
策略模式的缺点
1,客户端必须知道所有的策略,才能自行决定使用哪一种策略
2,代码中会产生较多的策略类,增加了维护的难度
ps:设计模式就是这样,有优点也有缺点,过多使用设计模式也会增加程序的复杂度,一昧的套用设计模式并不可取,需要根据实际业务情况去应用。