早想把23种常见的设计模式整理一遍,但总是各种原因导致不孕不育,今天正好需要讨论策略模式,那就从这个模式开始!所谓的设计模式都是前辈大牛整理出一套解决程序开发过程中特定问题的思路,于内功心法一样,千万不要只学招式,招式类似那些奇淫巧技的编程技巧,而要设计模式与招式并进,最终忘记模式,如同张三丰教张无忌太极拳一样。
促销里面的各种不同的促销行为最终会导致不同的结论,一种不好的处理方式就是使用ifelse,如果是促销行为足够多,这ifelse就成了灾难,每次修改新的处理方法都得修改原来代码,这也不符合对修改关闭对扩展开启的开闭原则。策略模式并不是处理策略,而是组织策略。
策略模式的UML
通过上图可以看出策略模式有以下角色构成:
- 1、抽象策略(Strategy)角色:抽象策略角色由抽象类或接口来承担,它给出具体策略角色需要实现的接口;
- 2、具体策略(ConcreteStrategy)角色:实现封装了具体的算法或行为;
- 3、上下文(Context)角色:持有抽象策略类的引用。
假设一个情况,订单最终形成订单总价,需要根据会员等级进行折扣计算,有一级会员,二级会员等等,每种会员等级不同折扣也不同,一级会员打八折,二级会员打九折,非会员不打折。此时如果按照ifelse的形式来是可以进行,但是涉及到下次增加一个特级会员就又得来修改这个ifelse。那么引入策略模式是不是就不需要写原来ifelse里面的业务逻辑呢?当然要写,只是组织方式不一样,以前是从头到尾直接铺成,现在使用策略模式就变成曲线救国了。
来段代码进行对比,先看下不采用模式进行:
public Double recharge(Double charge ,MemberEnumeration member ){
if(member.equals(MemberEnumeration.ONE)){
return charge*0.80; //一级会员8折
}else if(member.equals(MemberEnumeration.TWO)){
return charge*0.90; //二级会员9折
}else if(member.equals(MemberEnumeration.NONE)){
return charge; //非会员不打折
}else{
return null;
}
}
上面代码非常直白,貌似也非常容易理解,但是扩展就没有,如果增加特级,上面代码就得修改,违背开闭原则。
接下来采用策略模式来实现这个功能,首先会增加若干代码、若干类,甚至对初学者造成学习负担,但这些不是问题,我们的最终做项目做产品的满足随时随地的需求变动,这个重构是需要的。
按照之前的思路来先定义一个接口
public interface Strategy {
public Double recharge(Double charge ,MemberEnumeration member );
}
接下来做具体实现
public class OneClassMemberStrategy implements Strategy{
@Override
public Double recharge(Double charge, MemberEnumeration member) {
Assert(member.equals(MemberEnumeration.ONE));
return charge*0.8;
}
}
public class TwoClassMemberStrategy implements Strategy{
@Override
public Double recharge(Double charge, MemberEnumeration member) {
Assert(member.equals(MemberEnumeration.TWO));
return charge*0.9;
}
}
public class NoneClassMemberStrategy implements Strategy{
@Override
public Double recharge(Double charge, MemberEnumeration member) {
Assert(member.equals(MemberEnumeration.NONE));
return charge;
}
}
有了具体实现,可以造个工厂来生成这些具体策略,该步骤在这里不是必须的
public class StrategyFactory {
//单例
private static StrategyFactory factory = new StrategyFactory();
private StrategyFactory(){
}
private static Map<Integer ,Strategy> strategyMap = new HashMap<>();
static{
strategyMap.put(MemberEnumeration.ONE.value(), new OneClassMemberStrategy());
strategyMap.put(MemberEnumeration.TWO.value(), new TwoClassMemberStrategy());
strategyMap.put(MemberEnumeration.NONE.value(), new NoneClassMemberStrategy());
}
public Strategy creator(Integer type){
return strategyMap.get(type);
}
public static StrategyFactory getInstance(){
return factory;
}
}
有了这个工厂,我们来把上下文给弄出来
public class Context {
//依赖抽象
private Strategy strategy;
public Double recharge(Double charge, Integer type) {
//从工厂里面抓到对应的策略出来使用
strategy = StrategyFactory.getInstance().creator(type);
return strategy.recharge(charge, MemberEnumeration.valueOf(type));
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
最终我们再客户端调用的时候直接使用具体的策略和上下文即可,具体的策略注入到上下文
Context context = new Context();
//一级会员买了100元的东西,得到最终价格80
Double money = context.recharge(100D,MemberEnumeration.ONE.value());
System.out.println(money); //80
这样走了一大段,貌似比原来的复杂了很多,这有意义吗?还是有意义的,首先这次不再是n多的ifelse嵌套了,新增一个会员等级直接实现接口把该等级的会员价格计算,使用的时候,还是得修改几个地方,第一是工厂类里面增加新的会员实例化工作,这个可以利用反射(关于反射后面再讨论)获得实例。第二是客户端还得修改啊,其实真正的客户端的信息可以从数据库获取,作为入参进行处理,这样可以完全可扩展,只增加不修改。
网友评论