Head First设计模式之策略模式(Strategy)
小白将从以下几点对策略模式进行解析
-
策略模式引入的设计技巧
-
策略模式的定义及类图
-
策略模式的优缺点
-
策略模式的应用场景
-
策略模式实战
-
总结
零、策略模式引入的技巧
先过一遍,放入脑海中,等看完后面所有的内容,可以结合上下文进行消化
我们要做的不是看过、读过、理解过、消化过,而是需要将其平常化,使用模式就像使用变量一样简单。
-
封装变化(把会变化的部分取出并“封装”起来,好让其他部分不会受到影响,这样一来,代码变化引起的不经意后果变少,系统变得更有弹性)
-
针对接口编程,而不是针对实现编程(可以把具体的实现延迟到”运行时“,执行时会根据实际状况执行到真正的行为,不会在编译时直接绑死,充分利用多态使系统更有弹性)
-
多用组合,少用继承("有一个”可能比“是一个”更好,可以在“运行时”动态的改变具体的实现,这样使系统有很大的弹性)
一、策略模式的定义及类图
定义:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。(PS:不要一带而过,停下来思考一下这句话的含义,多思考标注颜色的词语)
类图:类图中一共有三个角色(至少三个角色),抽象策略角色、具体策略角色、环境角色
- 抽象策略角色:策略类,通常由一个接口或者抽象类实现
- 具体策略角色:包装了相关的算法和行为(算法和行为 对于策略模式来说是一样的,只是基于的角度不同,所以叫法不同而已)
- 环境角色:持有一个策略类的引用,最终提供给客户端调用
以下是UML类图:
二、策略模式的优缺点
优点:
- 可以动态的改变对象的行为而不需要客户端做任何的改动(从定义中就能看出来)
- 使用策略可以避免多重转移语句(比如 if else)。多重转移语句不便于维护,并且这种写法将采取哪一种算法的逻辑和算法的逻辑混合在一起,高耦合了
- 恰当的使用继承可以将公共的代码转移到父类(抽象策略角色)中,避免重复代码《此有点并非策略模式独有的优点,凡是采用继承的都可以拥有该优点》
缺点:
- 对于客户端来说,它必须知道所有的策略类,这样便可以根据不同的场景使用不同的策略类来完成当前的task。
- 策略模式的出现会引入许多的策略类,因此一般使用策略模式时需要考虑类泛滥的问题,一般4.5个具体策略类比较合适
三、策略模式的应用场景
-
多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为
- 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现
- 对客户隐藏具体策略(算法)的实现细节,彼此完全独立
-
实际应用场景如下:(好好结合以下实际场景和上述语句进行融合)
- 比如买东西支付,你可以使用微信支付、支付宝支付、银行卡支持、pos机支付、零钱支持,具体使用哪种支付方式您肯定需要根据当时的场景来进行选择。
- 比如出行,您可以选择自行车出行、开车出行、走路出行、坐地铁出行。
- 再不如聊天,您可以选择微信聊天、QQ聊天、电话聊天。 等等
根据当时环境选择哪一种方式进行后续动作便是策略。
四、策略模式实战
以下代码不会太复杂,只是作为示例,我这边已支付来进行实战
抽象策略角色设计如下
/** * @Author: wander * @Descripttion: 抽象策略角色 * @Date: 2018年07月15日22时40分 */ public interface Pay { void pay(Integer money); }
三个具体策略角色设计如下
/** * @Author: wander * @Descripttion: 支付宝支付 * @Date: 2018年07月15日22时43分 */ public class AliPay implements Pay { public void pay(Integer money) { System.out.println("支付宝支付了" + money + "元"); } }
/** * @Author: wander * @Descripttion: 微信支付 * @Date: 2018年07月15日22时41分 */ public class WeChatPay implements Pay{ public void pay(Integer money) { System.out.println("微信支付了" + money + "元"); } }
/** * @Author: wander * @Descripttion: * @Date: 2018年07月15日22时44分 */ public class BankCardPay implements Pay { public void pay(Integer money) { System.out.println("银行卡支付了" + money + "元"); } }
环境角色
/** * @Author: wander * @Descripttion: 环境角色 * @Date: 2018年07月15日22时49分 */ public class Person { private Pay pay; private Integer money; public Person(Integer money) { this.money = money; } public void pay(Pay pay) { pay.pay(money); } }
运行类
/** * @Author: wander * @Descripttion: 运行环境类 * @Date: 2018年07月15日22时52分 */ public class Strategy { public final static Integer TEN = 10; public static void main(String[] args) { WeChatPay weChatPay = new WeChatPay(); AliPay aliPay = new AliPay(); Person person = new Person(TEN); person.pay(weChatPay); person.pay(aliPay); } }
运行结果如下:
微信支付了10元
支付宝支付了10元
五、总结
当你需要根据不同的情况选择不同的方式处理问题,并且对使用者隐藏处理问题的过程,同时使用者可以随时更换处理方式而不需要做任何的改动