- 门面模式: 简化外部客户和内部系统的耦合
- Proxy模式:增加间接层,实现不为外界所知的功能
- Adaper模式:将一个类的接口转换成客户希望的另一个接口
- Mediator: 将密集的、大量的多个对象间复杂的关联关系解耦
接口隔离模式
- 某些接口之间的依赖会带来问题
- 通过增加一层间接(稳定)接口,隔离相互紧密相关的接口
- 典型模式
- Facade
- Proxy
- Adapter
- Mediator
门面模式
动机
- 组件的客户和组件的子系统之间有了复杂的耦合
- 随着外部客户端和子系统的演化
- 过多的耦合会造成很多问题
- 如何简化外部客户和内部系统的耦合?
- 如何将外部客户程序的演化,和内部子系统之间的依赖相互隔离?
模式定义
- 为系统中的一组接口提供一个对外稳定的、一致的界面,Facade模式定义了一个高层接口,这个接口使得这个系统更加容易使用
- 它是一个接口,它知道某个请求可以交由哪个子系统进行处理
- 它使用组合将客户端的请求委派给相应的子系统对象
结构
要点总结
- 从客户角度,Facade模式简化了系统的接口
- 对于组件的内部程序和外部客户来说,达到了解耦的效果
- 内部子系统的变化不会影响到Facade接口的变化
- 对于组件的内部程序和外部客户来说,达到了解耦的效果
- Facade模式注重从架构层次看这个系统
- 不是单个的类视角
- Facade模式不是集装箱
- 内部的子系统是相互耦合关系较大的一系列组件
- 而不是简单的工具箱(功能的集合)
Rust 实现
实现功能
假设你要在家中举行一场婚礼,并且由你来张罗这一切。这真是一个艰巨的任务。你必须预订一家酒店或场地,与餐饮人员交代酒菜、布置场景,并安排背景音乐。 在不久以前,你已经自己搞定了一切,例如找相关人员谈话、与他们进行协调、敲定价格等,那么现在你就很轻松了。此外,你还可以去找会务经理,让他/她为你处理这些事情。会务经理负责跟各个服务提供商交涉,并为你争取最优惠的价格
- 我们从门面模式的角度来看待这些事情:
- 客户端
- 你需要在婚礼前及时完成所有的准备工作。每一项安排都应该是顶级的,这样客人才会喜欢这些庆祝活动
- 门面
- 会务经理负责与所有相关人员进行交涉,这些人员负责处理食物、花卉装饰等
- 子系统
- 它们代表提供餐饮、酒店管理和花卉装饰等服务的系统
- 客户端
具体实现
#![feature(non_ascii_idents)]
struct 活动经理;
impl 活动经理 {
fn 安排(&mut self) {
酒店.定酒店();
花店.定花();
乐队.音乐类型();
}
}
struct 酒店;
impl 酒店 {
fn 有空房(&self) -> bool {
println!("[酒店] 指定日期那天有空房");
true
}
fn 定酒店(&self) {
println!("[酒店] 为结婚安排酒店?");
if self.有空房() {
println!("[酒店] 房间已预定好\n");
}
}
}
struct 花店;
impl 花店 {
fn 定花(&self) {
println!("[花店] 你需要什么鲜花装饰?");
println!("[花店] 已预定好玫瑰、茉莉和康乃馨\n");
}
}
struct 乐队;
impl 乐队 {
fn 音乐类型(&self) {
println!("[乐队] 你需要什么风格的音乐?");
println!("[乐队] 准备当天演奏爵士和古典音乐\n");
}
}
struct 承办人;
impl 承办人 {
fn 问活动经理(&self) {
println!("[你]: 我来找找活动经理");
let mut 经理 = 活动经理;
经理.安排();
}
}
fn main() {
let 你 = 承办人;
println!("老爸:给你哥的婚礼安排得怎么样了?");
你.问活动经理();
}
运行结果:
老爸:给你哥的婚礼安排得怎么样了?
[你]: 我来找找活动经理
[酒店] 为结婚安排酒店?
[酒店] 指定日期那天有空房
[酒店] 房间已预定好
[花店] 你需要什么鲜花装饰?
[花店] 已预定好玫瑰、茉莉和康乃馨
[乐队] 你需要什么风格的音乐?
[乐队] 准备当天演奏爵士和古典音乐
Proxy 模式
动机
- 有些对象由于某些原因,直接访问会给使用者或系统结构带来麻烦
- 对象创建的开销很大
- 某些系统操作需要安全控制
- 或需要进程外的访问
- 本机要在其他机子上创建对象(分布式计算)
- 如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性
- 增加一层间接层是软件开发中常见的解决方式
- 透明
- 在本机创建对象的方式,也可以在其他机子上创建对象
- 但是那些复杂操作(创建对象、安全控制)对客户端不可见
模式定义
- 为其他对象提供一种代理以控制(隔离、使用接口)对这个对象的访问
结构
初始设计
class ISubject{
public:
virtual void process();
};
class RealSubject: public ISubject{
public:
virtual void process(){
//....
}
}
class ClientApp{
ISubject* subject;
public:
// 有可能得不到这个对象
ClientApp(){
subject=new RealSubject();
}
void DoTask(){
//...
subject->process();
//....
}
};
重构设计
class ISubject{
public:
virtual void process();
};
//Proxy的设计
class SubjectProxy: public ISubject{
public:
virtual void process(){
//对RealSubject的一种间接访问
//....可能是网络访问、安全控制
}
};
class ClientApp{
ISubject* subject;
public:
// 通过代理来创建real suject对象
ClientApp(){
subject=new SubjectProxy();
}
void DoTask(){
//...
subject->process();
//....
}
};
要点总结
- 增加中间层是软件系统应对复杂问题的一种解法
- 如果直接使用某些对象会造成问题
- 作为间接层的Proxy是解决此问题的一种手段
- 具体Proxy的实现方法、实现细粒度相差很大
- 对单个对象细粒度的控制:Copy-on-Write
- 对整个组件模块提供抽象代理层
- 在架构层次对对象做Proxy
- Proxy不要求保持接口完整的一致性
- 主要能实现间接控制
- 损失一定的透明度是可以接受的
- Proxy核心
- 增加间接层,实现不为外界所知的功能
Adaper模式
动机
- 由于应用环境的变化
- 需要将现有的对象放到新的环境中
- 但新环境要求的接口是这些对象不能提供的
- 如何应对这种迁移性质的变化?
- 如何既利用现有对象的良好实现
- 有满足新应用环境要求的接口?
模式定义
- 将一个类的接口转换成客户希望的另一个接口
结构
- Adaptee:被适配者,以前的接口
- Target:最后转换成的目标
- 继承一个类(is a)
- 我遵循这个类定义的接口规范
- Adapter具备Target的接口规范
- 组合一个类(has a)
- 支持一个类的实现方式
初始设计
//目标接口(新接口)
class ITarget{
public:
virtual void process()=0;
};
//遗留接口(老接口)
class IAdaptee{
public:
virtual void foo(int data)=0;
virtual int bar()=0;
};
//遗留类型
class OldClass: public IAdaptee{
//....
};
//对象适配器
class Adapter: public ITarget{ //继承
protected:
IAdaptee* pAdaptee;//组合
public:
Adapter(IAdaptee* pAdaptee){
this->pAdaptee=pAdaptee;
}
// 老接口转换成新接口
virtual void process(){
int data=pAdaptee->bar();
pAdaptee->foo(data);
}
};
//类适配器
class Adapter: public ITarget,
protected OldClass{ //多继承
}
int main(){
IAdaptee* pAdaptee=new OldClass();
// 旧的类放到适配器后,可以当新的类用
ITarget* pTarget=new Adapter(pAdaptee);
pTarget->process();
}
- 设计模式的应用
- 设计模式有很多变体模式
- 和经典的定义不同,不需要精确符合定义
- 关键
- 识别设计模式的场景
- 解决问题的手法
- 对变化点和稳定点的分离
要点总结
- Adapter主要应用于
- 希望复用一些现有的类
- 但接口又和应用环境不一致的情况
中介者模式
动机
- 在软件的构建中,会出现多个对象互相关联交互的情况
- 对象之间常常会维持一种复杂的引用关系
- 如果遇到一些需求更改
- 这种直接引用的关系会面临许多更改
- 比如
- 界面和数据之间的关系
- 界面的控件会引用数据对象
- 数据对象也会关联多个界面控件
- 使用中介对象来管理对象的关联关系
- 避免相互交互的对象间的紧耦合引用关系
- 更好的抵御变化
模式定义
- 用一个中介对象来封装一系列的对象交互
- 中介者使对象不需要显示的相互引用
- 编译时依赖-> 运行时依赖
- A –> IB
- B –> IB
- 在编译时A不依赖B,但在运行时A依赖B
- 从而使耦合变松散(管理变化)
- 可以独立的改变它们之间的交互
- 编译时依赖-> 运行时依赖
结构
- Colleague内部有一个指针指向了Mediator
- ConcreteMediator中有指针指向了 ConcreteColleague1/2
- 核心
- Colleague之间没有依赖关系
- 本来它们之间有依赖关系的,现在没有了
- 它们都依赖Mediator
- 而 Mediator 内部又会依赖 Colleague
- 所以 Mediator 和 Colleague 之间是双向依赖关系
- 但 Colleague 之间不再有依赖
- 达到依赖的解耦
- 下面的对象之间先是随意的相互依赖,现在大家都依赖中介者Mediator
- 当1要通知3时,1要通知M,再由M通知3
- 这时需要定义一个消息通知机制
要点总结
- 将密集的、大量的多个对象间复杂的关联关系解耦
- 将多个对象间的控制逻辑集中管理
- 将多个对象的关联变为多个对象和一个中介者的关联
- 随着控制逻辑的复杂
- Mediator具体对象的实现可能很复杂
- 这时可以对Medaitor对象进行分解处理
- Facade是解耦系统间(单向)的关联关系
- Meidator是解耦系统内各对象间(双向)的关联关系