超级链接: Java常用设计模式的实例学习系列-绪论
参考:《HeadFirst设计模式》
策略模式是一种行为
模式。
策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
如果某种行为拥有多种运作方式,那么把这种行为视为一种策略,把这些运作方式视为策略实现,这些策略实现之间可以相互替换。
这样,行为调用方就可以在运行时,根据不同的需求,将相应的策略实现当做参数,以达到行为按照某种方式运作的目的。
本文以购物车的支付策略
为场景来学习策略模式
:
由于本文的关注对象是设计模式,所以这里直接列出商品的代码。
商品:Goods
/**
* 商品
*
* @author hanchao
*/
@Setter
@Getter
@AllArgsConstructor
public class Goods {
/**
* 商品名称
*/
private String name;
/**
* 商品价格
*/
private Float price;
}
购物车: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元.
优点:
实现简单
:代码逻辑很简单,并且只需要编写一个类即可。缺点:
扩展性差
,每增减一种支付方式,都需要修改OrdinaryShoppingCart
,违背了设计模式的开放-封闭原则(OCP,对扩展开放,对修改关闭)。可复用低
,每种支付方式都采用不同的方法命名。策略模式
的关键在于:将行为及其运作方式抽象为策略和策略实现。对于本场景而言,
支付
就是一种行为,可以将其视为一种策略,即:支付策略。支付宝支付
和微信支付
都是支付
行为的不同运作方式,可以分别将其视为一种策略实现。支付策略: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
的成员变量,如此才能承载不同的策略实现。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元.
优点:
缺点:
HystrixCommand熔断器的默认隔离侧率是线程池隔离
,可以通过setter
方法修改为信号量隔离
,代码如下:
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroupKey"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
//选择隔离策略:信号量
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
)
);
最后以UML类图来总结本文的购物车付款场景以及策略模式。