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

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

参考:《HeadFirst设计模式》


1.关于策略模式

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

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

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

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

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

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

2.关于商品

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

商品:Goods

/**
 * 

商品

* * @author hanchao */
@Setter @Getter @AllArgsConstructor public class Goods { /** * 商品名称 */ private String name; /** * 商品价格 */ private Float price; }

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

3.1.代码

购物车:OrdinaryShoppingCart

/**
 * 

购物车(普通版本)

* * @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

/**
 * 

购物车场景测试

* * @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

/**
 * 

支付策略

* * @author hanchao */
public interface PayStrategy { /** * 支付 * * @param cost 支付金额 */ void pay(Float cost); }

支付策略实现一:支付宝

/**
 * 

支付宝支付策略

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

支付策略实现二:微信

/**
 * 

微信支付策略

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

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

/**
 * 

购物车(策略模式版本)

* * @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

/**
 * 

购物车场景测试

* * @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

/**
 * 

银联支付策略

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

测试代码:ShoppingCartDemo

/**
 * 

购物车场景测试

* * @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类图来总结本文的购物车付款场景以及策略模式。

设计模式-策略模式-以购物车的支付策略为例_第1张图片

你可能感兴趣的:(Java设计模式)