外观模式(Facade Pattern)是一种结构型设计模式,它为复杂系统提供一个简化的接口,隐藏系统内部的复杂性,使客户端能够更轻松地使用系统。这种模式通过创建一个外观类,封装系统内部的交互逻辑,客户端只需与外观类交互,而无需了解系统内部的细节。
外观模式的核心角色
- Facade(外观类):提供简化的接口,封装系统内部的复杂交互
- Subsystem(子系统):由多个类组成的复杂系统,实现具体功能
- Client(客户端):通过外观类使用系统功能,无需直接与子系统交互
C++实现示例
以下以"家庭影院系统"为例实现外观模式,家庭影院包含投影仪、音响、播放器等多个设备(子系统),外观类提供简化的接口来控制整个观影流程:
#include <iostream>
#include <string>// 子系统1:投影仪
class Projector {
public:void on() {std::cout << "投影仪开启" << std::endl;}void off() {std::cout << "投影仪关闭" << std::endl;}void setInput(const std::string& source) {std::cout << "投影仪输入源设置为: " << source << std::endl;}void setMode(const std::string& mode) {std::cout << "投影仪模式设置为: " << mode << std::endl;}
};// 子系统2:音响
class SoundSystem {
public:void on() {std::cout << "音响开启" << std::endl;}void off() {std::cout << "音响关闭" << std::endl;}void setVolume(int level) {std::cout << "音响音量设置为: " << level << std::endl;}void setSurroundSound(bool enable) {std::cout << (enable ? "开启" : "关闭") << "环绕声" << std::endl;}
};// 子系统3:播放器
class Player {
private:std::string currentMovie;public:void on() {std::cout << "播放器开启" << std::endl;}void off() {std::cout << "播放器关闭" << std::endl;}void loadMovie(const std::string& movie) {currentMovie = movie;std::cout << "加载电影: " << currentMovie << std::endl;}void play() {std::cout << "播放电影: " << currentMovie << std::endl;}void pause() {std::cout << "暂停播放" << std::endl;}void stop() {std::cout << "停止播放" << std::endl;}
};// 子系统4:灯光
class Lights {
public:void on() {std::cout << "灯光开启" << std::endl;}void off() {std::cout << "灯光关闭" << std::endl;}void dim(int level) {std::cout << "灯光调暗至 " << level << "% 亮度" << std::endl;}
};// 外观类:家庭影院外观
class HomeTheaterFacade {
private:// 持有子系统对象的引用Projector& projector;SoundSystem& soundSystem;Player& player;Lights& lights;public:// 构造函数,接收所有子系统对象HomeTheaterFacade(Projector& p, SoundSystem& s, Player& pl, Lights& l): projector(p), soundSystem(s), player(pl), lights(l) {}// 简化接口:准备观影void watchMovie(const std::string& movie) {std::cout << "\n=== 准备开始观影 ===" << std::endl;lights.dim(10); // 调暗灯光projector.on(); // 打开投影仪projector.setInput("播放器"); // 设置输入源projector.setMode("电影模式"); // 设置模式soundSystem.on(); // 打开音响soundSystem.setVolume(8); // 设置音量soundSystem.setSurroundSound(true); // 开启环绕声player.on(); // 打开播放器player.loadMovie(movie); // 加载电影player.play(); // 开始播放}// 简化接口:暂停电影void pauseMovie() {std::cout << "\n=== 暂停观影 ===" << std::endl;player.pause(); // 暂停播放lights.dim(50); // 调亮一点灯光}// 简化接口:结束观影void endMovie() {std::cout << "\n=== 结束观影 ===" << std::endl;lights.on(); // 打开灯光player.stop(); // 停止播放player.off(); // 关闭播放器soundSystem.off(); // 关闭音响projector.off(); // 关闭投影仪}
};// 客户端代码
int main() {// 创建子系统对象Projector projector;SoundSystem soundSystem;Player player;Lights lights;// 创建外观对象HomeTheaterFacade homeTheater(projector, soundSystem, player, lights);// 客户端通过外观接口操作复杂系统homeTheater.watchMovie("星际穿越");// 模拟观影过程中暂停homeTheater.pauseMovie();// 继续播放后结束观影homeTheater.watchMovie("星际穿越"); // 这里实际应该是继续播放,简化处理为重新开始homeTheater.endMovie();return 0;
}
代码解析
-
子系统类:
Projector
、SoundSystem
、Player
和Lights
分别代表家庭影院的各个设备,每个类都有自己的操作接口,构成了复杂系统。 -
外观类
HomeTheaterFacade
:- 持有所有子系统对象的引用,封装了子系统之间的交互逻辑
- 提供了简化的接口(
watchMovie
、pauseMovie
、endMovie
),隐藏了系统内部的复杂操作序列 - 客户端只需调用这些简单接口,即可完成复杂的观影流程
-
客户端交互:客户端不需要知道各个设备的具体操作,只需通过外观类的接口与系统交互,大大简化了使用难度。
外观模式的优缺点
优点:
- 简化客户端操作,隐藏系统内部复杂性
- 降低客户端与子系统之间的耦合度,提高系统的独立性
- 便于子系统的维护和扩展,符合开放-封闭原则
- 可以为不同的客户端提供不同的外观接口
缺点:
- 外观类可能会变得庞大,承担过多责任,成为"上帝类"
- 新增子系统功能时,可能需要修改外观类,违反开放-封闭原则
- 可能限制了客户端对系统的灵活使用(如需使用子系统的特殊功能)
适用场景
- 当系统包含多个复杂子系统,希望简化客户端使用时
- 当需要降低客户端与子系统之间的耦合度时
- 当需要为一个复杂系统提供多个入口点(不同外观)时
- 当维护遗留系统时,可通过外观类封装遗留代码,供新系统调用
常见应用:
- 框架的入口类(如
HttpClient
封装底层网络操作) - 数据库访问层(封装连接、查询、关闭等操作)
- 操作系统API封装(如Win32 API的高层封装)
- 第三方库的适配层(简化第三方库的使用)
外观模式与适配器模式的区别:外观模式是为了简化接口,而适配器模式是为了使不兼容的接口兼容;外观模式针对的是整个系统,而适配器模式针对的是单个类或接口。