策略模式
是什么?
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。
怎么做?
定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
- 定义了一族算法(业务规则);
- 封装了每个算法;
- 这族的算法可互换代替(interchangeable)。
遵守原则?
- 封装变化。
- 多用组合,少用继承。
- 针对接口编程,不针对实现编程。
第一步,区分客户和一族算法:
以上面税收的例子,人就是客户。人可以是美国人、中国人。一族算法就是交个人所得税,有美国的算税方法,有中国特色的算税方法。
第二步,抽象代码:
// 客户的抽象
class Human: NSObject {
// 人类共同的属性和行为
}
class American: Human {
// 美国人特殊的属性和行为
}
class Chinese: Human {
// 中国人特殊的属性和行为
}
// 一族算法的抽象
protocol PayTaxBehavior {
// 交税的协议
func payingTaxes()
}
class ChinaTax: NSObject, PayTaxBehavior {
// 中国税收计算
func payingTaxes() {
print("中国税收计算!")
}
}
class AmericanTax: NSObject, PayTaxBehavior {
// 美国税收计算
func payingTaxes() {
print("美国税收计算!")
}
}
第三步,组合调用:
为什么示例代码是鸭子?因为我已经写好了,所以直接拷贝好了。
1.0 一族算法的协议
protocol FlyBehavior {
func fly()
}
protocol QuackBehavior {
func quack()
}
2.0 实现协议的算法类
class FlyWithWings: NSObject, FlyBehavior {
func fly() {
print("I am fly with wings.")
}
}
class FlyNoWay: NSObject, FlyBehavior {
func fly() {
print("I am fly no way.")
}
}
class Quack: NSObject, QuackBehavior {
func quack() {
print("quack quack quack!")
}
}
class Squeak: NSObject, QuackBehavior {
func quack() {
print("squeak squeak squeak!")
}
}
class MuteQuack: NSObject, QuackBehavior {
func quack() {
print("Mute quack.")
}
}
** 3.0 用户的抽象类**
class Duck: NSObject {
// 遵守协议的一族算法对象,私有是为了不让子类直接访问。
private var quackBehavior: QuackBehavior?
private var flyBehavior: FlyBehavior?
// 提供接口,让算法可互换代替
public func setQuackBehavior( quackBehavior : QuackBehavior )
{
self.quackBehavior = quackBehavior
}
public func setFlyBehavior( flyBehavior : FlyBehavior )
{
self.flyBehavior = flyBehavior
}
// 可能某些鸭子没有的行为,提供接口调用封装好的算法。
public func performFly()
{
flyBehavior?.fly()
}
public func performQuack()
{
quackBehavior?.quack()
}
// 鸭子共有的行为,可以直接继承。不同于父类的可以重写。
public func swim()
{
print("I am swimming!!!")
}
public func name()
{
print("I am duck.")
}
}
4.0 具体的用户
class DuckA: Duck {
override init() {
super.init()
// 子类有自己的默认行为。
self.setFlyBehavior(flyBehavior: FlyWithWings())
self.setQuackBehavior(quackBehavior: Quack())
}
public override func name() {
print("I am duck A.")
}
}
class DuckB: Duck {
override init() {
super.init()
// B 也有自己的默认行为
self.setFlyBehavior(flyBehavior: FlyWithWings())
self.setQuackBehavior(quackBehavior: Squeak())
}
public override func name() {
print("I am duck B.")
}
}
class DuckC: Duck {
override init() {
super.init()
// C 也有自己的默认行为
self.setFlyBehavior(flyBehavior: FlyNoWay())
self.setQuackBehavior(quackBehavior: MuteQuack())
}
public override func name() {
print("I am duck C.")
}
}
5.0 测试代码
let duck = DuckC()
// 我们有不一样的名字
duck.name()
// 但我们都会游泳
duck.swim()
// 调用默认行为
duck.performFly()
duck.performQuack()
// 改变行为
duck.setQuackBehavior(quackBehavior: Squeak())
duck.setFlyBehavior(flyBehavior: FlyWithWings())
// 调用改变后的行为
duck.performFly()
duck.performQuack()
策略模式的优点
- 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
- 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的策略中,使得它们易于切换,易于理解,易于扩展。
- 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
- 在策略模式中利用组合和委托来让客户拥有执行算法的能力,这也是继承的一种更轻便的替代方案。
缺点
- 首先,使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在客户中要好。
- 其次,要使用策略模式,必须了解所有的策略,必须了解各个策略之间的不同点,这样才能选择一个合适的策略。比如,我们要选择一种合适的旅游出行路线,必须先了解选择飞机、火车、自行车等方案的细节。此时策略要向客户暴露它的所有实现,这是违反最少知识原则的。