文章

23种设计模式-外观模式

Facade模式 外观模式

Intent/目的

提供一个统一的接口给子系统中的一组接口。Facade定义了一个更高级别的接口,使得子系统更容易使用。

Motivation/动机

将系统分为子系统有助于降低复杂性。一个常见的设计目标是最小化子系统之间的通信和依赖关系。实现这一目标的一种方法是引入一个外观对象,它提供了一个简化的接口来访问子系统的更一般的功能。

例如,考虑一个编程环境,它为应用程序提供对其编译器子系统的访问。这个子系统包含诸如ScannerParserProgramNodeBytecodeStreamProgramNodeBuilder等类,它们实现了编译器。一些特定的应用程序可能需要直接访问这些类。但是,大多数编译器的客户端通常不关心诸如解析和代码生成之类的细节;他们只是想编译一些代码。对于他们来说,编译器子系统中功能强大但低级的接口只会使他们的任务变得更加复杂。

为了提供一个更高级的接口,可以屏蔽客户端对这些类的直接访问,编译器子系统还包括一个Compiler类。这个类定义了编译器功能的统一接口。Compiler类充当了一个外观:它为客户端提供了一个简单的接口来访问编译器子系统。它将实现编译器功能的类粘合在一起,但并不完全隐藏它们。编译器外观为大多数程序员简化了工作,而对那些需要的少数人则不会隐藏低级功能。

Applicability/应用场景

  • 你想为一个复杂的子系统提供一个简单的接口。随着子系统的演变,它们通常会变得更加复杂。大多数设计模式在应用时会导致更多、更小的类。这使得子系统更具可重用性和易于定制,但对于不需要定制的客户端来说,使用起来也变得更加困难。一个外观可以为子系统提供一个简单的默认视图,对大多数客户端来说已经足够好了。只有需要更多定制性的客户端才需要查看外观之外的内容。
  • 对于一个抽象的实现类与客户端之间存在许多依赖关系。引入一个外观来解耦子系统与客户端以及其他子系统之间的关系,从而促进子系统的独立性和可移植性。
  • 你想要分层你的子系统。使用一个外观来定义每个子系统层级的入口点。如果子系统之间存在依赖关系,那么你可以通过让它们仅通过它们的外观进行通信来简化它们之间的依赖关系。

Structure/结构

Participants/角色

  • Facade (Compiler)
    • 知道哪些子系统类负责处理请求。
    • 将客户端的请求委托给适当的子系统对象。
  • subsystem classes (Scanner, Parser, ProgramNode, etc.)
    • 实现子系统功能。
    • 处理由Facade对象分配的工作。
    • 对Facade一无所知;也就是说,他们不保留对它的任何引用。

      Consequences/总结

Facade模式提供以下几种好处:

  1. 它通过屏蔽子系统的组件,减少客户端需要处理的对象数量,使得子系统更加易于使用。
  2. 它促进了子系统与其客户端之间的弱耦合。通常,子系统中的组件之间是强耦合的。弱耦合允许你更改子系统的组件而不影响其客户端。Facade有助于对系统进行分层和对象之间的依赖关系。它们可以消除复杂或循环的依赖。当客户端和子系统独立实现时,这是一个重要的结果。
    在大型软件系统中,减少编译依赖至关重要。当子系统类发生变化时,你希望通过最小化重新编译来节省时间。使用Facade减少编译依赖可以限制对重要子系统的小改动所需的重新编译量。Facade还可以简化系统到其他平台的移植,因为不用为了构建一个子系统而去构建所有其他子系统。
  3. 它不会阻止应用程序在需要时使用子系统类。因此,你可以在易用性和通用性之间做出选择。

抽象工厂(Abstract Factory)可以与Facade一起使用,以提供一种子系统独立的方式创建子系统对象的接口。抽象工厂也可以用作Facade的替代品,以隐藏特定于平台的类。

中介者(Mediator)在抽象现有类的功能方面与Facade类似。然而,中介者的目的是抽象同事对象之间的任意通信,通常集中那些不属于任何一个同事对象的功能。中介者的同事知道中介者并与之通信,而不是直接相互通信。相比之下,Facade仅抽象子系统对象的接口以使它们更易于使用;它不定义新功能,且子系统类不知道它的存在。

通常只需要一个Facade对象。因此,Facade对象通常是单例(Singleton)。

本文由作者按照 CC BY 4.0 进行授权

© Poul.Y. 保留部分权利。