设计模式-策略模式-以购物车的支付策略为例

超级链接: Java常用设计模式的实例学习系列-绪论

参考:《HeadFirst设计模式》


1.关于策略模式

策略模式是一种行为模式。

策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

如果某种行为拥有多种运作方式,那么把这种行为视为一种策略,把这些运作方式视为策略实现,这些策略实现之间可以相互替换。

这样,行为调用方就可以在运行时,根据不同的需求,将相应的策略实现当做参数,以达到行为按照某种方式运作的目的。

本文以购物车的支付策略为场景来学习策略模式

  • 每个商品都有名称和价钱。
  • 购物车可以添加多个商品。
  • 购物车支付方式暂时只支持支付宝和微信,但是后期可能会增减更多支付方式。

2.关于商品

由于本文的关注对象是设计模式,所以这里直接列出商品的代码。

商品:Goods

/**
 * <p>商品</P>
 *
 * @author hanchao
 */
@Setter
@Getter
@AllArgsConstructor
public class Goods {
    /**
     * 商品名称
     */
    private String name;

    /**
     * 商品价格
     */
    private Float price;
}

3.不采用策略模式的购物车

3.1.代码

购物车:OrdinaryShoppingCart

/**
 * <p>购物车(普通版本)</P>
 *
 * @author hanchao
 */
@Slf4j
public class OrdinaryShoppingCart {
    /**
     * 商品列表
     */
    private List<Goods> goodsList;

    public OrdinaryShoppingCart() {
        goodsList = new ArrayList<>();
    }

    /**
     * 添加商品
     */
    public OrdinaryShoppingCart addGoods(Goods goods) {
        goodsList.add(goods);
        return this;
    }

    /**
     * 计算总价
     */
    public float totalCost() {
        return goodsList.stream().map(Goods::getPrice).reduce(Float::sum).orElse(0f);
    }

    /**
     * 通过支付宝支付
     */
    public void payWithAlipay() {
        log.info("通过支付宝支付了" + totalCost() + "元.");
    }

    /**
     * 通过微信支付
     */
    public void payWithWeChat() {
        log.info("通过微信支付了" + totalCost() + "元.");
    }
}
  • 因为有多种支付方式,所以每种支付方式分表编写了一个方法。

测试代码:ShoppingCartDemo

/**
 * <p>购物车场景测试</P>
 *
 * @author hanchao
 */
public class ShoppingCartDemo {
    public static void main(String[] args) {
        //购物车(普通版本)
      	//张三的购物行为
        new OrdinaryShoppingCart()
                .addGoods(new Goods("一箱牛奶", 34.55f))
                .addGoods(new Goods("一瓶白酒", 250.50f))
                .payWithAlipay();

      	//李四的购物行为
        new OrdinaryShoppingCart()
                .addGoods(new Goods("一箱牛奶", 34.55f))
                .addGoods(new Goods("一瓶啤酒", 3.50f))
                .payWithWeChat();
    }
}

运行结果:

2019-07-02 15:53:48,424  INFO [main-1] pers.hanchao.designpattern.strategy.ordinary.OrdinaryShoppingCart:44 - 通过支付宝支付了285.05. 
2019-07-02 15:53:48,429  INFO [main-1] pers.hanchao.designpattern.strategy.ordinary.OrdinaryShoppingCart:51 - 通过微信支付了38.05. 

3.2.分析

优点:

  • 实现简单:代码逻辑很简单,并且只需要编写一个类即可。

缺点:

  • 扩展性差,每增减一种支付方式,都需要修改OrdinaryShoppingCart,违背了设计模式的开放-封闭原则(OCP,对扩展开放,对修改关闭)。
  • 可复用低,每种支付方式都采用不同的方法命名。

4.采用策略模式的购物车

4.1.代码

策略模式的关键在于:将行为及其运作方式抽象为策略和策略实现。对于本场景而言,

  • 支付就是一种行为,可以将其视为一种策略,即:支付策略。
  • 支付宝支付微信支付都是支付行为的不同运作方式,可以分别将其视为一种策略实现。

支付策略:PayStrategy

/**
 * <p>支付策略</P>
 *
 * @author hanchao
 */
public interface PayStrategy {
    /**
     * 支付
     *
     * @param cost 支付金额
     */
    void pay(Float cost);
}

支付策略实现一:支付宝

/**
 * <p>支付宝支付策略</P>
 *
 * @author hanchao
 */
@Slf4j
public class AliPayStrategy implements PayStrategy {
    @Override
    public void pay(Float cost) {
        log.info("通过支付宝支付了" + cost + "元.");
    }
}

支付策略实现二:微信

