设计模式(四):策略模式

定义

策略模式(StrategyPattern)是指定义了算法家族、分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。

应用场景

1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
2、一个系统需要动态地在几种算法中选择一种。

优点

1、策略模式符合开闭原则。
2、避免使用多重条件转移语句,如if…else…语句、switch语句
3、使用策略模式可以提高算法的保密性和安全性。

缺点

1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2、代码中会产生非常多策略类,增加维护难度。


下面请看案例
模拟一个场景,某商场正在进行活动,活动分为了三种,返现、团购、折扣。客户可以自行选择哪一种优惠。

1.定义一个公共的策略抽象

//优惠策略抽象
public interface PromotionStrategy {
    //优惠活动,公共行为
    void doPromotion();
}

2.分别写出上面三种策略具体行为。

//优惠券活动
public class CouponStrategy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("领取优惠券,所有商品8折优惠");
    }
}
//返现活动
public class CashbackStrtegy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("购满200元,返现20元到支付账号");
    }
}
//团购活动
public class GroupbuyStrategy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("团购活动,满10人享受团购价");
    }
}
//无优惠
public class EmptyStrategy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("没有活动力度!");
    }
}

3.定义活动规则并发布

//活动规则
public class PromotionActivity {

    private PromotionStrategy promotionStrategy;
    
    public PromotionActivity(PromotionStrategy promotionStrategy){
        this.promotionStrategy = promotionStrategy;
    }
    //发布活动
    public void execute(){
        this.promotionStrategy.doPromotion();
    }
}

现在展示两种不同的写法来比较一下。
测试
1.过去使用的写法,首先创建一个对象在,在构造函数中传递一个优惠类型来指定某种优惠。这种显然是最不提倡的。

	//一般写法
    public static void main(String[] args) {
        //优惠券活动
        PromotionActivity activity = new PromotionActivity(new CouponStrategy());
        activity.execute();
        //返现活动
        PromotionActivity activity1 = new PromotionActivity(new CashbackStrtegy());
        activity1.execute();
    }
//输出结果:
领取优惠券,所有商品8折优惠
购满200元,返现20元到支付账号

由于活动的选择是根据不同的需求的对促销策略进行动态选择的,并不会一次执行多个优惠,所以上面那种情况在实际场景中几乎用不着,我们一般会使用以下这种写法。

2.客户选择策略

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 CashbackStrtegy());
        }
        promotionActivity.execute();
    }
 //输出结果
 //领取优惠券,所有商品8折优惠

这样改写之后,我们满足了业务需求,可以可以根据自己的需求选择不同的优惠策略了。但是也并不是提倡的选择它也存在弊端:随着时间的业务累计,我们的促销活动会越来越多,于是我们每次增加活动时都得不停的修改逻辑和测试,判断的逻辑也会越来越复杂。
如何解决?------》结合单例和工厂模式来解决:有兴趣的朋友可以了解一下本分类下的文章(单例:单例 | 工厂:工厂

定义一个工厂类,在这里我就不定义接口了,直接写类来模拟
策略工厂PromotionStrategyFactory 类

public class PromotionStrategyFactory {

    //注册式单例
    private static Map<String,PromotionStrategy> PROOTION_STRATEGY_MAP = new HashMap<>();
    
    //模拟加载资源
    static {
        PROOTION_STRATEGY_MAP.put(PromotionKey.COUPON,new CouponStrategy());
        PROOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK,new CashbackStrtegy());
        PROOTION_STRATEGY_MAP.put(PromotionKey.GROUPBUY,new GroupbuyStrategy());
    }
    //构造方法
    private PromotionStrategyFactory(){

    }
    //无活动对象
    private static final  PromotionStrategy NON_PROMOTION = new EmptyStrategy();
    //定义活动类型
    private interface  PromotionKey{
        String COUPON = "COUPON";
        String CASHBACK = "CASHBACK";
        String GROUPBUY = "GROUPBUY";
    }
    //提供对外方法
    public static PromotionStrategy getPromotionStrategy(String promotionkey){
        return  promotionkey == null || promotionkey.equals("") ? NON_PROMOTION: PROOTION_STRATEGY_MAP.get(promotionkey);
    }
}

