This page looks best with JavaScript enabled

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

 ·  ☕ 2 min read

原型模式的使用场景

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

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

原型模式的类图

@startuml

interface Prototype {
+ clone()
}

class ConcretePrototype implements Prototype{
+ clone()
}

class Client

Client .o Prototype

@enduml
  • 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")
}

输出:

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")
}

输出:

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
}

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


Yang
WRITTEN BY
Yang
Developer