策略模式详解

导读:本篇文章讲解 策略模式详解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

什么是策略模式

策略模式也叫政策模式,他将定义的算法家族、分别封装起来,让他们之间可以相互替换,从而让算法的变化不会影响到使用算法的用户。属于行为型模式。

策略模式使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下具备不同实现。

应用场景

策略模式在生活中应用很多。比如一个人的胶水比例与他的工资有关,不同的工资水平对应不同的税率;再比如我们在下单前需要选择支付方式,比如支付宝、微信等。

策略模式可以解决在多种算法相似的情况下,避免使用if.else或switch…case带来的复杂性和臃肿性。在日常业务开发中,策略模式适用于以下场景:

  • 针对同一种类型问题,有多种处理方式,每一种都能独立解决问题。
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

首先看下策略模式的通用UML类图:

image-20211115164048789

从类图中,我们可以看到,策略模式主要包含三种角色:

  • 上下文角色(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

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!