目录
1.概述
2.结构
3.实现示例
4.使用场景
5.总结
1.概述
外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种降低系统复杂度的模式,通过向客户端提供一个统一的接口,来隐藏系统的复杂性。
外观模式是为了解决类与类之家的依赖关系的,像Java的spring框架一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度。
2.结构
外观模式的UML结构图如下所示:
角色定义:
外观类(Facade): 提供一个简化的接口,封装了系统的复杂性。外观模式的客户端通过与外观对象交互,而无需直接与系统的各个组件打交道。外观类知道哪些子类复杂处理请求,将客户的请求代理给适当的子系统。
子系统(Subsystem): 由多个相互关联的类组成,实现子系统的功能,负责系统的具体功能。外观对象通过调用这些子系统来完成客户端的请求,处理Facade对象指派的任务。注意子类中没有Facade的任何信息,即没有对Facade对象的引用。
客户端(Client): 使用外观对象来与系统交互,而不需要了解系统内部的具体实现。
3.实现示例
在C++中,外观模式可以通过定义一个接口(或类)来封装一组子系统的接口,从而提供一个更高层次、更简单的接口给客户端使用。以下是一个简单的C++外观模式示例,模拟了一个家庭自动化系统的场景。
首先,我们定义几个子系统的接口和具体实现:
// 子系统接口
class Light {
public:
virtual ~Light() = default;
virtual void turnOn() = 0;
virtual void turnOff() = 0;
};
class TV {
public:
virtual ~TV() = default;
virtual void turnOn() = 0;
virtual void turnOff() = 0;
virtual void setChannel(int channel) = 0;
};
// 子系统具体实现
class ConcreteLight : public Light {
public:
void turnOn() override {
std::cout << "Light is on.\n";
}
void turnOff() override {
std::cout << "Light is off.\n";
}
};
class ConcreteTV : public TV {
public:
void turnOn() override {
std::cout << "TV is on.\n";
}
void turnOff() override {
std::cout << "TV is off.\n";
}
void setChannel(int channel) override {
std::cout << "TV is set to channel " << channel << ".\n";
}
};
接下来,我们定义外观类,它封装了子系统的接口并提供了一个简单的接口给客户端使用:
// 外观类
class HomeAutomationFacade {
private:
Light* light;
TV* tv;
public:
HomeAutomationFacade() : light(new ConcreteLight()), tv(new ConcreteTV()) {}
~HomeAutomationFacade() {
delete light;
delete tv;
}
void turnOnAll() {
light->turnOn();
tv->turnOn();
}
void turnOffAll() {
light->turnOff();
tv->turnOff();
}
void watchMovie(int channel) {
tv->turnOn();
tv->setChannel(channel);
light->dim(); // 假设灯有调暗的功能,但不在Light接口中定义
}
// 注意:dim()方法可能不是Light接口的一部分,但为了演示,我们在这里调用它
// 在实际应用中,你可能需要扩展Light接口或外观类来处理这种情况
void dimLight() {
std::cout << "Light is dimmed.\n";
// 这里假设dim是一个简单的输出,实际中可能涉及更复杂的逻辑
}
};
最后,客户端代码使用外观类来与系统进行交互:
int main() {
HomeAutomationFacade facade;
facade.turnOnAll(); // 打开所有设备
facade.watchMovie(5); // 观看5频道的电影,自动打开电视和调暗灯光
facade.turnOffAll(); // 关闭所有设备
return 0;
}
在这个例子中,HomeAutomationFacade
是外观类,它封装了Light
和TV
子系统的接口,并为客户端提供了一个简单的接口(turnOnAll
、turnOffAll
、watchMovie
等)。客户端只需要与外观类交互,而不需要了解子系统的具体实现细节。
4.使用场景
1)当一个系统需要提供一个简单的接口来访问子系统时,可以使用外观模式。
2)当客户端与多个子系统之间存在复杂的依赖关系时,可以使用外观模式来简化这些依赖关系。
3)当需要构建一个层次结构的系统时,可以使用外观模式来定义系统中每一层的入口点。
5.总结
优点
1)降低耦合度:即减少相互依赖,客户端与子系统之间的耦合度降低,客户端只与外观角色交互,不需要了解子系统的具体实现。
2)简化操作:客户端不再需要了解子系统的内部实现和具体细节,只需要与外观角色交互,简化了客户端的操作。
3)易于维护:当子系统内部发生变化时,只需要修改外观角色,而不需要修改客户端代码,降低了维护成本。
缺点
1)不符合“开闭原则”:如果新增子系统或删除子系统,可能需要修改外观角色的代码,这在一定程度上违反了“开闭原则”。
2)可能隐藏了子系统的复杂性:如果外观角色设计得过于复杂,可能会隐藏子系统的复杂性,使得客户端难以理解和使用。