初探外观模式

代码链接

koonchen/design-patterns/facade

要点

为子系统中的一组接口提供一个一致的界面, Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

假设我们拥有一个编程器,这个编译器包含一些类,比如: Scanner 、 Parser 、 ProgramNode 、 BytecodeStream 和 ProgramNodeBuilder 等,用于实现编译器。大多数的用户仅仅将使用编译器完成编译工作,子系统中那些功能强大但层次较低的接口只会让他们将任务复杂化。

现在,我们用一个 Compiler 类来定义这个编译器的统一接口,它是一个外观,给用户提供了一个统一而简单的编译器子系统接口。这方便了大多数人使用,同时也对懂得使用底层功能的人展示它的底层功能。

什么情况下适用外观模式:

  • 当我们要为一个复杂子系统提供一个简单接口的时候:子系统往往因为不断演化而变得越来越复杂。这给那些不需要定制子系统的用户适用上造成困难,而 Facade 可以提供一个简单的缺省视图,这一视图对大多数用户而言已经足够,而需要定制的用户则可以通过 Facade 层来使用子系统。
  • 客户程序与抽象类的实现之间存在很大的依赖性:引入 Facade 将这个子系统与客户以及其他的子系统进行分离,可以提高子系统的独立性和可移植性。
  • 使用 Facade 作为子系统的入口:如果子系统之间相互通讯,那么 Facade 可以简化它们的依赖关系。

Facade 模式的参与者:

  • Facade ( Compiler ) : 知道哪些子系统负责处理请求,将用户的请求恰当发送。
  • Subsystem classes ( Scanner 等 ):实现子系统的功能,处理 Facade 指派。

PS: 没有从子系统指向 Facade 的指针。

该系统的结构如下:

cover

从协作角度出发,我们可以这样理解这个系统:

  • 客户程序通过发送请求给 Facade 的方式与子系统通讯, Facade 将这些消息转发给适当的子系统对象。
  • 使用 Facade 的客户程序不需要直接直接访问子系统对象。

Facade 模式具有以下优点:

  • 它对客户屏蔽子系统组件,因为减少了客户处理的对象的数目并使得子系统使用起来更加方便。
  • 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的,松耦合关系使得子系统的组件变化不会影响到它的客户。 Facade 模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。
  • 如果应用需要,它并不限制它们使用子系统。因此我们可以在系统易用性和通用性之间加以选择。

实现

在实现上,我们需要注意以下几点:

  • 降低客户 - 子系统之间的耦合度:用抽象类实现 Facade 而它的具体子类对应于不同的子系统实现,这可以进一步降低客户与子系统的耦合度。现在,客户可以通过抽象的 Facade 类接口与子系统通讯。
  • 公共子系统类与私有子系统类:一个子系统与一个类的相似之处在于,他们都有接口并且他们都封装了一些东西——类封装了状态和操作,而子系统封装了一些类。

java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

class BaseSystem {
void operation() {
System.out.println("BaseSystem instance has been created");
}
void otherOperation() {}
}

class Scanner extends BaseSystem {
@Override
void operation() {
System.out.println("Scanner instance has been created");
}
}

class Stream extends BaseSystem {
@Override
void operation() {
System.out.println("Stream instance has been created");
}
}

class CompilerFacade {
List<BaseSystem> subSystems;

public CompilerFacade() {
subSystems = new CopyOnWriteArrayList<>();
subSystems.add(new Scanner());
subSystems.add(new Stream());
}

void makeActions() {
subSystems.forEach(item -> {
item.operation();
});
}
}

public class FacadeClient {
public static void main(String[] args) {
CompilerFacade facade = new CompilerFacade();
facade.makeActions();
}
}

facade 在这里的实现就是一个子系统的入口。

python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python
# coding:utf8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

class BaseSystem(object):
def operation(self):
print('BaseSystem instance has been created')

class Scanner(BaseSystem):
def operation(self):
print('Scanner instance has been created')

class Stream(BaseSystem):
def operation(self):
print('Stream instance has been created')

class CompilerFacade(object):
def __init__(self):
self._scanner = Scanner()
self._stream = Stream()
self._sub_systems = [self._scanner, self._stream]
def operation(self):
[i.operation() for i in self._sub_systems]

if __name__ == '__main__':
facade = CompilerFacade()
facade.operation()

实现大同小异。

PS: 妥协了,从上一章开始,就改用 pyhton 的四格缩进了…

  • Abstract Factory 模式可以与 Facade 模式一起使用以提供一个接口,这一个接口可用来与一种子系统独立地方式创建子系统对象。当然, Abstract Factory 也可以代替 Facade 来隐藏那些平台相关的类,就是还需要管理子类,更显复杂。

  • Mediator 模式还没有开始,它与 Facade 相似的地方是,它抽象了一些已有的类的功能。然而 Mediator 的目的是对同事之间的任意通讯进行抽象,通常集中不属于任何单个对象的功能。它的同事知道中介者,并与其通讯。而 Facade 模式仅对子系统的接口进行抽象,它不会定义新的功能,子系统也不知道 Facade 的存在。

  • 通常,只有一个 Facade 对象,所以它属于 Singleton 模式。