1.策略模式的定义: 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 2.策略模式举例: 先举个简单的例子:不通年纪的人买保险的需求不一样,买保险的时候一般会根据人的情况来买的。给儿童买保险是为了保护他快乐成长,给中年人买保险是为了财产安全,给老年人买保险是为了生病时候不会损失过大。 面对的情况,如果我们把所有的打折可能性都写到 if/else 里面,那只要有新的人群需求(比如青年人群)出现我们就会去改动这块的代码,我们都知道只要修改代码就有可能会引入新的问题,为了避免修改代码带来新的问题 我们可以使用策略模式来解决。
1.策略模式的结构图 Strategy: 抽象策略类,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。 ConcreteStrategy: 具体的策略实现,也就是具体的算法实现。 Context: 上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现。 2.使用策略模式编写上面买保险的案例 先定义一个接口,这个接口就是抽象策略类,该接口定义了买保险的方法,具体的实现由具体的策略类来完成.
package com.i2fly.strategy; //策略接口 public interface InsuredStrategy { //定义的抽象算法方法 来约束具体的算法实现方法 买保险 public void buyInsures(); }针对不同的人群,定义三种具体的策略类来买保险,以下几个类体现了封装变化的设计原则,不同的人群有不同的买保险策略,它们之间相互没有影响。
package com.i2fly.strategy; public class ChildInsuredStrategy implements InsuredStrategy { // 儿童购买保险的具体方法 @Override public void buyInsures() { System.out.println("购买儿童保险,保障儿童快乐成长。。。"); } } package com.i2fly.strategy; public class MiddleAgedInsuredStrategy implements InsuredStrategy{ //中年人购买保险的具体方法 @Override public void buyInsures() { System.out.println("中年人购买保险,保障家庭财产安全。。。。"); } } package com.i2fly.strategy; public class OldInsuredStrategy implements InsuredStrategy { //老年人购买保险的具体方法 @Override public void buyInsures() { System.out.println("老年人购买保险,保障身体生病花钱少。。。。"); } }上下文定义
package com.i2fly.strategy; //策略上下文 public class InsuredStrategyContext { //持有一个策略实现的引用 private InsuredStrategy insuredStrategy; public void excuteBuyInsures(){ //调用策略实现的方法 这里执行买保险的动作 insuredStrategy.buyInsures(); } public InsuredStrategyContext(InsuredStrategy insuredStrategy) { this.insuredStrategy = insuredStrategy; } }测试类
package com.i2fly.strategy; public class Client { public static void main(String[] args) { InsuredStrategyContext insuredStrategyContext; System.out.println("要给小孩买保险"); insuredStrategyContext = new InsuredStrategyContext(new ChildInsuredStrategy()); insuredStrategyContext.excuteBuyInsures(); System.out.println("要给中年人买保险"); insuredStrategyContext = new InsuredStrategyContext(new MiddleAgedInsuredStrategy()); insuredStrategyContext.excuteBuyInsures(); System.out.println("要给老年人买保险"); insuredStrategyContext = new InsuredStrategyContext(new OldInsuredStrategy()); insuredStrategyContext.excuteBuyInsures(); } }运行结果
要给小孩买保险 购买儿童保险,保障儿童快乐成长。。。 要给中年人买保险 中年人购买保险,保障家庭财产安全。。。。 要给老年人买保险 老年人购买保险,保障身体生病花钱少。。。。
策略模式就是采用了面向对象的继承和多态机制,其他没什么玄机。但是真实的业务环境有这么简单吗?一个类实现多个接口很正常,我们需要看清楚哪个接口是抽象策略接口,哪些是和策略模式没有任何关系。
策略模式调用的时序图
1.算法可以自由切换 这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。 2. 避免使用多重条件判断 如果没有策略模式,我们想想看会是什么样子?一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。 3.扩展性良好 这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。
1.策略类数量增多 每一个策略都是一个类,复用的可能性很小,类数量增多。 2.所有的策略类都需要对外暴露 上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。
1.多个类只有在算法或行为上稍有不同的场景。 2.算法需要自由切换的场景。 例如,算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是你最好的助手。 3.需要屏蔽算法规则的场景。 现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果就可以了。