意图

外观模式是一种结构型模式,能为程序库、框架或其他复杂类提供一个简单的接口。

问题

假设你必须在代码中使用某个复杂的库或框架中的众多对象。正常情况下,你需要负责所有对象的初始化工作、管理其依赖关系并按正确的顺序执行方法等。

最终,程序中类的业务逻辑将与第三方类的实现细节紧密耦合,使得理解和维护代码的工作很难进行。

解决方案

外观类为包含许多活动部件的复杂子系统提供一个简单的接口。与直接调用子系统相比,外观提供的功能可能比较有限,但它却包含了客户端真正关心的功能。

如果你的程序需要与包含几十种功能的复杂库整合,但只需使用其中非常少的功能,那么使用外观模式会非常方便,

例如,上传猫咪搞笑短视频到社交媒体网站的应用可能会用到专业的视频转换库,但它只需使用一个包含encode(filename, format)方法(以文件名与文件格式为参数进行编码的方法)的类即可。在创建这个类并将其连接到视频转换库后,你就拥有了自己的第一个外观。

结构

  1. 外观(Facade)提供了一种访问特定子系统功能的便捷方式,其了解如何重定向客户端请求,知晓如何操作一切活动部件。
  2. 创建附加外观(Additional Facade)类可以避免多种不相关的功能污染单一外观,使其变成又一个复杂结构。客户端和其他外观都可使用附加外观。
  3. 复杂子系统(Complex Subsystem)由数十个不同对象构成。如果要用这些对象完成有意义的工作,你必须深入了解子系统的实现细节,比如按照正确顺序初始化对象和为其提供正确格式的数据。
    子系统类不会意识到外观的存在,它们在系统内运作并且相互之间可直接进行交互。
  4. 客户端(Client)使用外观代替对子系统对象的直接调用。

实现方式

  1. 考虑能否在现有子系统的基础上提供一个更简单的接口。如果该接口能让客户端代码独立于众多子系统类,那么你的方向就是正确的。
  2. 在一个新的外观类中声明并实现该接口。外观应将客户端代码的调用重定向到子系统中的相应对象处。如果客户端代码没有对子系统进行初始化,也没有对其后续生命周期进行管理,那么外观必须完成此类工作。
  3. 如果要充分发挥这一模式的优势,你必须确保所有客户端代码仅通过外观来与子系统进行交互。此后客户端代码将不会受到任何由子系统代码修改而造成的影响,比如子系统升级后,你只需修改外观中的代码即可。
  4. 如果外观变得过于臃肿,你可以考虑将其部分行为抽取为一个新的专用外观类。

代码演示

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using System;

namespace RefactoringGuru.DesignPatterns.Facade.Conceptual
{
// The Facade class provides a simple interface to the complex logic of one
// or several subsystems. The Facade delegates the client requests to the
// appropriate objects within the subsystem. The Facade is also responsible
// for managing their lifecycle. All of this shields the client from the
// undesired complexity of the subsystem.
public class Facade
{
protected Subsystem1 _subsystem1;

protected Subsystem2 _subsystem2;

public Facade(Subsystem1 subsystem1, Subsystem2 subsystem2)
{
this._subsystem1 = subsystem1;
this._subsystem2 = subsystem2;
}

// The Facade's methods are convenient shortcuts to the sophisticated
// functionality of the subsystems. However, clients get only to a
// fraction of a subsystem's capabilities.
public string Operation()
{
string result = "Facade initializes subsystems:\n";
result += this._subsystem1.operation1();
result += this._subsystem2.operation1();
result += "Facade orders subsystems to perform the action:\n";
result += this._subsystem1.operationN();
result += this._subsystem2.operationZ();
return result;
}
}

// The Subsystem can accept requests either from the facade or client
// directly. In any case, to the Subsystem, the Facade is yet another
// client, and it's not a part of the Subsystem.
public class Subsystem1
{
public string operation1()
{
return "Subsystem1: Ready!\n";
}

public string operationN()
{
return "Subsystem1: Go!\n";
}
}

// Some facades can work with multiple subsystems at the same time.
public class Subsystem2
{
public string operation1()
{
return "Subsystem2: Get ready!\n";
}

public string operationZ()
{
return "Subsystem2: Fire!\n";
}
}


class Client
{
// The client code works with complex subsystems through a simple
// interface provided by the Facade. When a facade manages the lifecycle
// of the subsystem, the client might not even know about the existence
// of the subsystem. This approach lets you keep the complexity under
// control.
public static void ClientCode(Facade facade)
{
Console.Write(facade.Operation());
}
}

class Program
{
static void Main(string[] args)
{
// The client code may have some of the subsystem's objects already
// created. In this case, it might be worthwhile to initialize the
// Facade with these objects instead of letting the Facade create
// new instances.
Subsystem1 subsystem1 = new Subsystem1();
Subsystem2 subsystem2 = new Subsystem2();
Facade facade = new Facade(subsystem1, subsystem2);
Client.ClientCode(facade);
}
}
}

执行结果:

1
2
3
4
5
6
Facade initializes subsystems:
Subsystem1: Ready!
Subsystem2: Get ready!
Facade orders subsystems to perform the action:
Subsystem1: Go!
Subsystem2: Fire!

参考原文:外观设计模式