使程序运行更高效-原型模式

原型模式的使用场景

在创建一个实例,较复杂的或耗时的情况下,复制一个已经存在的实例能使程序运行更高效。

  • 类初始化需要耗费很多资源,通过原型拷贝避免这些耗费
  • 通过 new 产生一个对象,需要频繁的数据准备或访问权限时
  • 保护性拷贝,防止一个对象被调用者修改

原型模式的类图

  • Client: 客户端
  • Prototype: 抽象类或接口,声明具有 clone 能力
  • ConcretePrototype: 具体的原型类

简单实现

使用原型模式,实现一个文档拷贝的例子. WordDocument 表示一份用户的文档,这个文档中包含文字和图片。
用户希望编辑一份已经存在的文档,但是不确定是否采用新的编辑。因此,为了安全,将原有的文档拷贝一份,在拷贝的文档上进行操作。
原始文档就是样板实例,既原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class WordDocument : Cloneable {

var text: String = ""
var images: ArrayList<String> = ArrayList()

public override fun clone(): WordDocument {
val doc = super.clone() as WordDocument
doc.text = this.text
doc.images = this.images
return doc
}

override fun toString(): String = "doc: $text\n images: $images"
}

修改文档文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main(args: Array<String>) {
val doc = WordDocument()
doc.text = "document text"
doc.images.add("image1")
doc.images.add("image2")
println("doc1: $doc")
// 拷贝
val doc2 = doc.clone()
println("doc2: $doc2")
doc2.text = "update document text"
println("doc2: $doc2")
println("doc1: $doc")
}

输出:

1
2
3
4
5
6
7
8
9
10
11
doc1: doc: document text
images: [image1, image2]

doc2: doc: document text
images: [image1, image2]

doc2: doc: update document text
images: [image1, image2]

doc1: doc: document text
images: [image1, image2]

可见新的文档对象的文本内容修改不会影响原始的文本对象。

修改文档文本和图片内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main(args: Array<String>) {
val doc = WordDocument()
doc.text = "document text"
doc.images.add("image1")
doc.images.add("image2")
println("doc1: $doc")
// 拷贝
val doc2 = doc.clone()
println("doc2: $doc2")
doc2.text = "update document text"
// 添加一张图片
doc2.images.add("image3")
println("doc2: $doc2")
println("doc1: $doc")
}

输出:

1
2
3
4
5
6
7
8
9
10
11
doc1: doc: document text
images: [image1, image2]

doc2: doc: document text
images: [image1, image2]

doc2: doc: update document text
images: [image1, image2, image3]

doc1: doc: document text
images: [image1, image2, image3]

在新文档对象中增加的图片同时也出现在了原始对象中,这是由于 WordDocumentclone 的实现是浅拷贝造成的。由于 imagesList 类型,属于引用类型,浅拷贝传递的是对象的引用。

深拷贝,浅拷贝

对于引用类型对象,变量持有的是对象的引用,在进行拷贝时,如果只是引用赋值,就是浅拷贝,新对象中的变量就还是引用的原型对象中的变量。既 doc2.imagesdoc1.images 都引用的同一个对象。

修改 WordDocumentclone 为深拷贝:

1
2
3
4
5
6
public override fun clone(): WordDocument {
val doc = super.clone() as WordDocument
doc.text = this.text
doc.images = this.images.clone() as ArrayList<String>
return doc
}

通过深拷贝可以避免对拷贝对象的修改影响原型对象的内容。

赏杯咖啡 🍵 Donate