发布于 2026-01-05 4 阅读
0

理解设计模式:以宝可梦和龙珠为例讲解外观模式!DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

理解设计模式:以宝可梦和龙珠为例,讲解外观模式!

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

原著中描述了23种经典设计模式
Design Patterns: Elements of Reusable Object-Oriented Software。这些模式为软件开发中经常重复出现的特定问题提供了解决方案。

在本文中,我将描述立面模式的原理;以及如何
以及何时应用它。

立面图案:基本理念

面模式(也拼作façade)是一种软件设计模式,常用于面向对象编程。类似于建筑中的立面,立面是一个作为前端界面的对象,它掩盖了更复杂的底层或结构代码。——维基百科

为子系统中的一组接口提供统一的接口。外观模式定义了一个更高级别的接口,使子系统更易于使用。——《设计模式:可复用面向对象软件的基础》

这种模式的主要特点是使用类来简化
复杂系统的接口。因此,这种模式解决了以下两个问题:

  1. 复杂的子系统更容易使用。
  2. 对子系统的依赖性被最小化。

总而言之,外观模式包含多个不同类的实例,这些实例必须对客户端隐藏。这就是简化接口的方式。该模式的 UML 图如下所示:

该类Facade是模块与外部客户端之间的中间件。在 UML 图中,它仅包含一个Facade类,但当接口非常复杂时,这种模式可以用于不同层之间。

立面图案:何时使用

  1. 这是一个复杂的系统,你需要一个简单的界面来与它进行通信。
  2. 由于客户端需要对系统有广泛的了解,因此代码耦合度很高。外观模式可以降低组件之间的耦合度。
  3. 该系统需要为每一层软件提供一个入口点。

立面图案具有以下几个优点:

  • 由于外观简化了接口,代码更容易使用、理解和测试。
  • 代码简洁,因为客户端/上下文未使用复杂的接口,系统更加灵活且可重用

外观模式——示例 1:客户希望使用来自不同系统的多个类

现在我将向您展示如何使用 TypeScript 实现这种模式。在本例中,我创建了一个问题:有一个名为 `A` 的类,Client它定义了两个方法,这两个方法使用了来自不同包(`A`System1和`B` System2)的多个类。这些包由多个类组成,每个类都有多个公共方法。下面的 UML 图展示了我刚才描述的场景。

相关代码client如下:

这个方案的主要问题在于代码耦合性过高。这意味着客户端需要了解每个类的位置和工作原理。大量的导入语句是使用外观模式解决问题的第一个征兆。另一个警示信号是客户端需要对每个类的运行机制有深入的了解。

解决方案是使用外观模式,该模式包含一个Facade使用`A`System1和 ` B` 的类System2。也就是说,使用适配器模式的新 UML 图如下所示:

与客户端和外观相关的代码如下:

在新代码中,客户端将职责委托给了外观模式,但外观模式实际上执行的是客户端之前执行的相同功能。事实上,如果代码不断增加,外观模式可能会成为一种反模式,称为BLOBhttps://sourcemaking.com/antipatterns/the-blob)。因此,一个好的做法是在每个包中使用外观模式,如下面的 UML 图所示:

本解决方案中与clientfacade关联的代码如下:facadeSystem1facadeSystem2

客户端与之前的版本完全相同。

FacadeSystem1外观模式使用了为每个子系统创建的外观模式。更重要的是,外观模式类只知道由`and`提供的接口FacadeSystem2

它们FacadeSystem1FacadeSystem2知道它们所属包的类。务必记住,每个外观模式只导出那些需要公开的类,而这些方法可能是多个内部类方法的组合。

我创建了几个 npm 脚本,在应用外观模式后运行此处显示的代码示例。

npm run example1-problem
npm run example1-facade-solution-1
npm
run example1-facade-solution-2

外立面图案——示例 2:宝可梦和龙珠组合!

另一个可以使用外观模式解决的有趣问题是,当存在多个具有不同接口但可以协同工作的包时。以下 UML 图展示了这种情况:

在这种情况下,客户端使用 `A`DragonballFacade和 ` B` 包PokemonFacade。因此,客户端只需要了解这些外观模式提供的接口即可。例如,`A`DragonballFacade提供了一个名为 `value` 的方法genki,用于计算多个对象协同工作的值。另一方面,`B`PokemonFacade提供了一个名为 `value` 的方法calculateDamage,用于与它所属包中的其他类进行交互。

与该客户端关联的代码如下:

与外观相关的代码如下:

我创建了两个 npm 脚本,它们在应用外观模式后运行这里显示的两个示例。

npm run example2-problem
npm run example2-facade-solution1

外观模式的一大优势在于,它能从一个并不简单的系统简化为一个最简单的系统。例如,在龙珠包中,使用了适配器模式,但这并不影响客户端的正常行为。而宝可梦包的复杂性则更高,因为它使用了模板方法模式来处理方法calculateDamage,并使用工厂模式来创建不同的宝可梦。所有这些复杂性都被外观模式隐藏起来,对这些类的任何更改都不会影响客户端的行为,这使得我们能够创建一个更加解耦的系统。

结论

外观模式可以避免项目中的复杂性,当多个包相互通信,或者客户端需要使用多个类时,外观模式就非常适用。

最重要的不是像我演示的那样实现这个模式,而是能够识别出这个特定模式可以解决的问题,以及何时应该或不应该使用它。这一点至关重要,因为具体的实现方式会因你使用的编程语言而异。

更多更多更多……



原文发表于
* www.carloscaballero.io
*

文章来源:https://dev.to/carlillo/understanding-design-patterns-facade-using-pokemon-and-dragonball-examples-5868