1.概述
外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。
2.结构
- 外观角色(Facade):为多个子系统对外提供一个共同的接口,知道哪些子系统负责处理请求,将客户端的请求转发给适当的子系统对象。
- 附加外观 (Additional Facade): 类可以避免多种不相关的功能污染单一外观, 使其变成又一个复杂结构。 客户端和其他外观都可使用附加外观(非必须,根据实际情况可选)。
- 子系统角色(SubSystem):实现子系统的功能,处理外观角色指派的任务。客户可以通过外观角色访问它。子系统在整个系统中可以是一个或多个模块,每个模块都有若干类组成,这些类可能相互之间有着比较复杂的关系。
- 客户角色(Client):调用外观角色访问各个子系统的功能
3.实现
外观模式模式不但重要,而且其应用也非常广泛,如在软件项目中,我们做多表数据更新时,业务逻辑层(Service层)对数据访问层(DAO层)的调用可能包含多个步骤,除此之外还要进行事务处理,最终统一对外提供一个update()方法,如此一来上层(如控制器Controller层)便可一步调用。软件模块应该只专注于各自擅长的领域,合理明确的分工模式才能更好地整合与共享资源。这正是外观模式所解决的问题,其中外观门面类对子系统的整合与共享极大地保证了用户访问的便利性。
3.1 实例类比
现在以电脑启动过程为例,以便进一步说明外观模式。为了用户使用方便,电脑开机按钮可以控制电脑所有硬件和系统软件的启动和关闭。如果没有开机按钮,我们(客户端)需要采用最原始的方式一步一步的操作HardDrive 类、Memory 类、Processor 类方法实现启动,采用外观模式即按钮Computer类中的方法,直接调用Operation方法即可完成电脑的启动。
3.2 具体实现
#include <iostream>
#include <string>
//子系统/子模块1
class HardDrive {
public:
std::string Operation() const {
return "HardDrive: Ready!\n";
}
// ...
std::string ReadData() const {
return "Guide loading startup data...\n";
}
};
//子系统/子模块2
class Memory {
public:
std::string Operation() const {
return "Memory: Ready!\n";
}
// ...
std::string LoadOS() const {
return "Load initialization system...\n";
}
};
//子系统/子模块3
class Processor {
public:
std::string Start() const {
return "Enter user usage mode...\n";
}
};
//外观角色 具体为电脑启动按钮或关闭
class Computer {
protected:
HardDrive* hardDrive;
Memory * memory;
Processor* processor;
public:
Computer(
HardDrive *hardDrive = nullptr,
Memory *memory = nullptr,
Processor* processor = nullptr) {
this->hardDrive = hardDrive;
this->memory = memory;
this->processor = processor;
}
~Computer() {
delete hardDrive;
delete memory;
delete processor;
}
std::string Operation() {
std::string result = "computer is starting .....\n";
result += this->hardDrive->Operation();
result += this->memory->Operation();
result += "computer orders model to perform the action:\n";
result += this->hardDrive->ReadData();
result += this->memory->LoadOS();
result += this->processor->Start();
return result;
}
};
//客户端
void ClientCode(Computer *facade) {
// ...
std::cout << facade->Operation();
// ...
}
int main() {
HardDrive *hardDrive = new HardDrive;
Memory *memory = new Memory;
Processor* processor = new Processor;
Computer *facade = new Computer(hardDrive, memory, processor);
ClientCode(facade);
delete facade;
return 0;
}
3.3 运行结果
4.外观模式优缺点
优点:
- 减少相互依赖:实现了子系统与客户之间的松耦合关系,子系统的组件变化不会影响到调用它的客户类,只需调整外观类即可。
- 提高灵活性:对客户屏蔽子系统组件,减少了客户处理的对象数目,客户代码使用子系统使用起来更加容易。但并不妨碍客户直接访问子系统。
- 提高安全性:更好地划分访问层次。
- 遵循迪米特法则,即最少知道原则。
缺点:
- 当增加子系统和扩展系统行为时,可能容易带来未知风险。
- 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
- 某些情况下可能违背单一职责原则。
5 应用场景
- 减少对子系统的依赖性
- 子系统相对独立且越来越复杂,增加门面模式提供接口
- 构建多层系统结构,利用门面对象作为每层的入口,简化层间调用