概念
- 外观模式是一种结构型模式,为复杂的子系统提供一个统一的接口,使得子系统的功能对外界更加简单、易用。
![](https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/5e2a03f8805f4b718d7d4e9cf95b7754~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1736259454&x-orig-sign=753zhueJhy0ehTtCgaC4qPVOur8%3D)
与真实世界的类比
- 当你通过打电话给商店下达订单时,接线员就是该商店所有服务和部门的外观。 接线员为你提供了一个包含购物系统、支付网关、送货等服务的简单语言接口。
![](https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/a861d2c09f2f4ae88e1e1e38e6108fa6~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1736259454&x-orig-sign=ccr9Uamvm%2BaJa9VRWZONt3nZiMk%3D)
外观模式结构图
![](https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/a7733193b0e84d0eb9469e4744d5315c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1736259454&x-orig-sign=20VNbAky4QZHSYUSRbCjTrkAfiQ%3D)
public class AirCondition {
public void on() {
System.out.println("AirCondition is on");
}
public void off() {
System.out.println("AirCondition is off");
}
}
public class Light {
public void on() {
System.out.println("Light is on");
}
public void off() {
System.out.println("Light is off");
}
}
public class TV {
public void on() {
System.out.println("TV on");
}
public void off() {
System.out.println("TV off");
}
}
public class SmartAppliancesFacade {
private TV tv;
private AirCondition airCondition;
private Light light;
public SmartAppliancesFacade() {
tv = new TV();
airCondition = new AirCondition();
light = new Light();
}
public void say(String message){
if(message.contains("打开")){
allOn();
}else if(message.contains("关闭")){
allOff();
}else{
System.out.println("没有这个指令");
}
}
public void allOn() {
tv.on();
airCondition.on();
light.on();
}
public void allOff() {
tv.off();
airCondition.off();
light.off();
}
}
public class Client {
public static void main(String[] args) {
SmartAppliancesFacade facade = new SmartAppliancesFacade();
facade.say("打开家电");
System.out.println("-----------------------------------------");
facade.say("关闭家电");
System.out.println("-----------------------------------------");
facade.say("shfsahf");
System.out.println("-----------------------------------------");
}
}
适用的应用场景
-
- 如果一个系统由多个模块组成,对外提供多个复杂接口,可以通过外观模式提供一个简单的接口调用。
- 例如:支付系统涉及签名、网络请求、结果处理,通过 Facade Pattern 封装为一个简单的支付接口。
-
- 在分层系统种,可以在子系统的每一层使用 Facade Pattern ,减少高层模块对低层模块的依赖。
-
- 当需要对已有系统添加新的功能或优化时,但不想破坏原有设计,可以通过 Facade Pattern 封装已有系统,提供新的接口。
优点
- 降低客户端与子系统的耦合:
-
- Client 无需了解 Subsystem 的具体实现。
- 提高子系统的灵活性:
-
- Subsystem 可以在不影响 Client 的情况下自由修改。
- 提高可维护性:
-
- Subsystem 的接口变化只需要修改 Facade,而不需要修改 Client 代码
缺点
- 外观类可能会成为上帝对象 (了解过多或者负责过多的对象)
- 在一定程度上违反开闭原则,子系统添加新功能,可能需要修改外观类。
-
- 使用抽象外观类:将
Facade
定义为一个 abstract class
或 interface
,提供基本的功能接口。每次扩展时,可以通过创建新的外观类来实现拓展,替代了直接修改原有的 Facade
。
public abstract class AbstractSmartHomeFacade {
public abstract void startMode();
public abstract void stopMode();
}
public class SmartHomeFacade extends AbstractSmartHomeFacade {
private Light light;
private SoundSystem soundSystem;
private AirConditioner airConditioner;
public SmartHomeFacade() {
this.light = new Light();
this.soundSystem = new SoundSystem();
this.airConditioner = new AirConditioner();
}
@Override
public void startMode() {
light.turnOn();
soundSystem.playMusic();
airConditioner.turnOn();
}
@Override
public void stopMode() {
light.turnOff();
soundSystem.stopMusic();
airConditioner.turnOff();
}
}
public class AdvancedSmartHomeFacade extends SmartHomeFacade {
private Curtain curtain;
public AdvancedSmartHomeFacade() {
super();
this.curtain = new Curtain();
}
@Override
public void startMode() {
super.startMode();
curtain.close();
}
@Override
public void stopMode() {
super.stopMode();
curtain.open();
}
}
-
- 使用组合代替继承:通过组合的方式,将新增的功能封装成独立的类,再将其组合进外观类,替代直接
Facade
的代码。
public class SmartHomeFacade {
private List<Object> subsystems = new ArrayList<>();
public void addSubsystem(Object subsystem) {
subsystems.add(subsystem);
}
public void startAll() {
for (Object subsystem : subsystems) {
}
}
public void stopAll() {
for (Object subsystem : subsystems) {
}
}
}
public class Client {
public static void main(String[] args) {
SmartHomeFacade facade = new SmartHomeFacade();
facade.addSubsystem(new Light());
facade.addSubsystem(new SoundSystem());
facade.addSubsystem(new AirConditioner());
System.out.println("Starting all subsystems...");
facade.startAll();
System.out.println("\nStopping all subsystems...");
facade.stopAll();
}
}
案例实现
![](https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/2699823d18804be5ab3fac6226f943e9~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1736259454&x-orig-sign=0jS7hQqR3XFXrv9FscksqBdEOUc%3D)
在源码中的应用——Tomcat
- 使用 Tomcat 作为 Web 容器时,Coyote 接受浏览器发送的请求,封装为
Request
。为了符合 Servlet API 标准,Tomcat 使用 RequestFacade
(实现了 HttpServletRequest
接口)对内部的 Request
对象进行包装。
- 所以,在 Servlet 中开发者接触到的
Request
其实是 RequestFacade
,通过 Facade 模式,隐藏了实现细节,确保安全性和封装性。
![](https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/0e17e82c072c43ab974fa872c2993852~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1736259454&x-orig-sign=2PqggN%2BP969%2BDU3zCzW%2BVAK8qXs%3D)
与其他设计模式的关系
- 外观模式为现有对象定义了一个新接口, 适配器模式则会试图运用已有的接口。适配器通常只封装一个对象,外观通常会作用于整个对象子系统上。
- 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观。
- 享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。
- 外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
-
- 外 观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
- 中 介 者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。
- 外观类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。
- 外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)