【设计模式】策略模式
问题背景
有父类duck,实现了quack(叫)和swim(游泳)两个方法
预留了display方法给子类实现
子类可能有多种,现在假设只有MallardDuck和RedheadDuck
需求:fly()
现在需要给鸭子实现fly(飞)方法
解决方法1:在父类中实现
在父类中添加fly方法给子类继承
变化1:部分Duck不能fly
如果仍使用继承,那么本不该fly的Duck也能访问到fly接口
解决方法2:在子类中进行重写
缺点:
这种方法的缺点:(ABDF?)
且在未来的维护过程中,如果需要添加新的类,就必须反复检查所有接口并可能重写部分接口
解决方法2:用接口实现
缺点:
如果未来出现需要对【fly】这个行为进行修改,比如修改fly的速度,那么可能需要对已有的所有类都要进行修改
而且使用接口的方法本身需要在每个子类中附加实现,这样产生了非常多的重复和冗余的代码
java接口不具有实现代码,所以集成接口无法达到代码的复用。这意味着无论何时需要修改某个行为,必须得往下追踪并在每一个定义此行为的类中修改它,可能会造成新的错误。
策略模式方法
-
把会变化的部分取出并封装起来,好让其他部分不受到影响
-
以下的用词【接口】既可以指java中的interface,也可以指abstract class,只是一个概念性接口
-
如果每次新的需求依赖,都会使某些方面的代码发生变化,那么就可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分
将变化的代码抽取出来
将经常变化的代码,在本例中指fly和quack,抽取出来,独立为两个接口:FlyBehavior和QuackBehavior
具体每种实现方式,可以由继承他们的子类来实现,而Duck类中只需要留下调用Behavior的接口即可
优点:
- 这样的设计,可以让fly和quack的动作被其他的对象复用,因为这些行为已经与Duck类无关了(例如:在可以发出鸭叫的机器中可以调用quack的动作,但是机器不是Duck类)
- 我们可以新增一些行为,不会影响到已有的类,以及使用现有行为的类
现在的Duck类
-
将Duck类中经常变化的行为抽象为一个接口,即flyBehavior和quackBehavior
-
用perform行为来替代原来的行为,即performQuack和performFly替代原来的quack和fly
-
每个子类实例化时,都可以指定该子类对应的行为
-
也可以为子类预留setter方法,从而在运行中改变其行为方式
在运行过程中,子类可以动态的调用setter方法来改变其行为方式
总览UML图
设计原则
-
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
-
- 针对接口编程,而不是针对实现编程
-
- 多用组合,少用继承
策略模式
-
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it
翻译:策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
-
Use the Strategy pattern when
-
many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
-
you need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms.
-
an algorithm uses data that clients shouldn’t know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
-
a class defines many behaviors, and these appearas multiple conditional statements in its operations.
Instead of many conditionals, move related conditional branches into their own Strategy class.
翻译:当出现以下情况时可以使用策略模式:
-
许多相关类仅在它们的行为上有所不同。策略提供了一种方法来配置具有多种行为之一的类。
-
你需要一个算法的不同变体。例如,您可以定义反映不同空间/时间权衡的算法。当这些变体被实现为算法的类层次结构时,可以使用策略。
-
算法使用客户端不应该知道的数据。使用Strategy模式可以避免暴露复杂的、特定于算法的数据结构。
-
一个类定义了许多行为,这些行为在它的操作中表现为多个条件语句。将相关的条件分支移动到它们自己的策略类中,而不是使用许多条件语句。
-
-
Consequences
- Families of related algorithms. Hierarchies of Strategy classes define a family of algorithms or behaviors for contexts to reuse. Inheritance can help factor out common functionality of the algorithms.
- An alternative to subclassing.
- Strategies eliminate conditional statements.
- A choice of implementations. Strategies can provide different implementations of the same behavior. The client can choose among strategies with different time and space trade-offs.
- Clients must be aware of different Strategies. The pattern has a potential drawback in that a client must understand how Strategies differ before it can select the appropriate one. Clients might be exposed to implementation issues.
- Communication overhead between Strategy and Context.
- Increased number of objects.
翻译:使用策略模式的影响:
- 相关算法族。策略类的层次结构定义了一系列要重用的上下文的算法或行为。继承可以帮助提取出算法的常见功能。
- 子类化的替代方法。
- 策略消除了条件语句。
- 实现的选择。策略可以为相同的行为提供不同的实现。客户可以选择不同的时间和空间权衡策略。
- 客户必须了解不同的策略。该模式有一个潜在的缺点,即客户必须了解策略之间的差异,然后才能选择合适的策略。客户端可能会遇到实现问题。
- 策略和上下文之间的沟通开销。
- 增加的对象数量。