概念
- 外观模式是一种结构型模式,为复杂的子系统提供一个统一的接口 ,使得子系统的功能对外界更加简单、易用。
与真实世界的类比
- 当你通过打电话给商店下达订单时,接线员就是该商店所有服务和部门的外观。 接线员为你提供了一个包含购物系统、支付网关、送货等服务的简单语言接口。
外观模式结构图
// 空调
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) { // 统一调用子系统的 start 方法 // 可以通过接口或反射实现 } } public void stopAll() { for (Object subsystem : subsystems) { // 统一调用子系统的 stop 方法 } }
}
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(); }
}
-
案例实现
在源码中的应用------Tomcat
- 使用 Tomcat 作为 Web 容器时,Coyote 接受浏览器发送的请求,封装为
Request
。为了符合 Servlet API 标准,Tomcat 使用RequestFacade
(实现了HttpServletRequest
接口)对内部的Request
对象进行包装。 - 所以,在 Servlet 中开发者接触到的
Request
其实是RequestFacade
,通过 Facade 模式,隐藏了实现细节,确保安全性和封装性。
与其他设计模式的关系
- 外观模式为现有对象定义了一个新接口, 适配器模式则会试图运用已有的接口。适配器 通常只封装一个对象,外观通常会作用于整个对象子系统上。
- 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观。
- 享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。
- 外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
-
- 外 观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
- 中 介 者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。