- 非常感謝你閱讀本文,歡迎【👍點贊】【?收藏】【📝評論】~
- 放棄不難,但堅持一定很酷!希望我們大家都能每天進步一點點!🎉
- 本文由 二當家的白帽子 https://le-yi.blog.csdn.net/ 博客原創,轉載請注明來源,謝謝~
文章目錄
- 什么是原始模型模式
- 原始模型模式中的角色
- 抽象原型角色(Prototype)
- 具體原型角色(ConcretePrototype)
- 客戶端角色(Client)
- 使用Java內置機制實作原始模型模式
- 淺拷貝和深拷貝
- 怎么實作深拷貝
- 尾聲
什么是原始模型模式
通過給出一個原型物件指明所要創建的物件的型別,然后通過復制這個原型物件來獲取的更多的同型別的物件,
這讓我不由自主的想起克隆技術,還記得克隆羊嗎?我們接下來講的內容和克隆羊不能說關系密切,只能說毫無關系,

設計模式和編程語言無關,但是二當家的依然用Java語言去實戰舉例,而且Java有標準的實作原始模型模式的方法,
原始模型模式中的角色

- Prototype:抽象類或者一個介面,給出具體模型需要的介面,
- ConcretePrototype:繼承抽象原型模型角色,被復制的物件,
- Client:提出復制請求,
抽象原型角色(Prototype)
我們用家庭作業為抽象原型角色(Prototype),我們這里的作業是可以抄的,大家不要學哈,
package com.secondgod.prototype;
/**
* 作業
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public interface IHomework {
/**
* 抄一份
* @return
*/
IHomework copy();
/**
* 修改所有者
* @param owner
*/
void setOwner(String owner);
}
具體原型角色(ConcretePrototype)
我們用語文作業作為具體原型角色(ConcretePrototype),
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework {
/**
* 作業的所有者
*/
private String owner;
/**
* 作業標題/作業要求
*/
private String title;
/**
* 作業內容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
ChineseHomework homework = new ChineseHomework(this.getOwner(), this.getTitle(), this.getContent());
return homework;
}
}
客戶端角色(Client)
我們測驗一下,
package com.secondgod.prototype;
/**
* 測驗原始模型
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test {
public static void main(String[] args) {
// 老師讓寫作業,大當家按時完成
IHomework homework = new ChineseHomework("大當家的", "作文-最崇拜的人", "不瞞你們說,我最崇拜的是二當家的");
// 二當家的沒按時完成,決定去抄大當家的作業~
IHomework newHomework = homework.copy();
newHomework.setOwner("二當家的");
System.out.println(homework);
System.out.println(newHomework);
}
}

和我們的預期一致,Nice,二當家的竟然崇拜自己,但這是因為懶得寫作業,不是真的,
使用Java內置機制實作原始模型模式
在Object類中有這樣一個方法,Java中所有的類都繼承自Object類,也就是說所有的類內部都可以復制自己,但是卻不能直接呼叫別的類的克隆方法,也就是說有統一的方式,但是默認不可用,

我們改一下語文作業類,使用clone方法去嘗試,克隆自己,
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework {
/**
* 作業的所有者
*/
private String owner;
/**
* 作業標題/作業要求
*/
private String title;
/**
* 作業內容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
try {
return (ChineseHomework) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

這時候會報錯,因為我們還少做一件事,我們需要把語文作業類實作Cloneable介面,然而這個介面里沒有任何抽象方法,僅僅是一個標記介面,這就像注解一樣,就是為了明確宣告可以克隆,

實作介面后,則可以正確運行,
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework, Cloneable {
/**
* 作業的所有者
*/
private String owner;
/**
* 作業標題/作業要求
*/
private String title;
/**
* 作業內容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
try {
return (ChineseHomework) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

和我們自己實作copy效果一樣,clone是一個native方法,是交給本地實作的,通常是直接記憶體拷貝,
淺拷貝和深拷貝
淺拷貝:創建一個新物件,然后將當前物件的非靜態欄位復制到該新物件,如果欄位是值型別的,那么對該欄位執行復制;如果該欄位是參考型別的話,則復制參考但不復制參考的物件,因此,原始物件及其副本參考同一個物件,
深拷貝:創建一個新物件,然后將當前物件的非靜態欄位復制到該新物件,無論該欄位是值型別的還是參考型別,都復制獨立的一份,當你修改其中一個物件的任何內容時,都不會影響另一個物件的內容,
二當家的理解方式是,淺拷貝就是僅拷貝當前物件的內容,深拷貝就是遞回拷貝當前物件和當前物件的參考型別屬性的內容,直到全部都是基本資料型別的屬性為止,另外,僅僅使用clone方法是淺拷貝,
package com.secondgod.prototype;
/**
* 測驗原始模型
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test implements Cloneable {
private Field field;
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Test t = new Test();
t.setField(new Field());
Test cloneT = t.clone();
System.out.println(t == cloneT);
System.out.println(t.getField() == cloneT.getField());
}
}
class Field {
}

源物件和克隆出的新物件field屬性值是同一個物件,所以是淺拷貝,
怎么實作深拷貝
- 讓每個參考型別屬性內部都重寫clone() 方法,然后需要對所有參考型別屬性都呼叫clone方法,
package com.secondgod.prototype;
/**
* 測驗原始模型
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test implements Cloneable {
private Field field;
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
public Test deepClone() throws CloneNotSupportedException {
Test t = new Test();
t.setField(this.getField().deepClone());
return t;
}
public static void main(String[] args) throws CloneNotSupportedException {
Test t = new Test();
t.setField(new Field());
Test cloneT = t.clone();
System.out.println(t == cloneT);
System.out.println(t.getField() == cloneT.getField());
Test deepCloneT = t.deepClone();
System.out.println(t == deepCloneT);
System.out.println(t.getField() == deepCloneT.getField());
}
}
class Field implements Cloneable {
public Field clone() throws CloneNotSupportedException {
return (Field) super.clone();
}
public Field deepClone() throws CloneNotSupportedException {
// 沒有參考型別屬性
return this.clone();
}
}

這種方式需要遞回每個參考型別的屬性,要把他們的類都實作深拷貝,只到僅有基本資料型別為止,
- 利用序列化,這是個偷懶的辦法,但是二當家的覺得更安全,如果你確定是要深拷貝,使用該方式可以防止某處漏實作clone方法,而變成不完全的深拷貝,
package com.secondgod.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 測驗原始模型
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test implements Cloneable, Serializable {
private static final long serialVersionUID = 5439585691441925427L;
private Field field;
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
/**
* 淺拷貝
* @return
* @throws CloneNotSupportedException
*/
public Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
/**
* 深拷貝
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Test deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Test) ois.readObject();
}
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Test t = new Test();
t.setField(new Field());
Test cloneT = t.clone();
System.out.println(t == cloneT);
System.out.println(t.getField() == cloneT.getField());
Test deepCloneT = t.deepClone();
System.out.println(t == deepCloneT);
System.out.println(t.getField() == deepCloneT.getField());
}
}
class Field implements Serializable {
private static final long serialVersionUID = 4530741098042781181L;
}

到底使用淺拷貝還是深拷貝,要根據實際情況,但是有一點是確定的,深拷貝需要創建更多物件,占用更多記憶體,
尾聲
最后二當家的再次宣告,抄作業是不對的哦,

推薦繼續閱讀:java 設計模式實戰,建造者模式之生產線
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292937.html
標籤:java
