【java设计模式】(7)---策略模式(案例解析)
策略模式
一、概念
1、理解策略模式
策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口
和 具体行为的实现
。策略模式最大的特点是行为的变化,行为之间可以相互替换。
每个if判断都可以理解为就是一个策略。
2、策略模式特点
策略模式把对象本身和行为区分开来,因此我们整个模式也分为三个部分。
1、抽象策略类(Strategy):策略的抽象,行为方式的抽象
2、具体策略类(ConcreteStrategy):具体的策略实现,每一种行为方式的具体实现。
3、环境类(Context):用来封装具体行为,操作策略的上下文环境。
3、举例理解(打车)
这里举个简单的例子,来理解开发中运用策略模式的场景。
有一个打车软件,现在有三种计费模式给 用户 选择,1、拼车 2、快车 3、豪车这个时候用户就是对象,三种计费方式就是行为,可以根据不同的行为计算不同不通的值。
1)传统实现方式
代码
/**
* @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来管理,这样才能做到真正的开闭原则。下面会在举例。
4、策略模式优缺点
优点
1)避免使用多重条件判断
如果没有策略模式,一个策略家族有多个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重if的条件语句?多重条件语句不易维护,而且出错的概率大大增强。
使用策略模式后,简化了操作,同时避免了条件语句判断。
2)扩展性良好
在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。
缺点
1)策略类数量增多
策略模式一个明显的缺点是当备用行为过多时,行为对象会非常庞大
5、策略模式运用场景
通过上面的优缺点我们可以很好的去思考,什么场景下可以考虑用策略模式?
我的理解就是:每个if判断都可以理解为就是一个策略。按理说都可以采用策略模式,但是如果if else里面的逻辑不多,且复用性很低,那就不需要。如果if里面的行为比较大
而且这些行为复用性比较高就可以考虑通过采用策略模式。
在我们生活中比较常见的应用模式有:
1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式
2、电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式
二、策略模式实战示例
最近正在做到电商项目,因为有多个活动,所以我就考虑用策略模式来实现。我们活动分为很多种满减送,包邮活动,限时折扣等等,这里大致写下对于多个活动如何去使用策略模式。
1、Order实体
活动是跟订单绑定在一起的,只有下了单才去计算这个订单走了哪个活动。
/**
* @Description: 订单实体
*/
public class Order {
/**
* 用户ID
*/
private Long userId;
/**
* 订单编号
*/
private String orderNumber;
/**
* 购买数量
*/
private Integer goodsNumber;
/**
* 订单运费
*/
private Double orderFreight;
/**
* 订单总价(订单价格 + 运费)
*/
private Double orderPrice;
/**
* 活动类型 1、包邮 2、满减送 3、限时折扣
*/
private String activityType;
/**
* 活动ID
*/
private String activityId;
//省略get set方法
2、ActivityStrategy(活动策略接口)
/**
* 定义一个总的活动抽象
*/
public interface ActivityStrategy {
/**
* 定义一个我们优惠活动的价格算法方法
*/
Order calculate (Order order);
}
3、具体活动策略实现类
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;
}
}
3、环境类
/**
* 负责和具体的策略类交互 动态的切换不同的算法
*/
@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() {
//1、包邮 2、满减送 3、限时折扣
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);
}
}
4、测试类
@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 来获取具体是哪个活动类型。
以后新添加一个活动,比如拼团活动,我们只需做两步
1、新建一个拼团活动策略类 实现总策略接口 2、activityStrategyMap中put这个折扣活动的bean实体。
除了map管理bean外,还有一种方式就是可以通过spring来管理这些具体的策略类
//freeShippingActivity 这个值可以在数据库添加一张表来管理,然后来读取 这样的话新建活动只需要做上面的第一步,代码更加好维护了。
ActivityStrategy payStrategy= SpringUtils.getBean("freeShippingActivity", ActivityStrategy.class);
这里只是列了个架构,实际开发中比这个复杂多了,因为可以同时选择多个活动,活动于活动之间又会有互斥关系。
参考
1、策略模式
```
别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。
攻我盾者,乃我内心之矛(8)。
```
【java设计模式】(7)---策略模式(案例解析)的更多相关文章
- Java设计模式1——策略模式(Strategy Pattern)
最近觅得一本好书<您的设计模式>,读完两章后就能断言,一定是一头极品屌丝写的,而且是专写给开发屌丝男的智慧枕边书,小女子就委屈一下,勉强看看,人笨,谁让他写得这么通俗易懂呢!为了加深理解, ...
- JAVA设计模式 之 策略模式
一. 定义 设计模式定义了算法族,分别封装起来,让他们之间可以互相替代,此模式让算法的变化独立于使用算法的客户(该定义来自于Head First 设计模式). 二. 应用场景 当我们在应用程序中完成一 ...
- Java设计模式之策略模式(一)
今年寒假没有回家,打算利用这个假期的时间进行学习设计模式,这一个人感觉比较牛的知识,前一段时间一直在忙着搞自己的专业课,还有就是捣鼓了一下Linux系统,没有好好的学习关于Java还有Android方 ...
- Java设计模式---Strategy策略模式
参考于 : 大话设计模式 马士兵设计模式视频 1.场景介绍 购物网站上有一个产品,有三个字段,档次,价格,重量. 有些同学喜欢轻的,有些手头紧,想要便宜的,有些喜欢档次高的. 那么我们为了提高网站用户 ...
- 折腾Java设计模式之策略模式
博客原文地址 简介 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式.简单理解就是一组算法,可以互换,再简单点策略就是封装算法. ...
- Java设计模式之策略模式(Strategy Pattern)
简介 策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 组成 1.抽象策略角色(Strategy): 策略类,通常由一个接口或者抽象类实现. 2.具 ...
- Java 设计模式之 策略模式
思维导图: 我们先来看 STRATEGY 设计模式的通俗解释: 跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,但目的都是为了得到 MM 的芳心, ...
- java设计模式之策略模式
策略模式 定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户(大话设计模式). 策略模式UML图 策略模式代码 古代的各种计谋都是一种策略,这次我们 ...
- Java设计模式之-----策略模式
首先,我们来看下策略模式的概念.一般的解释如下: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.(原文:The St ...
- java设计模式之——策略模式
1.策略模式有什么好处? 策略模式的好处在于你可以动态的改变对象的行为. 2.设计原则 设计原则是把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口(c++中可以用虚类),然后在类中包含这 ...
随机推荐
- kafka启动报错"A broker is already registered on the path /brokers/ids/1"解决方案
问题 kafka挂掉后,启动报错日志如下 [2020-03-19 17:50:58,123] FATAL Fatal error during KafkaServerStartable startup ...
- LeetCode#232-Implement Queue using Stacks-用栈实现队列
一.题目 使用栈实现队列的下列操作: push(x) -- 将一个元素放入队列的尾部. pop() -- 从队列首部移除元素. peek() -- 返回队列首部的元素. empty() -- 返回队列 ...
- 「每日五分钟,玩转JVM」:两种算法
前言 上篇文章,我们了解了GC 的相关概念,这篇文章我们通过两个算法来了解如何去确定堆中的对象实例哪些是我们需要去回收的垃圾对象. 引用计数算法 引用计数法的原理很简单,就是在对象中维护一个计数器,当 ...
- JAVA EE,JAVA SE,JAVA ME,JDK,JRE,JVM之间的区别
JAVA EE是开发企业级应用,主要针对web开发有一套解决方案. JAVA SE是针对普通的桌面开发和小应用开发. JAVA ME是针对嵌入式设备开发,如手机. JRE是程序的运行环境 JDK是程序 ...
- CSRF和XSS区别和预防
名词解释 CSRF(Cross-site request forgery)跨站请求伪造 XSS (Cross-site scripting)跨站脚本攻击,这里缩写css被前端层叠样式表(Cascadi ...
- CORS 跨域中的 preflight 请求
我们知道借助Access-Control-Allow-Origin响应头字段可以允许跨域 AJAX, 对于非简单请求,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求), ...
- 最小生成树的Prim算法以及Kruskal算法的证明
Prime算法的思路:从任何一个顶点开始,将这个顶点作为最小生成树的子树,通过逐步为该子树添加边直到所有的顶点都在树中为止.其中添加边的策略是每次选择外界到该子树的最短的边添加到树中(前提是无回路). ...
- JSP(一)----入门学习
## JSP 1.概念: * Java Server Pages:java服务端页面 * 可以理解为:一个特殊的页面,其中既可以直接定义html标签,又可以定义java代码 2.原理 * ...
- Javascript之盒子拖拽(跟随鼠标、边界限定、轨迹回放)
本文通过拖拽案例,实现"跟随鼠标.边界限定.轨迹回放"三大效果: 完整代码中有详尽注释,故不再进行细致讲解: 对于案例中需要注意的重点或易错点问题,会总结在最后. 效果图(仅演示左 ...
- Redis 按正则获取keys
首先,我被坑了很久由于不知道这个redis支持的正则只有3种 1. * 任意长度的任意字符 2. ? 任意单一字符 3. [xxx] 匹配方括号中的一个字符 2.从上面开来,keys的模糊匹配功能很方 ...