什么是策略模式
策略模式也叫政策模式,他将定义的算法家族、分别封装起来,让他们之间可以相互替换,从而让算法的变化不会影响到使用算法的用户。属于行为型模式。
策略模式使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下具备不同实现。
应用场景
策略模式在生活中应用很多。比如一个人的胶水比例与他的工资有关,不同的工资水平对应不同的税率;再比如我们在下单前需要选择支付方式,比如支付宝、微信等。
策略模式可以解决在多种算法相似的情况下,避免使用if.else或switch…case带来的复杂性和臃肿性。在日常业务开发中,策略模式适用于以下场景:
- 针对同一种类型问题,有多种处理方式,每一种都能独立解决问题。
- 算法需要自由切换的场景
- 需要屏蔽算法规则的场景
首先看下策略模式的通用UML类图:
从类图中,我们可以看到,策略模式主要包含三种角色:
-
上下文角色(Context)
用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化。
-
抽象策略角色(Strategy)
规定策略或算法的行为
-
具体策略角色(Concrete Strategy)
具体的策略或算法实现
促销优惠业务场景
大家都知道商城经常会有优惠活动,但是优惠策略会有多种可能。比如:领取优惠券、返现促销、拼团优惠等。
我们可以通过策略模式来实现。
抽象类
首先创建一个促销策略的抽象类:
public interface IPromotionStrategy {
void doPromotion();
}
实现类
然后分别创建优惠券抵扣策略、返现促销策略、拼团优惠策略及无优惠策略。
public class CouponStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("使用优惠券抵扣");
}
}
public class CashbackStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("返现,直接打款到支付宝账号");
}
}
public class GroupbuyStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("5人成团,可以优惠");
}
}
public class EmptyStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("无优惠");
}
}
然后创建促销活动方案类:
public class PromotionActivity {
private IPromotionStrategy strategy;
public PromotionActivity(IPromotionStrategy strategy) {
this.strategy = strategy;
}
public void execute(){
strategy.doPromotion();
}
}
测试
编写客户端测试类:
public static void main(String[] args) {
PromotionActivity activity618 = new PromotionActivity(new CouponStrategy());
PromotionActivity activity11 = new PromotionActivity(new CashbackStrategy());
activity618.execute();
activity11.execute();
}
但是这种测试代码放到我们实际的业务场景是不适用的。因为正常情况下我们是需要根据不同的需求动态选择优惠策略。所以正常情况下,我们可以这样下:
public static void main(String[] args) {
PromotionActivity promotionActivity = null;
String promotionKey = "COUPON";
if (promotionKey.equals("COUPON")) {
promotionActivity = new PromotionActivity(new CouponStrategy());
} else if (promotionKey.equals("CASHBACK")) {
promotionActivity = new PromotionActivity(new CashbackStrategy());
} else {
//其他类型
}
promotionActivity.execute();
}
这样改造之后,我们可以根据自己的需求选择不同的优惠策略。但是,经过一段时间的积累,优惠活动会越累越多,代码约越来越臃肿,所以我们应该对其再次重构。这次我们可以结合之前的单例模式和工厂模式对其进行改造。创建一个工厂类:
public class PromotionStrategyFactory {
private static Map<String,IPromotionStrategy> PROMOTIONS = new HashMap<String,IPromotionStrategy>();
static {
PROMOTIONS.put(PromotionKey.COUPON,new CouponStrategy());
PROMOTIONS.put(PromotionKey.CASHBACK,new CashbackStrategy());
PROMOTIONS.put(PromotionKey.GROUPBUY,new GroupbuyStrategy());
}
private static final IPromotionStrategy EMPTY = new EmptyStrategy();
private PromotionStrategyFactory(){}
public static IPromotionStrategy getPromotionStrategy(String promotionKey){
IPromotionStrategy strategy = PROMOTIONS.get(promotionKey);
return strategy == null ? EMPTY : strategy;
}
private interface PromotionKey{
String COUPON = "COUPON";
String CASHBACK = "CASHBACK";
String GROUPBUY = "GROUPBUY";
}
public static Set<String> getPromotionKeys(){
return PROMOTIONS.keySet();
}
}
这时候我们测试代码改成这样:
public static void main(String[] args) {
PromotionStrategyFactory.getPromotionKeys();
String promotionKey = "COUPON";
IPromotionStrategy promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey);
promotionStrategy.doPromotion();
}
经过这样优化之后,有没有觉得简单多了?
选择支付场景
为了加深理解,我们在来一个实例:下单的时候会选择不同的支付方式。
抽象类
老规矩,首先创建支付方式的抽象类:
public abstract class Payment {
public abstract String getName();
//通用逻辑放到抽象类里面实现
public MsgResult pay(String uid, double amount) {
//余额是否足够
if (queryBalance(uid) < amount) {
return new MsgResult(500, "支付失败", "余额不足");
}
return new MsgResult(200, "支付成功", "支付金额" + amount);
}
protected abstract double queryBalance(String uid);
}
实现类
分别创建具体的支付方式类:
public class AliPay extends Payment {
public String getName() {
return "支付宝";
}
protected double queryBalance(String uid) {
return 900;
}
}
public class JDPay extends Payment {
public String getName() {
return "京东白条";
}
protected double queryBalance(String uid) {
return 500;
}
}
public class UnionPay extends Payment {
public String getName() {
return "银联支付";
}
protected double queryBalance(String uid) {
return 120;
}
}
public class WechatPay extends Payment {
public String getName() {
return "微信支付";
}
protected double queryBalance(String uid) {
return 263;
}
}
包装类
public class MsgResult {
private int code;
private Object data;
private String msg;
public MsgResult(int code, String msg, Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
@Override
public String toString() {
return "MsgResult{" +
"code=" + code +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
}
支付策略管理类
public class PayStrategy {
public static final String ALI_PAY = "AliPay";
public static final String JD_PAY = "JdPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String UNION_PAY = "UnionPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String, Payment> strategy = new HashMap<String, Payment>();
static {
strategy.put(ALI_PAY, new AliPay());
strategy.put(JD_PAY, new JDPay());
strategy.put(WECHAT_PAY, new WechatPay());
strategy.put(UNION_PAY, new UnionPay());
}
public static Payment get(String payKey) {
if (!strategy.containsKey(payKey)) {
return strategy.get(DEFAULT_PAY);
}
return strategy.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;
}
public MsgResult pay() {
return pay(PayStrategy.DEFAULT_PAY);
}
public MsgResult pay(String payKey) {
Payment payment = PayStrategy.get(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次交易金额为" + amount + ",开始扣款");
return payment.pay(uid, amount);
}
}
测试
public class Test {
public static void main(String[] args) {
Order order = new Order("1", "2021111501000323", 324.5);
System.out.println(order.pay(PayStrategy.UNION_PAY));
}
}
欢迎使用银联支付
本次交易金额为324.5,开始扣款
MsgResult{code=500, data=余额不足, msg='支付失败'}
策略模式的优缺点
优点
- 策略模式符合开闭原则
- 避免使用多重条件转移语句,如if…else和switch这种。
- 使用策略模式可以提高算法的保密性和安全性
缺点
- 客户端需要知道所有的策略,并且自行决定使用哪一个策略类
- 代码中会产生非常多的策略类,造成代码的臃肿,怎加维护难度。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16818.html