设计模式之策略模式

策略模式

是什么?

策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。

怎么做?

定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

  • 定义了一族算法(业务规则);
  • 封装了每个算法;
  • 这族的算法可互换代替(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()

策略模式的优点

  • 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
  • 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的策略中,使得它们易于切换,易于理解,易于扩展。
  • 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
  • 在策略模式中利用组合和委托来让客户拥有执行算法的能力,这也是继承的一种更轻便的替代方案。

缺点

  • 首先,使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在客户中要好。
  • 其次,要使用策略模式,必须了解所有的策略,必须了解各个策略之间的不同点,这样才能选择一个合适的策略。比如,我们要选择一种合适的旅游出行路线,必须先了解选择飞机、火车、自行车等方案的细节。此时策略要向客户暴露它的所有实现,这是违反最少知识原则的。

参考资料

维基百科-策略模式

你可能感兴趣的:(设计模式之策略模式)