JavaScript 中复合模式的威力

2025-05-25

JavaScript 中复合模式的威力

在Medium上找到我

在本文中,我们将探讨JavaScript 中的复合设计模式。在软件工程中,复合模式是指将一组对象视为单个对象的单个实例,从而实现这些对象及其组合的一致性。

组合的目的是将多个对象组合成某种树状结构。这种树状结构代表了一种部分-整体的层次结构

为了更详细地理解复合模式,我们必须了解部分整体是什么以及它在视觉角度上是什么样子。

简而言之,部分-整体关系本质上是指集合中的每个对象都是整体组合的一部分。这个“整体”组合是由多个部分组成的集合。现在,当我们考虑部分-整体层次结构时,它是一种树形结构,其中每个单独的“叶子”或“节点”都与树中的其他所有叶子或节点同等对待。这意味着一组或一组对象(叶子/节点的子树)也是一个叶子或节点。

从视觉角度来看,该示例最终可能看起来像这样:

JavaScript 中的复合模式视觉透视

现在我们对“部分-整体”的概念有了更清晰的理解,让我们回到“组合”这个术语。我们说过,组合的目的是将这些对象(叶子/节点)组合成一棵表示该概念的树。

因此,复合设计模式是指集合中的每个项目都可以容纳其他集合,从而使它们能够创建深度嵌套的结构。

解剖学

树结构中的每个节点共享一组通用的属性和方法,这使得它们能够支持单个对象,并将它们视为对象集合。该接口促进了递归算法的构建和设计,这些算法可以遍历复合集合中的每个对象。

谁在使用这个模式?

操作系统使用该模式,进而产生了有用的功能,例如允许我们在其他目录中创建目录。

文件(我们可以将目录中的任何内容称为“项目”,这样更有意义)是整个复合体(目录)的叶子/节点(部分)。在此目录中创建子目录也是一个叶子/节点,其中包含视频、图像等其他项目。然而,目录或子目录也是一个复合体,因为它也是部分(对象/文件/等)的集合。

React 和 Vue 等热门库广泛使用组合模式来构建健壮、可复用的界面。您在网页中看到的所有内容都表示为一个组件。网页的每个组件都是树的一片叶子,并且可以将多个组件组合在一起以创建新的叶子(当发生这种情况时,它虽然是组合的,但仍然是树的一片叶子)。这是一个强大的概念,因为它可以帮助库的使用者更轻松地进行开发,此外,它还可以非常方便地构建利用多个对象的可扩展应用程序。

我们为什么要关心这种模式?

最简单的说法是:因为它很强大。

复合设计模式之所以如此强大,是因为它能够将一个对象视为一个复合对象。这是因为它们都共享一个通用接口。

这意味着您可以重复使用对象而不必担心与其他对象不兼容。

当您开发应用程序时,如果遇到需要处理具有树状结构的对象的情况,那么在代码中采用这种模式可能是一个非常好的决定。

示例

假设我们正在为一家新公司开发一款应用程序,其主要目的是帮助医生获得远程医疗平台的资格。他们通过收集医生对法律要求的强制性文件的签名来实现这一点。

我们将创建一个Document类,该类将包含signature一个默认值为 的属性false。如果医生签署了文件,signature则应将其值转换为医生的签名。我们还将定义一个sign方法来实现此功能。

遗嘱如下Document

class Document {
  constructor(title) {
    this.title = title
    this.signature = null
  }
  sign(signature) {
    this.signature = signature
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,当我们实现复合模式时,我们将支持Document已定义的类似方法。

class DocumentComposite {
  constructor(title) {
    this.items = []
    if (title) {
      this.items.push(new Document(title))
    }
  }

  add(item) {
    this.items.push(item)
  }

  sign(signature) {
    this.items.forEach((doc) => {
      doc.sign(signature)
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,模式的美妙之处就显现出来了。请注意我们最近的两个代码片段。让我们从视觉角度来看一下:

复合模式文档1

太棒了!看来我们走对了路。我们知道这一点,因为我们得到的图和之前的图很相似:

复合模式文档2

所以我们的树结构包含两个叶子/节点,分别是DocumentDocumentComposite。它们共享相同的接口,因此它们都充当整个复合树“部分”

这里的问题是,树中非复合类型的叶子/节点(Document不是对象的集合或组,所以它就到此为止了。然而,复合类型的叶子/节点包含一个由多个部分组成的集合(在我们的例子中是items)。记住,DocumentDocumentComposite共享一个接口,共享一个sign方法。

那么它到底有多强大呢?好吧,尽管DocumentComposite共享相同的接口(因为它有一个sign和 一样的方法)Document,但它实际上实现了一种更健壮的方法,同时仍然保持了最终目标。

所以不要这样:

const pr2Form = new Document(
  'Primary Treating Physicians Progress Report (PR2)',
)
const w2Form = new Document('Internal Revenue Service Tax Form (W2)')

const forms = []
forms.push(pr2Form)
forms.push(w2Form)

forms.forEach((form) => {
  form.sign('Bobby Lopez')
})
Enter fullscreen mode Exit fullscreen mode

我们可以利用复合材料来改变我们的代码,使其更加健壮:

const forms = new DocumentComposite()
const pr2Form = new Document(
  'Primary Treating Physicians Progress Report (PR2)',
)
const w2Form = new Document('Internal Revenue Service Tax Form (W2)')
forms.add(pr2Form)
forms.add(w2Form)

forms.sign('Bobby Lopez')

console.log(forms)
Enter fullscreen mode Exit fullscreen mode

在复合方法中,我们只需要sign在添加所需文档后执行一次,它就会签署所有文档。

我们可以通过查看结果来确认这一点console.log(forms)

复合模式文档 3

在此之前的示例中,我们必须手动将项目添加到数组中,然后自己循环遍历每个文档sign

我们也不要忘记我们DocumentComposite可以容纳一系列物品。

所以当我们这样做的时候:

forms.add(pr2Form) // Document
forms.add(w2Form) // Document
Enter fullscreen mode Exit fullscreen mode

我们的图表变成了这样:

复合图案文件4

由于我们添加了两种形式,因此这与我们原来的图表非常相似:

复合图案文件5

然而,我们的树停止了,因为树的最后一片叶子只渲染了两片,这与最后一张截图并不完全相同。如果我们改为w2form像这样合成:

const forms = new DocumentComposite()
const pr2Form = new Document(
  'Primary Treating Physicians Progress Report (PR2)',
)
const w2Form = new DocumentComposite('Internal Revenue Service Tax Form (W2)')
forms.add(pr2Form)
forms.add(w2Form)

forms.sign('Bobby Lopez')

console.log(forms)
Enter fullscreen mode Exit fullscreen mode

然后我们的树就可以继续生长:

复合文档 复合 7

最后,我们仍然实现了同样的目标,即我们需要签署强制性文件:

最终结果复合设计模式

这就是复合模式的力量。

结论

这篇文章到此结束!希望你觉得这篇文章很有价值,并期待未来有更多精彩内容!

在Medium上找到我

文章来源:https://dev.to/jsmanifest/the-power-of-composite-pattern-in-javascript-2732
PREV
JavaScript 中工厂设计模式的威力
NEXT
JavaScript 中的命令设计模式