JavaScript 中复合模式的威力
在Medium上找到我
在本文中,我们将探讨JavaScript 中的复合设计模式。在软件工程中,复合模式是指将一组对象视为单个对象的单个实例,从而实现这些对象及其组合的一致性。
组合的目的是将多个对象组合成某种树状结构。这种树状结构代表了一种部分-整体的层次结构。
为了更详细地理解复合模式,我们必须了解部分整体是什么以及它在视觉角度上是什么样子。
简而言之,部分-整体关系本质上是指集合中的每个对象都是整体组合的一部分。这个“整体”组合是由多个部分组成的集合。现在,当我们考虑部分-整体层次结构时,它是一种树形结构,其中每个单独的“叶子”或“节点”都与树中的其他所有叶子或节点同等对待。这意味着一组或一组对象(叶子/节点的子树)也是一个叶子或节点。
从视觉角度来看,该示例最终可能看起来像这样:
现在我们对“部分-整体”的概念有了更清晰的理解,让我们回到“组合”这个术语。我们说过,组合的目的是将这些对象(叶子/节点)组合成一棵表示该概念的树。
因此,复合设计模式是指集合中的每个项目都可以容纳其他集合,从而使它们能够创建深度嵌套的结构。
解剖学
树结构中的每个节点共享一组通用的属性和方法,这使得它们能够支持单个对象,并将它们视为对象集合。该接口促进了递归算法的构建和设计,这些算法可以遍历复合集合中的每个对象。
谁在使用这个模式?
操作系统使用该模式,进而产生了有用的功能,例如允许我们在其他目录中创建目录。
文件(我们可以将目录中的任何内容称为“项目”,这样更有意义)是整个复合体(目录)的叶子/节点(部分)。在此目录中创建子目录也是一个叶子/节点,其中包含视频、图像等其他项目。然而,目录或子目录也是一个复合体,因为它也是部分(对象/文件/等)的集合。
React 和 Vue 等热门库广泛使用组合模式来构建健壮、可复用的界面。您在网页中看到的所有内容都表示为一个组件。网页的每个组件都是树的一片叶子,并且可以将多个组件组合在一起以创建新的叶子(当发生这种情况时,它虽然是组合的,但仍然是树的一片叶子)。这是一个强大的概念,因为它可以帮助库的使用者更轻松地进行开发,此外,它还可以非常方便地构建利用多个对象的可扩展应用程序。
我们为什么要关心这种模式?
最简单的说法是:因为它很强大。
复合设计模式之所以如此强大,是因为它能够将一个对象视为一个复合对象。这是因为它们都共享一个通用接口。
这意味着您可以重复使用对象而不必担心与其他对象不兼容。
当您开发应用程序时,如果遇到需要处理具有树状结构的对象的情况,那么在代码中采用这种模式可能是一个非常好的决定。
示例
假设我们正在为一家新公司开发一款应用程序,其主要目的是帮助医生获得远程医疗平台的资格。他们通过收集医生对法律要求的强制性文件的签名来实现这一点。
我们将创建一个Document
类,该类将包含signature
一个默认值为 的属性false
。如果医生签署了文件,signature
则应将其值转换为医生的签名。我们还将定义一个sign
方法来实现此功能。
遗嘱如下Document
:
class Document {
constructor(title) {
this.title = title
this.signature = null
}
sign(signature) {
this.signature = signature
}
}
现在,当我们实现复合模式时,我们将支持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)
})
}
}
现在,模式的美妙之处就显现出来了。请注意我们最近的两个代码片段。让我们从视觉角度来看一下:
太棒了!看来我们走对了路。我们知道这一点,因为我们得到的图和之前的图很相似:
所以我们的树结构包含两个叶子/节点,分别是Document
和DocumentComposite
。它们共享相同的接口,因此它们都充当整个复合树的“部分”。
这里的问题是,树中非复合类型的叶子/节点(Document
)不是对象的集合或组,所以它就到此为止了。然而,复合类型的叶子/节点包含一个由多个部分组成的集合(在我们的例子中是items
)。记住,Document
和DocumentComposite
共享一个接口,共享一个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')
})
我们可以利用复合材料来改变我们的代码,使其更加健壮:
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)
在复合方法中,我们只需要sign
在添加所需文档后执行一次,它就会签署所有文档。
我们可以通过查看结果来确认这一点console.log(forms)
:
在此之前的示例中,我们必须手动将项目添加到数组中,然后自己循环遍历每个文档sign
。
我们也不要忘记我们DocumentComposite
可以容纳一系列物品。
所以当我们这样做的时候:
forms.add(pr2Form) // Document
forms.add(w2Form) // Document
我们的图表变成了这样:
由于我们添加了两种形式,因此这与我们原来的图表非常相似:
然而,我们的树停止了,因为树的最后一片叶子只渲染了两片,这与最后一张截图并不完全相同。如果我们改为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)
然后我们的树就可以继续生长:
最后,我们仍然实现了同样的目标,即我们需要签署强制性文件:
这就是复合模式的力量。
结论
这篇文章到此结束!希望你觉得这篇文章很有价值,并期待未来有更多精彩内容!
在Medium上找到我
文章来源:https://dev.to/jsmanifest/the-power-of-composite-pattern-in-javascript-2732