5.3 面向复用的设计模式
综述:本节介绍几种典型的“面向复用”的设计模式
为什么要提出可复用的设计模式?
设计模式:软件设计中给定上下文中常见问题的一般可重用解决方案,除了类本身,设计模式更强调多个类/对象之间的关系和交互过程—比接口/类复用的粒度更大
1. 结构型模式
1.1 适配器模式
- 将某个类/接口转换为client期望的其他形式,也叫包裹器
- 通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。(具体是通过delegation的方法封装的)
- 本质上就是迎合client
- 但最终是面向接口编程,新加的类也要实现总的接口
- 实例:
封装前:
封装后:
1.2 装饰器模式
- 解决的问题:为对象增加不同侧面的特性,但不想通过继承来实现(继承会导致组合爆炸)
- 解决方法:对每一个特性构造子类,通过委派机制增加到对象上,装饰器同时用了分型和委托
- 具体实现:客户端需要一个具有多种特性的object,通过一层一层的
装饰来实现 - 优点:更加灵活,且更容易组合
- 实例:
接口和抽象类的定义:
实现了具体的子类(super是核心,相当于在构造时沿用上一层的构造,不断迭代,并且添加了该子类的特有的功能):
客户端: - 装饰器模式和静态继承的区别:
- 装饰器运行时组合特性,而继承时编译时就确定了
- 装饰器由多个协作对象组成,而继承是创造了一个单一的,结构清晰的对象
- 装饰功能可以灵活的实现和组合,而基于继承的组合十分困难
1.3 外观模式
- 解决的问题:客户端需要通过一个简化的接口来访问复杂系统内的功能
- 方法:提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用
- 优点:便于客户端学习使用,解耦,它还促进子系统与其潜在的多个客户端的分离。
- 实质,把所有api放到一个方法里,然后api具体实现时要各种判断
- 实例:
单独的api:
用外观模式:
客户端的代码: - 从实现中我们可以发现,开发者的实现工作量加大,要对client的不同的输入进行不同的判断
2. 行为类模式
2.1 策略模式
- 解决问题:针对特定任务存在不同的算法,但客户端可以在运行时根据动态上下文在算法之间切换。(黑盒框架)
- 方法:为算法创建一个接口,为算法的每个变体提供一个实现类。
- 优点:
- 方便扩展新的算法实现
- 将算法与客户端的上下文分离
- 实质:客户端用统一的接口调用它向需要的算法
- 实例:
问题的总类图:其中paymentstrategy就是算法的接口
实例代码:
另一个实例代码和管理类:可以看到管理类在pay时把接口传了进来,即委托的方式
客户端:客户端在调用时要选择具体的算法
2.2 模板模式
- 解决问题:做事情的步骤一样,但具体方法不同 (白盒框架)
- 方法:共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现,对于每一个具体的步骤,子类可能有不同的实现
- 本质:父类把解决问题的步骤罗列出来,然后设计不同的子类去实现这几个步骤,客户端调用的时候按照父类罗列的顺序去调用
- 使用继承和重写实现模板模式
- 实例:
总类图:
抽象类代码:
子类实现父类代码、在客户端中的调用:
2.3 迭代器
- 解决问题:客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型,也就是说不管放到哪里,都应该提供同样的遍历方式
- 方法:让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove),允许客户端利用这个迭代器进行显式或隐式的迭代遍历
- 本质:让自己的集合类先实现一个xxxable的接口,这个接口里有一个方法会返回一个xxxtor的器,这样你的集合类就具有了这个器
- 优点:
- 隐藏底层容器的内部实现
- 支持具有统一接口的多种遍历策略
- 易于更换容器类型
- 促进程序各部分之间的沟通
- 实例: