设计模式-策略模式

早想把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嵌套了,新增一个会员等级直接实现接口把该等级的会员价格计算,使用的时候,还是得修改几个地方,第一是工厂类里面增加新的会员实例化工作,这个可以利用反射(关于反射后面再讨论)获得实例。第二是客户端还得修改啊,其实真正的客户端的信息可以从数据库获取,作为入参进行处理,这样可以完全可扩展,只增加不修改。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容