跟着Head First 学习设计模式一: 策略模式

338 阅读4分钟

策略模式

定义:定义了一系列算法,并将每个算法封装起来,使它们可以互相替换,且算法的变化不会影响使用算法的客户。

特点:策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

其类结构如下

image.png

有三个角色:

  • Context(环境角色): 持有一个指向Strategy的引用strategy,该引用指向的是Strategy 的实现类,这里用到了对象的多态特性。

  • Strategy(抽象策略角色):Strategy 是用接口定义的,遵循面向接口编程的原则,此接口定义出所有具体策略类所需的接口方法。

  • StrategyA(具体策略决策):包装了相关的算法和行为。

image.png

在我们这个鸭子的例子中,基类为Duck === 环境角色,其持有一个FlyBehavior以及QuackBehavior 引用,通过set方法动态设置行为算法,使用那种行为算法由客户端决定。其Duck类无需关心其飞行、鸣叫的行为是什么样子的,只需要通过代理去执行不同的行为算法。

浏览其特点,策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理我们分析下这段描述:

  1. 策略模式属于对象行为模式,它通过对算法进行封装:所有的行为通过定义接口,其接口里的方法就是行为需要实现的方法,然后通过具体类来实现这些行为,FlyBehavior 接口定义了飞行的行为,其具体的实现则由FlyWithWings、FlyNoWay 这些类来实现。其每种算法都封装在了实现类中。

  2. 把使用算法的责任和算法的实现分割开来: 使用算法的责任是Duck 类,算法的实现是实现了FlyBehavior 和 QuackBehavior 的实现类。

  3. 并委派给不同的对象对这些算法进行管理:使用算法的类Duck 持有算法的接口引用,flyBehavior与quackBehavior,鸭子的飞行行为和鸣叫行为分别通过方法performFly() 和performQuack() 委派给客户端通过set 方法传递进来的飞行行为实现类和鸣叫行为实现类。

这个算法遵循了设计原则如下:

  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。---开闭原则(对修改关闭,对扩展开放)

  2. 针对接口编程,而不是针对实现编程。---每个行为都是定义了接口,然后通过具体行为类来实现算法。

  3. 多用组合编程,少用继承。----Duck类中持有飞行行为的引用好和鸣叫行为的引用,可以实现动态的调整,继承是静态的,无法动态改变其行为。

策略模式的主要优点如下

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。

  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。

  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。

  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。

  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离

其主要缺点如下:

  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。

  2. 策略模式造成很多的策略类,增加维护难度。---接口+实现类。小类太多。

策略模式的使用场景:

  1. 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。

  2. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。

  3. 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。

  4. 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。

  5. 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

总结:

image.png

参考文献:

c.biancheng.net/view/1378.h…

segmentfault.com/a/119000004…

www.cnblogs.com/java-my-lif…

具体实现代码请移步github: github.com/Warkey1991/…