策略模式Strategy
在我们开发过程中碰到最多的就是if...else,大家都知道这样会使的代码变得臃肿,维护成本增高。有的人改成case,其实这都是换汤不换药,并没有实际效果。而策略模式就可以比较好解决这类问题
策略模式官方定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
根据这个定义,可能无法领悟这个策略模式。接下来我们举一个典型的例子
商场商品打折:平时不打折,遇到节假日打九折,搞店庆活动的时候满200减1等类似活动。
在我们没有策略模式思想的时候,想到要实现这样的功能,大部分人是这样想的。定义一个方法A,里面传三个参数第一个参数type标识哪个活动,第二个参数金额money商品原价,在A方法中进行if或是case判断是走什么活动,分别调用不同活动的方法(各种活动封装不方法)。这还是好一点的,对于有些开发者更是直接在一个方法中各种判断最后得出最终金额数。咋一看这样也没有毛病,现在我们具体分析下策略模式是怎么实现的。
先定义一个抽象策略类,这个类是主要是用来结算总金额的。
public interface ResultStrategy {
double getResult(double total);
}
然后分别写具体的实现类,也叫具体策略角色
不打折:
public class NormalStrategy implements ResultStrategy {
@Override
public double getResult(double total) {
return total;
}
}
//打折:
public class DiscountStrategy implements ResultStrategy {
private double rate;
public DiscountStrategy(double rate) {
this.rate = rate;
}
@Override
public double getResult(double total) {
return total*rate;
}
}
//满减活动
public class ReBackStrategy implements ResultStrategy {
private double max;
private double reBackNum;
public ReBackStrategy(double max, double reBackNum) {
this.max = max;
this.reBackNum = reBackNum;
}
@Override
public double getResult(double total) {
if(total>max){
return total-reBackNum;
}
return total;
}
}
创建一个Context环境角色
public class ResultContext {
private ResultStrategy resultStrategy;
public ResultContext(ResultStrategy resultStrategy) {
this.resultStrategy = resultStrategy;
}
public ResultContext() {
}
public ResultStrategy getResultStrategy() {
return resultStrategy;
}
public void setResultStrategy(ResultStrategy resultStrategy) {
this.resultStrategy = resultStrategy;
}
public double getResult(double total){
return resultStrategy.getResult(total);
}
}
我们写个测试类测试一下调用:
public class TestMain {
public static void main(String[] args){
ResultContext context=new ResultContext(new NormalStrategy());
System.out.println(context.getResult(1000)); //不打折
context.setResultStrategy(new DiscountStrategy(0.8));
System.out.println(context.getResult(1000)); //打八折
context.setResultStrategy(new ReBackStrategy(200,1));
System.out.println(context.getResult(1000)); //满200减1
}
}
//打印结果
1000.0
800.0
999.0
写到这里一个策略模式就出来了,现在我们总结一些策略模式有哪些东西:
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
环境(Context)角色:持有一个Strategy的引用。
我们也可以总结出策略模式的优点:遵循了开闭原则。
开闭原则说的是 对修改关闭、对扩展开放。
对修改关闭就是不希望别人修改我们的代码,此路不通,对扩展开放就是希望别人以扩展的方式增加功能,策略模式把开闭原则体现得淋漓尽致。
当然缺点也显而易见:
随着你的策略增加,你的类也会越来越多。
所有的策略类都要暴露出去,所以如果你在实际开发中使用了策略模式,一定要记得写好文档让你的伙伴们知道已有哪些策略。就像 shiro 默认提供了三种验证策略,就必须在文档中写清楚,否则我们根本不知道如何使用。
所以使用策略模式我们需要注意:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
可能很多人觉得看完这个,懂是懂了这个模式但是很难运用,其实这个不必过于担心,首先我们需要学到这种思想,当你学会设计模式思想再去阅读一些框架源码你会豁然开朗,因为框架都是用了设计模式,你完全不懂这思想阅读起来肯定费劲。
实际工作中的运用:之前我写过一个公用poi解析类,提供jar进行调用,有的是word类型,有的是excel类型等,都是在调用的时候根据文件后缀名去判断,然后在相应创建对象进行解析。但是这个扩展性不好,当需求需要解析另一种类型的时候,就需要更改我封装的方法,这就违背了开闭原则,而且我提供的是jar包,调用者怎么修改?这时候应该怎么做?大家思考下。