本文首发于 我的个人博客
什么是策略模式
类型:行为类模式
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。——《Head First 设计模式》
场景
假设我们有个需求是,小孩和大人,小孩吃蔬菜,大人吃肉,小孩每天跑步一小时,大人每天跑步2小时,那么这个简单的需求怎么实现呢?
继承来实现
首先,我们想到的是继承,定义一个Person
类,里么有两个方法
1 | - (void)eatSomeThing; |
然后,定义ChildPerson
和 AdultPerson
都继承自Person
。然后在各自的类中,分别实现,eatSomeThing
和 runEveryDay
的方法
这种方法是可以,但是,有个问题,就是代码重用性较差,例如,如果再来一个 老人,吃蔬菜,能跑2小时,那么实现起来的话,就是在定义一个类 OldPerson
继承Person
,也实现eatSomeThing
和 runEveryDay
的方法,但问题是,我们之前已经实现了,吃蔬菜的方法,所以没必要再写重复的代码。
这时候,就可以用到策略模式
策略模式来实现
run
- 协议
Run.h
1 |
|
- 定义类
RunOneHour
准守协议<Run>
1 |
|
- 定义类
RunTwoHours
准守协议<Run>
1 |
|
eat
- 协议
Eat
1 |
|
- 定义类
EatMeat
准守协议<Eat>
1 |
|
- 定义类
EatVegetables
准守协议<Eat>
1 |
|
person
Person
有两个属性,实现了<Eat>
协议的eat
属性 和 实现了<Run>
协议的run
属性
两个方法,分别是,eatSomeThing
和runEveryDay
1 |
|
ChildPerson
1 |
|
AdultPerson
1 |
|
使用
1 | Person *child = [[ChildPerson alloc] init]; |
输出
1 | 2019-07-08 08:10:41.159747+0800 iOS-策略模式[1402:13333] 我是小孩 |
这样就用策略模式实现了这个需求,如果这时候,增加一个需求 如果再来一个 老人,吃蔬菜,能跑2小时
只需要再定义一个类 OldPerson
如下
1 |
|
调用时候 只需要如下
1 | Person *old = [[OldPerson alloc] init]; |
即可输出
1 | 2019-07-08 08:46:16.007758+0800 iOS-策略模式[2567:27396] 我是老人 |
根本不需要重写吃,和跑的方法。
总结
优点
- 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
- 提供了可以替换继承关系的办法
- 策略模式的Stategy类层次为Context定义了一些列的可供重用的算法或行为。继承有助于析取出算法中的公共功能。
- 策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
- 易于扩展,增加一个新的策略对策略模式来说非常容易,基本上可以在不改变原有代码的基础上进行扩展
缺点
- 维护各个策略类会给开发带来额外开销,一般来说,策略类的数量超过6个,就比较麻烦
- 必须对客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么策略,并且了解各种策略之间的区别,否则,后果很严重。
改进
- 策略模式是一种简单常用的模式,我们在进行开发的时候,会经常有意无意地使用它,一般来说,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的情况比较多。