外观模式是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。它可以简化复杂系统的使用,降低客户端与子系统之间的耦合度。
使用场景
-
简化客户端接口:
当你有一个复杂的系统,包含多个子系统或类时,可以使用外观模式为客户端提供一个简单的接口,隐藏系统的复杂性。示例:在一个视频转换应用中,视频解码、音频处理、格式转换等操作都很复杂,使用外观模式提供一个统一的接口简化这些操作。
-
减少客户端与多个子系统之间的耦合:
外观模式将客户端与多个子系统解耦,使得客户端只需要与外观类交互,而不需要直接调用子系统的接口。示例:在一个电商系统中,订单处理涉及库存管理、支付处理、物流管理等子系统,使用外观模式提供一个订单处理接口,简化客户端的操作。
-
层次化系统:
当一个系统需要层次化设计时,可以使用外观模式定义子系统的入口点,使得每个子系统的使用更加明确。示例:在一个大型软件系统中,不同的模块(如用户管理、权限管理、日志管理)可以分别定义外观类,提供统一的接口供其他模块调用。
UML类图
+-------------------+
| Facade |
+-------------------+
| +operation() |
+-------------------+
|
|
+------+------+--------+
| | |
+------+ +------+ +------+
| Sub1 | | Sub2 | | Sub3 |
+------+ +------+ +------+
| +op()| | +op()| | +op()|
+------+ +------+ +------+
代码示例
假设我们有一个复杂的家庭影院系统,包括音响系统、投影仪、DVD播放器等,我们用外观模式来简化这些设备的操作。
#include <iostream>
// 子系统类:音响系统
class Amplifier {
public:
void on() { std::cout << "Amplifier on" << std::endl; }
void off() { std::cout << "Amplifier off" << std::endl; }
void setVolume(int level) { std::cout << "Setting volume to " << level << std::endl; }
};
// 子系统类:投影仪
class Projector {
public:
void on() { std::cout << "Projector on" << std::endl; }
void off() { std::cout << "Projector off" << std::endl; }
void setInput(const std::string& input) { std::cout << "Setting input to " << input << std::endl; }
};
// 子系统类:DVD播放器
class DVDPlayer {
public:
void on() { std::cout << "DVDPlayer on" << std::endl; }
void off() { std::cout << "DVDPlayer off" << std::endl; }
void play(const std::string& movie) { std::cout << "Playing movie: " << movie << std::endl; }
};
// 外观类:家庭影院外观
class HomeTheaterFacade {
public:
HomeTheaterFacade(Amplifier* amp, Projector* proj, DVDPlayer* dvd)
: amp_(amp), proj_(proj), dvd_(dvd) {}
void watchMovie(const std::string& movie) {
std::cout << "Get ready to watch a movie..." << std::endl;
amp_->on();
amp_->setVolume(10);
proj_->on();
proj_->setInput("DVD");
dvd_->on();
dvd_->play(movie);
}
void endMovie() {
std::cout << "Shutting down the movie theater..." << std::endl;
dvd_->off();
proj_->off();
amp_->off();
}
private:
Amplifier* amp_;
Projector* proj_;
DVDPlayer* dvd_;
};
// 客户端代码
int main() {
Amplifier amp;
Projector proj;
DVDPlayer dvd;
HomeTheaterFacade homeTheater(&, &proj, &dvd);
homeTheater.watchMovie("Inception");
homeTheater.endMovie();
return 0;
}
代码解读
-
子系统类(Amplifier, Projector, DVDPlayer):
- 职责:实现各自的功能,如打开、关闭、设置参数等。
- 方法:
Amplifier
提供了on
,off
,setVolume
方法。Projector
提供了on
,off
,setInput
方法。DVDPlayer
提供了on
,off
,play
方法。
-
外观类(HomeTheaterFacade):
- 职责:提供一个简单的接口,封装子系统的复杂操作。
- 方法:
watchMovie
:打开家庭影院系统并播放电影。endMovie
:关闭家庭影院系统。
-
客户端代码:
- 创建子系统对象
Amplifier
,Projector
,DVDPlayer
。 - 创建外观对象
HomeTheaterFacade
并传入子系统对象。 - 调用外观对象的
watchMovie
和endMovie
方法,简化了对家庭影院系统的操作。
- 创建子系统对象
优点
-
简化客户端接口:
客户端代码只需与外观类交互,不需要了解子系统的复杂接口,简化了客户端的使用。 -
降低客户端与子系统的耦合度:
客户端与子系统之间通过外观类进行交互,降低了耦合度,使得子系统的变化不会直接影响客户端。 -
更好的分层设计:
外观模式可以使得子系统与客户端之间的交互更加明确,有助于分层设计和模块化。
缺点
-
可能增加系统复杂性:
如果子系统本身已经很简单,引入外观模式可能会增加不必要的复杂性。 -
不符合开闭原则:
如果需要改变子系统的行为,可能需要修改外观类,违背了开闭原则(对扩展开放,对修改关闭)。
使用场景总结
- 简化客户端接口:如复杂系统中的多个子系统接口。
- 减少客户端与多个子系统之间的耦合:如电商系统中的订单处理。
- 层次化系统:如大型软件系统中不同模块的统一接口。
通过这个例子和解释,可以看到外观模式在简化接口、降低耦合和分层设计方面的强大功能。