策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口
和 具体行为的实现
。策略模式最大的特色是行为的变化,行为之间能够相互替换。java
每一个if判断均可以理解为就是一个策略。git
策略模式把对象自己和行为区分开来,所以咱们整个模式也分为三个部分。github
一、抽象策略类(Strategy):策略的抽象,行为方式的抽象 二、具体策略类(ConcreteStrategy):具体的策略实现,每一种行为方式的具体实现。 三、环境类(Context):用来封装具体行为,操做策略的上下文环境。
这里举个简单的例子,来理解开发中运用策略模式的场景。算法
有一个打车软件,如今有三种计费模式给 用户 选择,一、拼车 二、快车 三、豪车这个时候用户就是对象,三种计费方式就是行为,能够根据不一样的行为计算不一样不通的值。spring
1)传统实现方式数据库
代码
segmentfault
/** * @Description: 这里只展现计费最终费用示例 * * @param type 计费类型 * @param originalPrice 原始价格 */ public Double calculationPrice(String type, Double originalPrice) { //拼车计费 if (type.equals("pc")) { return originalPrice * 0.5; } //快车计费 if (type.equals("kc")) { return originalPrice * 1; } //豪车计费 if (type.equals("hc")) { return originalPrice * 2; } return originalPrice; }
传统的实现方式,经过传统if代码判断。这样就会致使后期的维护性很是差。当后期须要新增计费方式,还须要在这里再加上if(),也不符合设计模式的开闭原则。设计模式
2)策略模式实现微信
抽象策略类
架构
/** * 出行策略接口 */ public interface PriceStrategy { /** * @param originalPrice 原始价格 * @return 计算后的价格 */ Double countPrice(Double originalPrice); }
具体策略实现类
/** * @Description: 拼车的计费方式 */ public class PcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 0.5; } } /** * @Description: 快车的计费方式 */ public class KcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 1; } } /** * @Description: 拼车的计费方式 */ public class HcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 2; } }
环境类
也叫作上下文类或环境类,起承上启下封装做用。
/** * 负责和具体的策略类交互 * 这样的话,具体的算法和直接的客户端调用分离了,使得算法能够独立于客户端独立的变化。 * 若是使用spring的依赖注入功能,还能够经过配置文件,动态的注入不一样策略对象,动态的切换不一样的算法. */ public class PriceContext { /** * 出行策略接口 */ private PriceStrategy riceStrategy; /** * 构造函数注入 */ public PriceContext(PriceStrategy riceStrategy) { this.riceStrategy = riceStrategy; } /** * 计算价格 */ public Double countPrice(Double originalPrice) { return riceStrategy.countPrice(originalPrice); } }
测试类
public static void main(String[] args) { //具体行为策略 PriceStrategy pcStrategy = new PcStrategy(); PriceStrategy kcStrategy = new KcStrategy(); PriceStrategy hcStrategy = new HcStrategy(); //用户选择不一样的策略 PriceContext pcContext = new PriceContext(pcStrategy); PriceContext kcContext = new PriceContext(kcStrategy); PriceContext hcContext = new PriceContext(hcStrategy); System.out.println("拼车价格 = " + pcContext.countPrice(10D)); System.out.println("快车价格 = " + kcContext.countPrice(10D)); System.out.println("豪车价格 = " + hcContext.countPrice(10D)); }
运行结果
拼车价格 = 5.0 快车价格 = 10.0 豪车价格 = 20.0
整理流程就是这个样的,这里有一点须要注意 我在作测试的时候具体策略对象都是new出来,而实际运用应该经过spring来管理,这样才能作到真正的开闭原则。下面会在举例。
优势
1)避免使用多重条件判断
若是没有策略模式,一个策略家族有多个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重if的条件语句?多重条件语句不易维护,并且出错的几率大大加强。
使用策略模式后,简化了操做,同时避免了条件语句判断。
2)扩展性良好
在现有的系统中增长一个策略太容易了,只要实现接口就能够了,其余都不用修改,相似于一个可反复拆卸的插件,这大大地符合了OCP原则。
缺点
1)策略类数量增多
策略模式一个明显的缺点是当备用行为过多时,行为对象会很是庞大
经过上面的优缺点咱们能够很好的去思考,什么场景下能够考虑用策略模式?
个人理解就是:每一个if判断均可以理解为就是一个策略。按理说均可以采用策略模式,可是若是if else里面的逻辑很少,且复用性很低,那就不须要。若是if里面的行为比较大
并且这些行为复用性比较高就能够考虑经过采用策略模式。
在咱们生活中比较常见的应用模式有:
一、电商网站支付方式,通常分为银联、微信、支付宝,能够采用策略模式 二、电商网站活动方式,通常分为满减送、限时折扣、包邮活动,拼团等能够采用策略模式
最近正在作到电商项目,由于有多个活动,因此我就考虑用策略模式来实现。咱们活动分为不少种满减送,包邮活动,限时折扣等等,这里大体写下对于多个活动如何去使用策略模式。
活动是跟订单绑定在一块儿的,只有下了单才去计算这个订单走了哪一个活动。
/** * @Description: 订单实体 */ public class Order { /** * 用户ID */ private Long userId; /** * 订单编号 */ private String orderNumber; /** * 购买数量 */ private Integer goodsNumber; /** * 订单运费 */ private Double orderFreight; /** * 订单总价(订单价格 + 运费) */ private Double orderPrice; /** * 活动类型 一、包邮 二、满减送 三、限时折扣 */ private String activityType; /** * 活动ID */ private String activityId; //省略get set方法
/** * 定义一个总的活动抽象 */ public interface ActivityStrategy { /** * 定义一个咱们优惠活动的价格算法方法 */ Order calculate (Order order); }
FreeShippingActivity
包邮
/** * @Description: 包邮活动 */ @Component @Service("freeShippingActivity") public class FreeShippingActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //包邮活动是一个大的主题 ,里面能够建立不少小活动 好比价格满100包邮活动,或者满2件以上包邮活动,江浙沪包邮活动等等 //若是这里经过活动ID获取用户具体选择了哪个活动。 String activityId = order.getActivityId(); //查询数据库 System.out.println("模拟查询数据库 ,当前的活动是满100包邮"); order.setOrderFreight(0.0D); return order; } }
FullDeliveryActivity
(满减送活动)
/** * @Description: 满减送活动 */ @Component @Service("fullDeliveryActivity") public class FullDeliveryActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //若是这里经过活动ID获取用户具体选择了哪个活动。 String activityId = order.getActivityId(); //查询数据库 System.out.println("模拟查询数据库 ,当前的活动是满100减20的"); if (order.getOrderPrice() > 100) { order.setOrderPrice(order.getOrderPrice() - 20); } return order; } }
LimitDiscountActivity
(限时折扣活动)
/** * @Description: 限时折扣活动 */ @Component @Service("limitDiscountActivity") public class LimitDiscountActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //若是这里经过活动ID获取用户具体选择了哪个活动。 String activityId = order.getActivityId(); //查询数据库 System.out.println("模拟查询数据库 ,当前的活动是截至2020.10.1前 打9折"); order.setOrderPrice(order.getOrderPrice() * 0.9); return order; } }
/** * 负责和具体的策略类交互 动态的切换不一样的算法 */ @Component public class ActivityContext { @Autowired private FreeShippingActivity freeShippingActivity; @Autowired FullDeliveryActivity fullDeliveryActivity; @Autowired LimitDiscountActivity limitDiscountActivity; /** * 出行策略接口 */ private final Map<String, ActivityStrategy> activityStrategyMap = new HashMap(); /** * 初始化 把这几个活动的示例 初始化的时候就装到一个map集合中 */ @PostConstruct public void init() { //一、包邮 二、满减送 三、限时折扣 activityStrategyMap.put("1", freeShippingActivity); activityStrategyMap.put("2", fullDeliveryActivity); activityStrategyMap.put("3", limitDiscountActivity); } /** * 计算价格 */ public Order calculate(Order order) { ActivityStrategy activityStrategy = activityStrategyMap.get(order.getActivityType()); return activityStrategy.calculate(order); } }
@RestController public class PayController { @Autowired private ActivityContext activityContext; @RequestMapping("/test") public Object test(){ Order order = new Order(); //1 表明包邮 order.setActivityType("1"); //具体活动ID order.setActivityId("12"); //总价 order.setOrderPrice(200D); //运费 order.setOrderFreight(10D); return activityContext.calculate(order); } } //输出: 模拟查询数据库 ,当前的活动是包邮
总结
这里咱们没有用到if else来判断用户到底选择了哪一个活动类型,而是经过先把全部活动的bean实体装入一个map中,而后经过activityType 来获取具体是哪一个活动类型。
之后新添加一个活动,好比拼团活动,咱们只需作两步
一、新建一个拼团活动策略类 实现总策略接口 二、activityStrategyMap中put这个折扣活动的bean实体。
除了map管理bean外,还有一种方式就是能够经过spring来管理这些具体的策略类
//freeShippingActivity 这个值能够在数据库添加一张表来管理,而后来读取 这样的话新建活动只须要作上面的第一步,代码更加好维护了。 ActivityStrategy payStrategy= SpringUtils.getBean("freeShippingActivity", ActivityStrategy.class);
这里只是列了个架构,实际开发中比这个复杂多了,由于能够同时选择多个活动,活动于活动之间又会有互斥关系。
一、策略模式
别人骂我胖,我会生气,由于我内心认可了我胖。别人说我矮,我就会以为可笑,由于我内心知道我不可能矮。这就是咱们为何会对别人的攻击生气。 攻我盾者,乃我心里之矛(8)。