原型設(shè)計(jì)模式是一種很簡(jiǎn)單的設(shè)計(jì)模式首昔,其實(shí)就是通過(guò)clone
方法去復(fù)制一個(gè)對(duì)象,也就是Java中的Cloneable
接口。原型模式是很多語(yǔ)言的特性之一绎签,包括Java和Kotlin。
核心思想:復(fù)制代替構(gòu)造酝锅。
Java Cloneable
原型設(shè)計(jì)模式對(duì)應(yīng)了Effective Java的第十三條謹(jǐn)慎覆蓋clone诡必。
Cloneable 接口并沒(méi)有包含任何方法,那么它到底有什么作用呢搔扁?它決定了 Object中受保護(hù)的 clone方法實(shí)現(xiàn)的行為:如果一個(gè)類實(shí)現(xiàn)了 Cloneable, Object 的 clone方法就返回該對(duì)象的逐域拷貝爸舒,否則就會(huì)拋出 CloneNotSupportedException 異常。這是接口的一種極端非典型的用法稿蹲,也不值得仿效 扭勉。 通常情況下,實(shí)現(xiàn)接口是為了表明類可以為它的客戶做些什么 。 然而,對(duì)于 Cloneable 接口,它改變了超類中受保護(hù)的方法的行為觉至。事實(shí)上璧尸,實(shí)現(xiàn) Cloneable 接口的類是為了提供一個(gè)功能適當(dāng)?shù)墓械?clone 方法 咒林。
實(shí)際上,clone 方法就是另一個(gè)構(gòu)造器爷光;必須確保它不會(huì)傷害到原始的對(duì)象垫竞,并確保正確地創(chuàng)建被克隆對(duì)象中的約束條件(invariant)。
簡(jiǎn)而言之蛀序,所有實(shí)現(xiàn)了 Cloneable 接口的類都應(yīng)該覆蓋 clone 方法欢瞪,并且是公有的方法,它的返回類型為類本身徐裸。 該方法應(yīng)該先調(diào)用 super.clone 方法遣鼓,然后修正任何需要修正的域 。 一般情況下重贺,這意味著要拷貝任何包含內(nèi)部“深層結(jié)構(gòu)”的可變對(duì)象骑祟,并用指向新對(duì)象的引用代替原來(lái)指向這些對(duì)象的引用 。 雖然气笙,這些內(nèi)部拷貝操作往往可以通過(guò)遞歸地調(diào)用 clone 來(lái)完成次企,但這通常并不是最佳方法 。 如果該類只包含基本類型的域潜圃,或者指向不可變對(duì)象的引用缸棵,那么多半的情況是沒(méi)有域需要修正 。 這條規(guī)則也有例外 谭期。 例如堵第,代表序列號(hào)或其他唯一 ID 值的域,不管這些域是基本類型還是不可變的隧出,它們也都需要被修正型诚。
最好提供某些其他的途徑來(lái)代替對(duì)象拷貝。 對(duì)象拷貝的更好的辦法是提供一個(gè)拷貝構(gòu)造器(copy constructor)或拷貝工廠(copy factory)鸳劳。拷貝構(gòu)造器的做法也搓,及其靜態(tài)工廠方法的變形赏廓,都比 Cloneable/clone 方法具有更多的優(yōu)勢(shì):它們不依賴于某一種很有風(fēng)險(xiǎn)的、語(yǔ)言之外的對(duì)象創(chuàng)建機(jī)制傍妒;它們不要求遵守尚未制定好文檔的規(guī)范幔摸;它們不會(huì)與 final 域的正常使用發(fā)生沖突;它們不會(huì)拋出不必要的受檢異常颤练;它們不需要進(jìn)行類型轉(zhuǎn)換既忆。
既然所有的問(wèn)題都與 Cloneable 接口有關(guān),新的接口就不應(yīng)該擴(kuò)展這個(gè)接口,新的可擴(kuò)展的類也不應(yīng)該實(shí)現(xiàn)這個(gè)接口患雇。雖然 final 類實(shí)現(xiàn) Cloneable 接 口沒(méi)有太大的危害跃脊,這個(gè)應(yīng)該被視同性能優(yōu)化,留到少數(shù)必要的情況下才使用苛吱。 總之酪术,復(fù)制功能最好由構(gòu)造器或者工廠提供 。 這條規(guī)則最絕對(duì)的例外是數(shù)組翠储,最好利用 clone 方法復(fù)制數(shù)組绘雁。
Effective Java關(guān)于clone講了非常多,以上是其核心內(nèi)容援所,簡(jiǎn)單來(lái)說(shuō)就是不應(yīng)該使用Cloneable接口庐舟,而應(yīng)該考慮使用拷貝構(gòu)造器(copy constructor)或拷貝工廠(copy factory)來(lái)代替,這些是說(shuō)給誰(shuí)聽的住拭,當(dāng)然是說(shuō)給Kotlin聽的挪略,于是乎Kotlin在其data class中遵循了這樣的建議,提供了方便的copy
方法废酷,copy
方法其實(shí)就相當(dāng)于一個(gè)拷貝工廠瘟檩,不過(guò),需要注意的是澈蟆,copy
方法實(shí)現(xiàn)的是淺拷貝墨辛,有些時(shí)候需要深拷貝,這就需要我們自己來(lái)實(shí)現(xiàn)趴俘,可以參考也許你需要這個(gè)為數(shù)據(jù)類生成 DeepCopy 方法的庫(kù)睹簇。