原型模式的使用场景
在创建一个实例,较复杂的或耗时的情况下,复制一个已经存在的实例能使程序运行更高效。
- 类初始化需要耗费很多资源,通过原型拷贝避免这些耗费
- 通过
new
产生一个对象,需要频繁的数据准备或访问权限时
- 保护性拷贝,防止一个对象被调用者修改
原型模式的类图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @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") }
|
输出:
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]
|
在新文档对象中增加的图片同时也出现在了原始对象中,这是由于 WordDocument
中 clone
的实现是浅拷贝造成的。由于 images
是 List
类型,属于引用类型,浅拷贝传递的是对象的引用。
深拷贝,浅拷贝
对于引用类型对象,变量持有的是对象的引用,在进行拷贝时,如果只是引用赋值,就是浅拷贝,新对象中的变量就还是引用的原型对象中的变量。既 doc2.images
与 doc1.images
都引用的同一个对象。
修改 WordDocument
的 clone
为深拷贝:
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 }
|
通过深拷贝可以避免对拷贝对象的修改影响原型对象的内容。