外观模式:从家庭电源控制看"简化接口"的设计智慧
一、什么是外观模式?------ 从"总控开关"说起
忙碌一天后准备入睡,若没有总控开关,你得起身逐个关闭运转的空调、亮着的台灯和播放新闻的电视,耗时又繁琐。而床头的"总控开关"只需轻轻一按,所有设备便一键关闭------这就是外观模式的核心逻辑。
外观模式(Facade Pattern)又称门面模式,是结构型设计模式的一种。它通过定义统一的高层接口,封装复杂子系统的交互逻辑,让客户端无需了解子系统内部细节,仅通过该接口就能操控整个系统。
就像餐厅服务员,你点一份双人套餐时,无需亲自对接后厨、吧台和仓库,服务员会协调各环节,将完整套餐送到你面前。外观模式就扮演这样的"服务员"角色,把客户端的简单需求拆解为子系统的具体操作,再反馈结果,不改变子系统功能,只简化交互方式。
二、外观模式的结构与UML图
外观模式结构简洁,靠两个核心角色配合实现"复杂系统简单化"。
1. 核心角色
- 外观角色(Facade):客户端直接交互的"总接口",内部持有子系统引用。它一方面将客户端指令转化为子系统能理解的调用,另一方面可将多个子系统操作打包成复合功能,比如"关闭所有设备"就是空调、台灯、电视关闭操作的组合。
- 子系统角色(Subsystem):实现具体功能的独立模块,像空调、台灯等,有自己的接口和逻辑,但从不主动与客户端交互,仅通过外观角色接收指令。
2. 通用UML图

1766846369053)
说明:客户端所有请求都指向外观类,外观类按请求调用对应子系统。就像用手机拍照,无需了解摄像头、处理器等细节,点击快门即可。
3. 家庭电源控制场景的UML图

