什么是 “原型模式” ?
作者:東風玖哥,小灰
來源:程序員小灰
—————? 第二天? —————
————————————
假如有一天,小灰被外星人抓走了,外星人要拿小灰做實驗,想了解小灰在吃得好、睡得好、玩得開心的場景下,與現實中小灰的生存狀態有什么區別。
于是,外星人克隆了幾個一模一樣的小灰:
就這樣,小灰的原型被留在現實中,而三個復制體分別提供了吃得好、睡得好、玩得開心三種不同環境,小灰的原型則不受三個復制體的影響。
過了一段時間,我們來觀察一下本體與分身的生存狀態:
在Java語言中,Object類實現了Cloneable接口,一個對象可以通過調用Clone()方法生成對象,這就是原型模式的典型應用。但需要注意的是,clone()方法并不是Cloneable接口里的,而是Object類里的,Cloneable是一個標識接口,標識這個類的對象是可被拷貝的,如果沒有實現Cloneable接口,卻調用了clone()方法,就會報錯。
//?protected?native?Object?clone()?throwsCloneNotSupportedException;protected?Object?clone()?throws CloneNotSupportedException?{if?(!(this?instanceof?Cloneable))?{throw?new?CloneNotSupportedException( "Class?"?+?getClass().getName()?+ "?doesn't?implement?Cloneable");}return?internalClone(); }//?Native?helper?method?for?cloning.private?native?Object?internalClone();?
?
Java中的數據類型,分為基本類型和引用類型。在一個方法里的變量如果是基本類型的話,變量就直接存儲在這個方法的棧幀里,例如int、long等;而引用類型則在棧幀里存儲這個變量的指針,指向堆中該實體的地址,例如String、Array等。深拷貝和淺拷貝是只針對引用數據類型的。
?
比如一個方法有一個基本類型參數和一個引用類型參數,在方法體里對參數重新賦值,會影響傳入的引用類型參數,而不會影響基本類型參數,因為基本類型參數是值傳遞,而引用類型參數是引用傳遞。
先定義一個用戶類:
//?這是一個非常簡單的用戶類public?class?User?{private?String?name;private?int?age;public?User(String?name,?int?age)?{this.name=name;this.age=age;}public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;}public?int?getAge()?{return?age;}public?void?setAge(int?age)?{this.age?=?age;}@Overridepublic?String?toString()?{return?"User{name='"?+?name?+?",?age="?+?age?+'}';}} private?int?x=10;public?void?updateValue(int?value){value?=?3?*?value; }private?User?user=?new?User("大黃",20);public?void?updateUser(User?student){student.setName("小灰");student.setAge(18); }public?void?test(){System.out.println("調用前x的值:"+x);updateValue(x);System.out.println("調用后x的值:"+x);System.out.println("調用前user的值:"+user.toString());updateUser(user);System.out.println("調用后user的值:"+user.toString());}測試:
Log打印結果如下:
調用前x的值:10 調用后x的值:10 調用前user的值:User{name='大黃,?age=20} 調用后user的值:User{name='小灰,?age=18}傳遞基本類型的方法(updateValue())流程圖:?
傳遞引用類型的方法(updateUser())流程圖:
這其中也包含著例外,比如String類型和大小不超過127的Long類型,雖然也是引用類型,卻像基本類型一樣不受影響。這是因為它們會先比較常量池維護的值,這涉及VM的內容,今天不做過多討論。
淺拷貝是在按位(bit)拷貝對象,這個對象有著原始對象屬性值的一份精確拷貝。我們結合應用場景分析一下,還是剛才的User類,我們增加一個存放地址的內部類Address,我們需要用戶信息可以被其他module查詢,但是不允許它們被其他module修改,新增代碼如下:
//?這是一個稍微復雜的、支持拷貝的用戶類public?class?User?implements?Cloneable?{ //?……省略上文代碼……private?Address?address;@NonNull@NotNull@Overridepublic?User?clone()?{try{return?(User)super.clone();}catch?(CloneNotSupportedException?e)?{e.printStackTrace();}return?null;}public?class?Address{//?地市public?String?city;//?區縣public?String?county;//?鄉鎮街道public?String?street;}? }?
需要注意的是,上面代碼的深拷貝其實并不徹底,因為徹底的深拷貝幾乎是不可能實現的,那樣不但可能存在引用關系非常復雜的情況,也可能存在引用鏈的某一級上引用了一個沒有實現Cloneable接口的第三方對象的情況。
絕大多數設計模式都是犧牲性能提升開發效率的,原型模式則是為數不多的犧牲開發效率提升性能的設計模式。
?
private?User?user=?new?User("大黃",20);public?void?testNew(){User?user1?=?new?User("小灰",18);}public?void?testClone(){User?user2?=?user.clone();}通過ASM工具查看bytecode,可以看出二者對棧資源的消耗:?
最后我們來總結一下原型模式的核心用途:
1.解決構建復雜對象的資源消耗問題,提升創建對象的效率。
2.保護性拷貝,防止外部對只讀對象進行需修改。
總結
以上是生活随笔為你收集整理的什么是 “原型模式” ?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Service Mesh微服务熔断、限流
- 下一篇: 翼方健数邓振:“DRG+AI”助力实现医