Prototype 原型模式

今天来介绍 Creational Patterns 当中的最後一个模式。

假设这里有一个 Engineer 类别,他除了有个 name 属性之外,还拥有 toolBox,可以不断加入新的工具,成为超级工具人。EngineerToolBox 的细节如下:

class Engineer {
  name: string
  toolBox: ToolBox

  constructor(name: string) {
    this.name = name
    this.toolBox = new ToolBox()
  }
}

class ToolBox {
  tools: string[]

  constructor(){
    this.tools = []
  }

  addTools(tools: string[]) {
    this.tools.push(...tools)
  }
}

接着,我们就可以产生出一个 Engineer 实例如下

const a = new Engineer('foo')
a.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])

a.name                    // 'foo'
a.toolBox.tools           // ['a', 'b', 'c', 'd', 'e']

但这时候,如果有另外一位使用者,也想要拥有一个跟 a 一样的Engineer,有一样的名字,和一样的 tools,於是就用同样的方式:先建立Engineer 实例,然後再加入 tools

const b = new Engineer('foo')
b.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])

b.name                    // 'foo'
b.toolBox.tools           // ['a', 'b', 'c', 'd', 'e']

就大功告成了!

问题

今天要建立一个 b 看起来很简单,但如果 a 本身是一个很复杂的物件,或者当中属性的资料需要经过复杂的操作或运算才能获得,那麽要建立一个跟 a 一模一样的 b,就会变得非常麻烦。

那麽,有没有一个可以让我们直接复制 a 的方式呢?

实作方式

为了让我们可以快速地复制 Engineer 的实例,我们在 Engineer 当中加入了 clone 的方法。另一方面,也需要为 ToolBox 制作 clone 方法。细节如下:

class Engineer {
  name: string
  toolBox: ToolBox

  constructor(name: string) {
    this.name = name
    this.toolBox = new ToolBox()
  }

  clone(): this {
    const clone = Object.create(this)
    clone.toolBox = this.toolBox.clone()

    return clone
  }
}

class ToolBox {
  tools: string[]

  constructor(){
    this.tools = []
  }

  addTools(tools: string[]) {
    this.tools.push(...tools)
  }

  clone(): ToolBox {
    const clone = new ToolBox()
    clone.addTools(this.tools)
    return clone
  }
}

接着,再让我们重新建立一次 a

const a = new Engineer('foo')
a.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])

然後,我们用 clone 方法来复制 a,立即得到一个跟 a 一模一样的 b

const b = a.clone()

b.name                    // 'foo'
b.toolBox.tools           // ['a', 'b', 'c', 'd', 'e']

虽然复制出来的时候是一模一样,但实际上是两个不同的、独立的个体,所以我们可以分别为他们加入新的 tool,得到不同的结果

a.toolBox.addTools(['f'])
b.toolBox.addTools(['g'])

a.toolBox.tools            // ['a', 'b', 'c', 'd', 'e', 'f']
b.toolBox.tools            // ['a', 'b', 'c', 'd', 'e', 'g']

原型模式

透过原型模式,让我们能够快速复制出同样的物件,好处是我们透过复制,我们不需要再次经历过该物件被建立的过程。

不过复制本身也带有一些挑战,如果一个物件里面引用/指向了许多其他的物件,那麽我们就不能单纯这些物件的复制 "reference" (shallow copy),而是要建立出独立物件们 (deep copy)。所以越复杂的物件,要复制它就可能有更多的挑战!


<<:  [NestJS 带你飞!] DAY21 - HTTP Module

>>:  Day22 [实作] 一对一视讯通话(2): Signaling server

[第08天]理财达人Mx. Ada-即时报价Snapshots

前言 本文说明如何查询即时报价-Snapshots作业。 程序实作 Snapshots(快照):某个...

GitHub Project Board - 看板方法

GitHub 目前提供的 Project 功能为 Board (看板),在撰写这篇文章时,GitH...

iris的routing

routing 在介绍完model验证之後要开始介绍网页程序设计的另一个观念路由routing,更明...

Day 03 - 动态调整的PM职涯规划(2)

图片来源 继续上一篇的目标设定, 有时候我觉得是因为你心中已有一个"既定的目标"...

Day26 - 使用 Guard 来实作一个马克杯的状态机

还记得在 Day 15 马克杯 装水 Guard 的例子吗? 另外一组例子,一个水瓶或是马克杯的状态...