说明:main函数是客户端,PowerSwitchFacade是外观类。客户端输入指令(如1、2、3、4)调用外观类的control()方法,外观类再调用对应子系统方法。子系统还可扩展功能,外观类也能灵活组合操作。
三、代码实现:家庭电源总开关的外观模式设计
基于小明家场景(输入1-4关设备、5-8开设备、9观影模式、10阅读模式),用C++实现外观模式。
1. 子系统类:定义设备的具体功能
cpp
#include <iostream>
#include <vector>
#include <string>
// 空调子系统:支持开关和温度调节
class AirConditioner {
private:
int temperature = 26;
bool isOn = false;
public:
void turnOn() {
if (!isOn) {
isOn = true;
std::cout << "Air Conditioner is turned on. Current temperature: " << temperature << "°C" << std::endl;
} else {
std::cout << "Air Conditioner is already on." << std::endl;
}
}
void turnOff() {
if (isOn) {
isOn = false;
std::cout << "Air Conditioner is turned off." << std::endl;
} else {
std::cout << "Air Conditioner is already off." << std::endl;
}
}
void setTemperature(int temp) {
if (isOn) {
if (temp >= 16 && temp <= 30) {
temperature = temp;
std::cout << "Air Conditioner temperature set to " << temperature << "°C" << std::endl;
} else {
std::cout << "Invalid temperature (16-30°C only)." << std::endl;
}
} else {
std::cout << "Cannot set temperature: Air Conditioner is off." << std::endl;
}
}
};
// 台灯子系统:支持开关和亮度调节
class DeskLamp {
private:
int brightness = 50;
bool isOn = false;
public:
void turnOn() {
if (!isOn) {
isOn = true;
std::cout << "Desk Lamp is turned on. Brightness: " << brightness << "%" << std::endl;
} else {
std::cout << "Desk Lamp is already on." << std::endl;
}
}
void turnOff() {
if (isOn) {
isOn = false;
std::cout << "Desk Lamp is turned off." << std::endl;
} else {
std::cout << "Desk Lamp is already off." << std::endl;
}
}
void setBrightness(int level) {
if (isOn) {
if (level >= 0 && level <= 100) {
brightness = level;
std::cout << "Desk Lamp brightness set to " << brightness << "%" << std::endl;
} else {
std::cout << "Invalid brightness (0-100 only)." << std::endl;
}
} else {
std::cout << "Cannot set brightness: Desk Lamp is off." << std::endl;
}
}
};
// 电视子系统:支持开关和频道切换
class Television {
private:
int channel = 1;
bool isOn = false;
public:
void turnOn() {
if (!isOn) {
isOn = true;
std::cout << "Television is turned on. Current channel: " << channel << std::endl;
} else {
std::cout << "Television is already on." << std::endl;
}
}
void turnOff() {
if (isOn) {
isOn = false;
std::cout << "Television is turned off." << std::endl;
} else {
std::cout << "Television is already off." << std::endl;
}
}
void changeChannel(int ch) {
if (isOn) {
if (ch >= 1 && ch <= 100) {
channel = ch;
std::cout << "Television channel changed to " << channel << std::endl;
} else {
std::cout << "Invalid channel (1-100 only)." << std::endl;
}
} else {
std::cout << "Cannot change channel: Television is off." << std::endl;
}
}
};
2. 外观类:封装总开关的控制逻辑
cpp
// 电源总开关(外观类)
class PowerSwitchFacade {
private:
AirConditioner ac;
DeskLamp lamp;
Television tv;
public:
void control(int code) {
switch (code) {
case 1: ac.turnOff(); break;
case 2: lamp.turnOff(); break;
case 3: tv.turnOff(); break;
case 4:
ac.turnOff();
lamp.turnOff();
tv.turnOff();
std::cout << "All devices are off." << std::endl;
break;
case 5: ac.turnOn(); break;
case 6: lamp.turnOn(); break;
case 7: tv.turnOn(); break;
case 8:
ac.turnOn();
lamp.turnOn();
tv.turnOn();
std::cout << "All devices are on." << std::endl;
break;
case 9:
tv.turnOn();
lamp.turnOff();
ac.turnOn();
ac.setTemperature(24);
std::cout << "Movie mode activated." << std::endl;
break;
case 10:
lamp.turnOn();
lamp.setBrightness(80);
tv.turnOff();
std::cout << "Reading mode activated." << std::endl;
break;
default: std::cout << "Invalid code (1-10 only)." << std::endl;
}
}
};
3. 客户端:通过外观类完成操作
cpp
int main() {
std::cout << "=== Home Power Control System ===" << std::endl;
std::cout << "Commands: 1-4(off), 5-8(on), 9(movie), 10(reading)" << std::endl;
std::cout << "Enter number of operations: ";
int n;
std::cin >> n;
std::vector<int> codes(n);
std::cout << "Enter commands (separated by space): ";
for (int i = 0; i < n; i++) {
std::cin >> codes[i];
}
PowerSwitchFacade mainSwitch;
std::cout << "\n=== Executing Commands ===" << std::endl;
for (int code : codes) {
mainSwitch.control(code);
}
return 0;
}
四、生活中的外观模式:无处不在的"简化接口"
外观模式在生活中随处可见,核心是用简单接口掩盖复杂流程。
- 智能手机的"快捷控制中心":点击"WiFi图标",无需了解无线网卡驱动、路由器认证等;开启"热点",不用管移动网络、WiFi、密码验证模块的协调,一个开关就能搞定。
- 网购平台的"一键下单":点击按钮后,库存检查、地址验证、支付、物流等子系统自动运作,用户无需关心各环节细节。
- 汽车的"启动按钮":无需插入钥匙、拧动点火等步骤,按钮会协调钥匙验证、发动机启动、电子设备通电等子系统。
- 银行ATM机:插入银行卡、输密码、选取款金额,背后的身份验证、账户查询、现金管理、交易记录等子系统自动处理,用户只需简单操作。
五、外观模式的价值与使用原则
1. 外观模式的核心价值
- 降低认知成本:减少用户记忆负担,无需记住每个子系统的操作逻辑,如只需记住总控开关的使用。
- 隔离系统变化:子系统迭代不影响客户端,符合"开闭原则",如空调升级后,只要接口不变,外观类和客户端无需修改。
- 简化复杂流程:将多个操作组合成一个接口,减少客户端调用次数,如"观影模式"一键实现多个设备的协调操作。
2. 使用外观模式的注意事项
- 避免外观类臃肿:按功能拆分外观,如"客厅设备外观"、"卧室设备外观",避免一个外观类负责所有子系统。
- 保留子系统直接访问权:外观是简化接口,不是唯一接口,高级用户或维修人员可能需要直接操作子系统。
- 平衡封装与灵活性:不过度封装,适当暴露子系统的高级功能,如空调的"定时关闭",满足不同需求。
参考资料
本文参考《大话设计模式》《Head First 设计模式》中对结构型设计模式的讲解,结合生活场景与代码案例进行通俗化解读,展现外观模式"简化接口"的设计智慧。