一、概念
1、什么是策略模式
策略模式是一种行为型模式,它将对象和行为分开,将行为定义为一个行为接口****和具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。
2、策略模式特点
策略模式把对象本身和行为区分开来,因此我们整个模式也分为三个部分。
1、抽象策略类(Strategy):策略的抽象,行为方式的抽象
2、具体策略类(ConcreteStrategy):具体的策略实现,每一种行为方式的具体实现。
3、环境类(Context):用来封装具体行为,操作策略的上下文环境。
3、以打车举例理解策略模式的运用
一个打车软件,现在有三种计费模式给 用户 选择,1、拼车 2、快车 3、豪车这个时候用户就是对象,三种计费方式就是行为,可以根据不同的行为计算不同不通的费用。
1)常规的实现方式
通常我们会使用if else语句进行判断车辆的类型,然后根据对应类型的计费规则计算车费。
/**
* @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(),也不符合设计模式的开闭原则,会导致后期的维护性非常差。
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));
}
4、策略模式优缺点
优点:
1)避免使用多重条件判断
如果没有策略模式,一个策略家族有多个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重if的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,简化了操作,同时避免了条件语句判断。
2)扩展性良好
在现有的系统中增加一个策略太容易了只要实现接口就可以,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。
缺点:
1)策略类数量增多
策略模式一个明显的缺点是当备用行为过多时,行为对象会非常庞大。
5、策略模式运用场景
我们可以将每个if判断理解为一个策略,但是如果if else里面的逻辑不多,并且复用性很低,那就不要使用策略模式。如果if里面的行为比较大而且这些行为复用性比较高就可以考虑通过采用策略模式。
实际生活中常见使用策略模式的场景:
1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式
2、电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式
6、打车场景使用策略模式结合工厂模式解决if else问题
定义策略接口:
/**
* 出行策略接口
*/
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;
}
}
定义工厂类,负责创建实现策略接口的类,使用Map存放策略实现类,避免过多的if else逻辑。
public class CalculateFactory {
private Map<String, PriceStrategy> map = new HashMap<>();
{
map.put("PcStrategy", new PcStrategy());
map.put("KcStrategy", new KcStrategy());
map.put("HcStrategy", new HcStrategy());
}
public PriceStrategy creat(String carType) {
return map.get(carType);
}
}
测试:
public Double calculatePrice(String carType) {
CalculateFactory factory = new CalculateFactory();
factory.creat(carType).countPrice(originalPrice);
}