测试代码

public static void main(String[] args) {
        String promotionKey = "GROUPBUY";
        PromotionActivity promotionActivity =
                new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
        promotionActivity.execute();
        //增加一个优惠
    }

这样优化了之后,每次增加新的活动不会在影响原来的代码逻辑。只需要工厂中进行添加即可。

再来模拟一个在实际开发中遇到的场景。

比如我们在买东西时候的支付时候得选择,可以选择支付宝、微信、京东、银联等方法来选择付款方式,下面我们就来模拟一下此类场景
1.定义一个抽象的类表示是哪一种付款类型对象(支付宝或微信还是京东又或者银联。 - -!不想落下其中一个,显得孤独。)
疑问:为什么定义成抽象类?
答:因为不管是上述其中任何一种支付类型,他们对于顾客来说支付类型都是相似的。扫码->余额是否足够->成功或者失败所以他们是一个公共的动作和相似的逻辑。因此在这定义成抽象类来约束规范和逻辑

//Payment 抽象类。
public abstract class Payment {
    //抽象方法,支付类型
    public abstract String getName();
    //账户余额,方法根据uid查询对应账户余额,在这里就简写了。
    protected abstract double quetyBalance(String uid);
    //支付方法
    public MsgResult pay(String uid,double amount){
        if(quetyBalance(uid) < amount){
            return new MsgResult(500,"支付失败","余额不足");
        }else {
            return new MsgResult(200,"支付成功","支付金额:"+amount);
        }
    }
}

2.分别列出上述支付方法继承抽象类。

public class AliPay extends Payment{
    @Override
    public String getName() {
        return "支付宝";
    }
    @Override
    protected double quetyBalance(String uid) {
        //表示支付宝里只有400块钱
        return 400;
    }
}
public class JDPay extends  Payment{
    @Override
    public String getName() {
        return "京东白条";
    }

    @Override
    protected double quetyBalance(String uid) {
        return 500;
    }
}
public class WechatPay extends Payment{
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    protected double quetyBalance(String uid) {
        return 300;
    }
}
public class UnionPay extends Payment{
    @Override
    public String getName() {
        return "银联支付";
    }

    @Override
    protected double quetyBalance(String uid) {
        return 200;
    }
}

按照第一个案例中来创建一个策略管理类

//支付策略管理类
public class PayStrategy {
    
    //支付种类
    public static final String ALI_PAY = "Alipay";
    public static final String JD_PAY = "JDpay";
    public static final String WECHAT_PAY = "Wechatipay";
    public static final String UNION_PAY = "UNIONpay";
    public static final String DEFAULT_PAY = "Alipay";
    
    //容器
    private static Map<String,Payment> payStrategy = new HashMap<>();
    //加载资源
    static {
        payStrategy.put(ALI_PAY,new AliPay());
        payStrategy.put(JD_PAY,new JDPay());
        payStrategy.put(WECHAT_PAY,new WechatPay());
        payStrategy.put(UNION_PAY,new UnionPay());
    }

    //通过key选择策略
    public static Payment get(String payKey){
        //使用默认策略
        if(!payKey.contains(payKey)){
            return payStrategy.get(DEFAULT_PAY);
        }
        return payStrategy.get(payKey);
    }
}

临时定义返回结果类

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 "支付状态---->" +
                "状态码:" + code +
                ", 支付金额:" + data +
                ", 支付结果:"+ msg  ;
    }
}

创建一个订单类模拟支付

//订单
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(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","20200407",499.6);

        System.out.println(order.pay(PayStrategy.ALI_PAY));

        System.out.println(order.pay(PayStrategy.JD_PAY));
    }
}

测试代码结果

欢迎使用【支付宝】
本次交易:499.6, 开始扣款...
支付状态---->状态码:500, 支付金额:余额不足, 支付结果:支付失败
欢迎使用【京东白条】
本次交易:499.6, 开始扣款...
支付状态---->状态码:200, 支付金额:支付金额:499.6, 支付结果:支付成功

我们来看一看类图
在这里插入图片描述
到此本篇就介绍介绍了,希望各位看过的伙伴们如果发现了问题能够及时批评指正,在此感谢。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈橙橙丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值