什么是外观模式(Facade Pattern)

外观模式(也叫门面模式)为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。它属于结构型模式。
想象一下启动一台电脑:你只需要按下一个电源按钮(统一的高层接口 - Facade)。这个简单的动作背后,电脑内部发生了很多事情:CPU 启动、内存检查、加载操作系统、启动各种服务等等(复杂的子系统)。电源按钮就是外观,它隐藏了这些复杂性,提供了一个简单的操作方式。
外观模式的核心在于提供一个简化的入口点来访问复杂系统。

核心思想/步骤

  1. 识别子系统: 确定构成复杂功能的一组类、接口或模块(这些就是子系统)。这些子系统类通常各自负责一部分具体的功能。
  1. 创建外观类 (Facade): 定义一个新的类,即外观类。
  1. 外观类了解子系统: 外观类内部持有它所封装的子系统各个对象的引用。它知道如何协调这些子系统对象来完成特定的任务。
  1. 提供简化接口: 外观类提供一些简单、高层的方法给客户端调用。这些方法代表了客户端常用的功能组合。
  1. 委托工作: 当客户端调用外观类的方法时,外观类会将这些请求委托给内部的子系统对象进行处理,可能需要调用多个子系统对象的方法并协调它们的工作。
  1. 客户端使用外观: 客户端代码直接与外观类交互,而不需要了解或直接操作复杂的子系统。
💡
重要: 外观模式并不阻止客户端在需要时直接访问子系统类,它只是提供了一个更方便的选项。

优点

  1. 简化接口,易于使用 (Simplified Interface): 为复杂的子系统提供了一个简单的接口,降低了客户端的使用难度。
  1. 减少耦合 (Reduced Coupling): 客户端代码只需要依赖于外观类,而不需要依赖于子系统中的大量类。这使得子系统的修改对客户端的影响变小。
  1. 更好的分层 (Improved Layering): 外观模式有助于构建层次化的系统结构。外观类可以作为子系统与外部系统之间的入口点,隐藏内部实现细节。
  1. 提高子系统的独立性: 子系统内部的类可以自由演化,只要外观类提供的接口保持稳定,就不会影响到客户端。

可能的缺点

  1. 可能引入不必要的封装: 对于非常简单的系统,引入外观层可能会增加额外的复杂性。
  1. 外观类可能变成“上帝类” (God Object): 如果一个外观类试图封装过多的子系统功能,它可能会变得非常庞大和复杂,违反单一职责原则。
  1. 不阻止直接访问: 外观模式本身不强制客户端必须通过它来访问子系统,如果开发者绕过外观直接操作子系统,可能会破坏封装性或引入混乱。

使用场景

  1. 当你需要为一个复杂的子系统提供一个简单的接口时。
  1. 当你想减少客户端与子系统之间的依赖关系,以提高子系统的独立性和可移植性时。
  1. 当你需要构建一个分层结构的系统,并希望用外观模式为每一层定义入口点时。
  1. 封装第三方库: 为一个功能复杂、接口繁多的第三方库提供一个更简洁、更符合你应用需求的接口。
  1. 简化常见任务流: 将一系列需要调用多个子系统对象才能完成的常用任务封装成外观类的一个方法。
  1. Android 中的场景:
      • 网络请求封装: 创建一个 NetworkFacade,封装底层的网络库(如 Retrofit + OkHttp)、JSON 解析库(如 Gson/Moshi)、线程切换逻辑、通用错误处理等,对外只提供简单的 fetchUserData(), submitOrder() 等方法。
      • 数据库操作封装: 创建一个 DatabaseFacade 或 Repository (Repository 模式常与 Facade 结合),封装 Room Database 的 Dao 操作、数据转换、线程管理等,对外提供 getUserById(), saveUser() 等业务方法。
      • 复杂初始化序列: 应用启动时可能需要初始化多个模块(日志、统计、推送、配置等),可以创建一个 InitializationFacade,提供一个 initializeApp() 方法来统一处理。
      • 与多个 Android Framework 组件交互: 如果某个功能需要同时与 WifiManager, BluetoothAdapter, LocationManager 等多个系统服务交互,可以创建一个 Facade 来简化调用逻辑。
      • 统一管理媒体播放: 创建一个 MediaFacade 来管理 MediaPlayer/ExoPlayer 的创建、播放、暂停、停止、状态回调等复杂操作。

UML 图

下面是基于一个“家庭影院”示例的外观模式 UML 图:
  • HomeTheaterFacade (Facade): 提供 watchMovie() 和 endMovie() 两个简单方法。它持有所有子系统(Amplifier, DvdPlayer, Projector, Screen, TheaterLights)的引用。
  • Amplifier, DvdPlayer, etc. (Subsystem Classes): 代表家庭影院中的各个组件,各自有具体的控制方法。它们不知道 Facade 的存在。
  • Client: 只需要创建 HomeTheaterFacade 并调用其简单方法即可,无需关心如何一步步操作各个组件。

代码示例

在这个例子中:
  • Amplifier, DvdPlayer 等是复杂的子系统组件。
  • HomeTheaterFacade 是外观类,它封装了启动和关闭家庭影院所需的复杂步骤。
  • 客户端 (main 函数) 只需要与 HomeTheaterFacade 交互,调用 watchMovie() 和 endMovie() 即可,代码非常简洁。

总结

  • 外观模式通过提供一个统一、简化的接口来访问复杂的子系统,降低了客户端的使用难度和耦合度。
  • 它有助于构建清晰的系统层次,并隐藏内部实现细节。
  • 在 Android 开发中,常用于封装第三方库、简化常用任务流(如网络请求、数据库操作)或管理复杂的初始化过程。
💡
记住它的核心:一个简单的门面(Facade),隐藏了背后的复杂房间(Subsystem)。
 
设计模式——结构型-代理模式设计模式——结构型-桥接模式