1. 策略模式简介
策略模式(Strategy Pattern), 是行为型模式之一(设计模式分类: https://www.jianshu.com/p/18fee11deac9).
在软件开发中常常遇到这样的情况: 实现某一个功能可以有多种算法或者策略, 我们根据实际情况选择不同的算法或者策略来完成该功能. 例如, 排序算法, 可以使用插入排序, 归并排序, 冒泡排序等.
针对这种情况, 一种常规的方法是将多种算法写在一个类中. 例如, 需要提供多种排序算法, 可以将这些算法写到一个类中, 每一个方法对应一个具体的排序算法; 当然, 也可以将这些排序算法封装在一个统一的方法中, 通过 if...else... 或者 case 等条件判断语句来选择具体的算法. 这两种实现方法我们都可以称为硬编码. 然而, 当很多个算法集中在一个类中, 这个类就会变得臃肿, 这个类的维护成本会变高, 在维护时也更容易引发错误. 如果我们增加一种新的排序算法, 需要修改封装算法类的源代码. 这就明显违反了我们所说的 OCP(开闭)原则和单一职责原则.(OCP: 软件中的对象 (类, 模块, 函数等) 应该对于扩展是开放的, 但是对于修改是封闭的.)
如果将这些算法或者策略抽象出来, 提供一个统一的接口, 不同的算法或者策略有不同的实现类(类肯定是变多了), 这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换, 这种模式的可扩展性, 可维护性也就更高, 也就是我们所说的策略模式.-《Android 源码设计模式解析与实战》
2 策略模式定义
策略模式定义了一系列算法, 并将每一个算法封装起来, 而且使它们还可以相互替换. 策略模式让算法独立于使用它的客户而独立变化.-《Android 源码设计模式解析与实战》
面向对象软件设计中, 我们可以把相关算法分离为不同的类, 成为策略. 与这种做法有关的一种设计模式称为策略模式.
3. 策略模式的使用场景
3.1 针对同一类型问题的多种处理方式, 仅仅只是具体行为有差别时.
3.2 需要安全地封装多种同一类型的操作时.
3.3 出现统一抽象类有多个子类, 而又需要使用 if-else 或者 switch-case 来选择具体子类时.
4. 策略模式的 UML 图
策略模式 UML.PNG
5. 策略模式 角色划分
角色一: Context 用于操作策略的上下文环境 场景类的对象配置有一个具体策略对象的实例, 场景对象使用策略接口调用有具体策略类定义的算法.
角色二: Stragety 抽象策略
角色三: ConcreteStragety 具体策略
6.Demo 实践
假设应用程序中需要用 UITextField 以接受用户的输入, 然后要在应用程序的处理中使用这个输入值. 应用程序有个文字输入框, 只接受字母, 即 a-z 或 A-Z, 还有个输入框只接受数值型的值, 即 0~9. 为了保证每个字段的输入有效, 需要在用户结束文本框的编辑时做些验证.
这个时候我们可以把数据验证放大 UITextField 的委托方法 textFieldDidEndEditing: 之中. UITextfield 的实例每当失去焦点时会调用这个方法. 如果不用策略模式, 代码通常会写成下面的样子:
- - (void)textFieldDidEndEditing:(UITextField *)textField
- {
- if (textField == letterTextField) {
- // 验证是否是字母
- } else if (textField == numberTextField){
- // 验证是否是数字
- } else if ...
- }
要是有更多不同类型的文本框, 条件语句还会继续下去, 代码越来越臃肿, 难以维护. 如果能去掉这些条件语句, 代码会更易管理, 将来对代码的维护也会容易得多.(如果代码中有很多条件语句, 就可能意味着需要把它们重构成各种策略模式)
所以现在的目标是把这些验证检查提到各种策略类中, 这样它们就能在委托或者其他方法中重用. 每个验证都从文本框取出输入值, 然后根据所需的策略进行验证, 最后返回个 BOOL 值; 如果验证失败, 还会返回一个 NSError 实例. 返回的 NSError 可以解释失败的原因.
Demo 的 UML 图如下:
策略模式 DemoUML.PNG
这里不把接口声明为协议, 而是声明为抽象基类. 抽象基类更适合解决这种问题, 因为它更容易重构各种具体策略子类的某些共同行为.
使用策略模式后客户端代码的样子:
- - (void)textFieldDidEndEditing:(UITextField *)textField
- {
- if ([textField isKindOfClass:[CustomTextField class]]) {
- [(CustomTextField*)textField validate];
- }
- }
重要的事情说三遍, 一定要看 Demo, 一定要看 Demo, 一定要看 Demo.
Demo 地址: https://github.com/zhiyoukaifa/StrategyPatternDemo
参考书籍:
《Objective-C 编程之道 iOS 设计模式解析》
《Android 源码设计模式解析与实战》
来源: http://www.jianshu.com/p/05054fe6df1b