理解设计模式:以宝可梦和龙珠为例,讲解外观模式!
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
原著中描述了23种经典设计模式Design Patterns: Elements of Reusable Object-Oriented Software。这些模式为软件开发中经常重复出现的特定问题提供了解决方案。
在本文中,我将描述立面模式的原理;以及如何
以及何时应用它。
立面图案:基本理念
立面模式(也拼作façade)是一种软件设计模式,常用于面向对象编程。类似于建筑中的立面,立面是一个作为前端界面的对象,它掩盖了更复杂的底层或结构代码。——维基百科
为子系统中的一组接口提供统一的接口。外观模式定义了一个更高级别的接口,使子系统更易于使用。——《设计模式:可复用面向对象软件的基础》
这种模式的主要特点是使用类来简化
复杂系统的接口。因此,这种模式解决了以下两个问题:
- 复杂的子系统更容易使用。
- 对子系统的依赖性被最小化。
总而言之,外观模式包含多个不同类的实例,这些实例必须对客户端隐藏。这就是简化接口的方式。该模式的 UML 图如下所示:
该类Facade是模块与外部客户端之间的中间件。在 UML 图中,它仅包含一个Facade类,但当接口非常复杂时,这种模式可以用于不同层之间。
立面图案:何时使用
- 这是一个复杂的系统,你需要一个简单的界面来与它进行通信。
- 由于客户端需要对系统有广泛的了解,因此代码耦合度很高。外观模式可以降低组件之间的耦合度。
- 该系统需要为每一层软件提供一个入口点。
立面图案具有以下几个优点:
- 由于外观简化了接口,代码更容易使用、理解和测试。
- 代码简洁,因为客户端/上下文未使用复杂的接口,系统更加灵活且可重用。
外观模式——示例 1:客户希望使用来自不同系统的多个类
现在我将向您展示如何使用 TypeScript 实现这种模式。在本例中,我创建了一个问题:有一个名为 `A` 的类,Client它定义了两个方法,这两个方法使用了来自不同包(`A`System1和`B` System2)的多个类。这些包由多个类组成,每个类都有多个公共方法。下面的 UML 图展示了我刚才描述的场景。
相关代码client如下:
这个方案的主要问题在于代码耦合性过高。这意味着客户端需要了解每个类的位置和工作原理。大量的导入语句是使用外观模式解决问题的第一个征兆。另一个警示信号是客户端需要对每个类的运行机制有深入的了解。
解决方案是使用外观模式,该模式包含一个Facade使用`A`System1和 ` B` 的类System2。也就是说,使用适配器模式的新 UML 图如下所示:
与客户端和外观相关的代码如下:
在新代码中,客户端将职责委托给了外观模式,但外观模式实际上执行的是客户端之前执行的相同功能。事实上,如果代码不断增加,外观模式可能会成为一种反模式,称为BLOB(https://sourcemaking.com/antipatterns/the-blob)。因此,一个好的做法是在每个包中使用外观模式,如下面的 UML 图所示:
本解决方案中与client、facade和关联的代码如下:facadeSystem1facadeSystem2
客户端与之前的版本完全相同。
FacadeSystem1外观模式使用了为每个子系统创建的外观模式。更重要的是,外观模式类只知道由`and`提供的接口FacadeSystem2。
它们FacadeSystem1只FacadeSystem2知道它们所属包的类。务必记住,每个外观模式只导出那些需要公开的类,而这些方法可能是多个内部类方法的组合。
我创建了几个 npm 脚本,在应用外观模式后运行此处显示的代码示例。
npm run example1-problemnpm run example1-facade-solution-1npm
run example1-facade-solution-2
外立面图案——示例 2:宝可梦和龙珠组合!
另一个可以使用外观模式解决的有趣问题是,当存在多个具有不同接口但可以协同工作的包时。以下 UML 图展示了这种情况:
在这种情况下,客户端使用 `A`DragonballFacade和 ` B` 包PokemonFacade。因此,客户端只需要了解这些外观模式提供的接口即可。例如,`A`DragonballFacade提供了一个名为 `value` 的方法genki,用于计算多个对象协同工作的值。另一方面,`B`PokemonFacade提供了一个名为 `value` 的方法calculateDamage,用于与它所属包中的其他类进行交互。
与该客户端关联的代码如下:
与外观相关的代码如下:
我创建了两个 npm 脚本,它们在应用外观模式后运行这里显示的两个示例。
npm run example2-problemnpm run example2-facade-solution1
外观模式的一大优势在于,它能从一个并不简单的系统简化为一个最简单的系统。例如,在龙珠包中,使用了适配器模式,但这并不影响客户端的正常行为。而宝可梦包的复杂性则更高,因为它使用了模板方法模式来处理方法calculateDamage,并使用工厂模式来创建不同的宝可梦。所有这些复杂性都被外观模式隐藏起来,对这些类的任何更改都不会影响客户端的行为,这使得我们能够创建一个更加解耦的系统。
结论
外观模式可以避免项目中的复杂性,当多个包相互通信,或者客户端需要使用多个类时,外观模式就非常适用。
最重要的不是像我演示的那样实现这个模式,而是能够识别出这个特定模式可以解决的问题,以及何时应该或不应该使用它。这一点至关重要,因为具体的实现方式会因你使用的编程语言而异。
更多更多更多……
- 设计模式:可重用面向对象软件的要素,作者:Gamma、Helm、Johnson 和 Vlissides,Addison Wesley 出版社,1995 年。
- 立面图案 — 维基百科。
- https://www.dofactory.com/javascript/facade-design-pattern
- https://github.com/sohamkamani/javascript-design-patterns-for-humans#-facade
原文发表于
* www.carloscaballero.io *














