
介绍
外观(Facade)模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。
在开发过程中的运用频率非常高,尤其是在现阶段各种第三方 SDK 充斥在我们的周边,而这些 SDK 很大概率会使用外观模式。通过一个外观类使得整个系统的接口只有一个统一的高层接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节。当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块、ImageLoader模块等。
优点
外观模式是“迪米特法则”的典型应用,主要优点如下:
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
- 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
缺点
- 不能很好地限制客户使用子系统类。
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
使用场景
- 为一个复杂的子系统提供一个简单接口,对外隐藏子系统的具体实现、隔离变化。
- 使用外观模式可以将一个子系统和使用它的客户端以及其它的子系统分离开来,这就提高了子系统的独立性和可移植性。
- 在构建一个层次化结构的时候,可以使用外观模式定义每一个层次对外交互的接口。这样,层与层之间只需要通过外观进行通信,从而简化层与层之间的依赖关系。
结构与实现
模式包含以下主要角色。
- Facade(外观角色):对外的统一入口。
- Sub System(子系统):一般由多个子系统构成,负责具体功能的实现。
其结构图如下图所示。

代码如下:
示例
手机就是一个外观模式的例子,它集合了电话功能、短信功能、GPS、拍照等于一身,通过手机你就可以完成各种功能。如下图所示:

下面模拟手机的外观模式实现。
测试代码:
ANDROID 源码中的实现
在用 Android 开发过程中,Context 是最重要的一个类型。Context 只是一个抽象类,它的真正实现在 ContextImpl 类中,ContextImpl 就是今天我们要分析的外观类。
在应用启动时,首先会 fork 一个子进程,并且调用 ActivityThread 的 main 方法启动该进程。ActivityThread 又会构建 Application 对象,然后和 Activity、ContextImpl 关联起来,最后会调用 Activity 的 onCreate、onStart、onResume 函数使 Activity 运行起来,此时应用的用户界面就呈现在我们面前。
main 函数会间接地调用 ActivityThread 中的 handleLaunchActivity 函数启动默认的 Activity。handleLaunchActivity 方法如下:
performLaunchActivity 方法如下:
在 handleLaunchActivity 中会调用 perfromLaunchActivity 执行 Applicaton、ContextImpl、Activity 的创建工作,并且通过 Activity 的 attach 将这 3 者关联起来。
Activity 是 Context 的子类,因此,Activity 就具有了 Context 定义的所有方法。但 Activity 并不实现具体的功能,它只是继承了 Context 的接口,并且将相关的操作交给 ContextImpl。ContextImpl 存储在 Activity 的上两层父类 ContextWrapper 中,变量名为 mBase,具体代码如下:
在 ActivityThread 类的 perfromLaunchActivity 函数中会调用 Activity 的 attach 方法将 ContextImpl 等对象关联到 Activity 中,这个 ContextImpl 最终会被 ContentWrapper 类的 mBase 字段引用。Activity 的 attach 如下:
attach 主要就是一些赋值操作。在 attach 中,调用了 attachBaseContext 函数。attachBaseContext 调用了父类 ContextWrapper 类,它就是简单地将 Context 参数传递给 mBase 字段。此时,我们的 Activity 内部就持有了 ContextImpl 的引用。
Activity 的 attachBaseContext:
Activity 在开发过程中部分充当了代理的角色,例如,当我们通过 Activity 对象调用 sendBroadcast、getResource 等函数时,实际上 Activity 只是代理了 ContextImpl 的操作,也就是内部都调用了 mBase 对象的相应方法来处理,这些方法被封装在 Activity 的父类 ContextWrapper 中。
ContextImpl 的实现如下:
ContextImpl 内部封装了很多不同子系统的操作,例如,Activity 的跳转、发送广播、启动服务、设置壁纸等,这些工作并不是在 ContextImpl 中实现,而是转交给了具体的子系统进行处理。通过 Context 这个抽象类定义了一组接口,ContextImpl 实现 Context 定义的接口,使得用户可以通过 Context 这个接口统一与 Android 系统进行交互,这样用户通常情况下就不需要对每个子系统进行了解,例如启动 Activity 时用户不需要手动调用 mMainThread.getInstrumentation().execStartActivity 启动 Activity。用户与系统服务的交互都通过 Context 的高层接口。这样对用户屏蔽了具体实现的细节,降低了使用成本。