/**
 * <p>微信支付策略</P>
 *
 * @author hanchao
 */
@Slf4j
public class WeChatPayStrategy implements PayStrategy {
    @Override
    public void pay(Float cost) {
        log.info("通过微信支付了" + cost + "元.");
    }
}

购物车(策略模式版本):StrategyShoppingCart

/**
 * <p>购物车(策略模式版本)</P>
 *
 * @author hanchao
 */
@Slf4j
public class StrategyShoppingCart {

    /**
     * 商品列表
     */
    private List<Goods> goodsList;

    /**
     * 支付策略
     */
    private PayStrategy payStrategy;

    public StrategyShoppingCart() {
        goodsList = new ArrayList<>();
    }

    /**
     * 添加商品
     */
    public StrategyShoppingCart addGoods(Goods goods) {
        goodsList.add(goods);
        return this;
    }

    /**
     * 计算总价
     */
    public float totalCost() {
        return goodsList.stream().map(Goods::getPrice).reduce(Float::sum).orElse(0f);
    }

    /**
     * 选择支付策略
     */
    public StrategyShoppingCart setPayStrategy(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
        return this;
    }

    /**
     * 支付
     */
    public void pay() {
        if (Objects.isNull(payStrategy)) {
            throw new IllegalStateException("未选择支付策略");
        } else {
            payStrategy.pay(totalCost());
        }
    }
}
  • 策略(抽象类/接口)PayStrategy作为客户端StrategyShoppingCart的成员变量,如此才能承载不同的策略实现。
  • 提供策略实现的setter方法setPayStrategy(PayStrategy payStrategy),为策略实现的切换提供入口。

测试代码:ShoppingCartDemo

/**
 * <p>购物车场景测试</P>
 *
 * @author hanchao
 */
public class ShoppingCartDemo {
    public static void main(String[] args) {
        //购物车(策略模式版本)
        //王五的购物行为
        new StrategyShoppingCart()
                .addGoods(new Goods("一箱牛奶", 34.55f))
                .addGoods(new Goods("一瓶白酒", 250.50f))
                .setPayStrategy(new AliPayStrategy()).pay();

        //赵六的购物行为
        new StrategyShoppingCart()
                .addGoods(new Goods("一箱牛奶", 34.55f))
                .addGoods(new Goods("一瓶啤酒", 3.50f))
                .setPayStrategy(new WeChatPayStrategy()).pay();
    }
}

运行结果:

2019-07-02 17:47:07,309  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.AliPayStrategy:15 - 通过支付宝支付了285.05. 
2019-07-02 17:47:07,310  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.WeChatPayStrategy:15 - 通过微信支付了38.05. 

新需求:新的支付方式:银联支付

当新的需求产生,比方说,新增一种支付方式:银联支付。

此时,只需要新增一种支付策略实现UnionPayStrategy即可,无需修改原有代码。

新的支付策略实现三:UnionPayStrategy

/**
 * <p>银联支付策略</P>
 *
 * @author hanchao
 */
@Slf4j
public class UnionPayStrategy implements PayStrategy {
    @Override
    public void pay(Float cost) {
        log.info("通过银联支付了" + cost + "元.");
    }
}

测试代码:ShoppingCartDemo

/**
 * <p>购物车场景测试</P>
 *
 * @author hanchao
 */
public class ShoppingCartDemo {
    public static void main(String[] args) {
        //购物车(策略模式版本)
        //孙七的购物行为
        new StrategyShoppingCart()
                .addGoods(new Goods("一箱牛奶", 34.55f))
                .addGoods(new Goods("一双袜子", 23.50f))
                .setPayStrategy(new UnionPayStrategy()).pay();
    }
}

运行结果:

2019-07-02 17:55:51,102  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.UnionPayStrategy:15 - 通过银联支付了58.05. 

4.2.分析

优点:

  • 扩展性好,每增减一种支付方式,只需增减一种支付策略实现即可,无需修改OrdinaryShoppingCart,遵从了设计模式的开放-封闭原则(OCP,对扩展开放,对修改关闭)。
  • 可复用高,每种支付方式都采用相同的方法命名。

缺点:

  • 实现复杂:如果有n种策略,则需要n个策略实现类,再加一个策略类。

5.实际案例

HystrixCommand熔断器的默认隔离侧率是线程池隔离,可以通过setter方法修改为信号量隔离,代码如下:

		super(Setter
			.withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroupKey"))
                .andCommandPropertiesDefaults(
                	HystrixCommandProperties.Setter()
                    	//选择隔离策略:信号量
						.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                ) 
       	);

6.总结

最后以UML类图来总结本文的购物车付款场景以及策略模式。

在这里插入图片描述

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值