iOS - 策略模式
定义
策略模式是一种比较简单的模式,也叫政策模式。定义一系列的算法,把它们一个个封装起来,并且使它们可相互替代。
类图
屏幕快照 2018-10-29 上午10.57.40.png策略模式的三个角色
策略模式使用的是面向对象的继承和多态机制,一起来了解一下策略模式中的三个角色:
Context封装角色
简单理解为上下文,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
Strategy抽象策略角色
策略,算法家族的抽象,通常为接口,定义每个策略或者算法必须具有的方法和属性。
ConcreteStrategy具体策略角色
DEMO
看一个简单的例子,源于这里
1、定义策略,Swift中我们基本上使用protocol,也可以使用基类,在基类中定义抽象操作
protocol PrintStrategy {
func print(_ string: String) -> String
}
2、遵守协议或者继承基类,实现具体的策略
final class UpperCaseStrategy: PrintStrategy {
func print(_ string: String) -> String {
return string.uppercased()
}
}
final class LowerCaseStrategy: PrintStrategy {
func print(_ string:String) -> String {
return string.lowercased()
}
}
3、构造封装类,即上下文,根据需要创建对应的策略
final class Printer {
private let strategy: PrintStrategy
func print(_ string: String) -> String {
return self.strategy.print(string)
}
init(strategy: PrintStrategy) {
self.strategy = strategy
}
}
4、在具体的场景中使用具体的策略
var lower = Printer(strategy: LowerCaseStrategy())
lower.print("O tempora, o mores!")
var upper = Printer(strategy: UpperCaseStrategy())
upper.print("O tempora, o mores!")
使用场景
1、多个类只有在算法或者行为稍有不同的场景
2、算法需要自由切换的场景
例如:算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是个不错的选择。
3、需要屏蔽算法规则的场景
只需要知道一个算法的名字,而不需要知道其他的内容。
优缺点
优点
1、算法可以自由切换
这是策略模式本身定义的,只要实现抽象策略,它就成为了策略家族的一员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。
2、避免使用多重条件判断
如果没有策略模式,当一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,这该怎么办?使用多重的条件语句,这个方案是可行,但是不易扩展和维护,而且出现错误的概率很大,当使用了策略模式之后,可以由其他模块决定采用何种策略,策略家族对外提供访问接口即可,这样简化了操作,同时避免了大量条件语句的判断。
3、扩展性好
因为在现有的系统中增加一个策略非常容易,只要实现接口就可以了,其他的都不需要修改。
4、策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
缺点
1、策略类的数量会越来越庞大
每个策略都是一个类,复用的可能性很小,类数量增多
2、所有的策略类都需要对外暴露
上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的。因为对于外界而言,我只想使用一个策略,干嘛要了解这个策略?那么封装类的意义在哪?这是策略模式的一个缺点。
注意
1、如果系统中的一个策略家族的具体策略数量超过了4个,那么需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就麻烦了。
2、策略模式跟代理模式(delegation pattern)有点相似,两者都需要依靠协议而不是具体的对象,区别在于策略模式需要一系列的对象,而代理是使用单一对象。