1、原型模式介紹 2、深拷貝 3、淺拷貝 4、弊端分析 5、示例代碼演示 6、改進方案 ...
java設計模式4——原型模式
1、寫在前面
本節內容與C++語言的複製構造函數、淺拷貝、深拷貝極為相似,因此建議學習者可以先瞭解C++的該部分的相關知識,或者學習完本節內容後,也去瞭解C++的相應內容,進行比對學習。
2、原型模式介紹
原型模式(Prototype Pattern)是用於創建重覆的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
這種模式是實現了一個原型介面,該介面用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的資料庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新資料庫,以此來減少資料庫調用。
3、java實現克隆的核心
1、實現一個介面(Cloneable)
2、重寫一個方法(clone())
clone()方法的源碼分析
protected native Object clone() throws CloneNotSupportedException;
由方法聲明的放回值類型=>native,可知該方法實際上是一個C++封裝好的方法,由java來進行調用,相當於C++語言的複製構造函數,但是又有所區別。
4、第一種原型模式實現(淺拷貝)
4.1、建立視頻的原型類
package com.xgp.company.創建型模式.第四種_原型模式.demo1;
import java.util.Date;
/**
* 1、實現一個介面
* 2、重寫一個方法
*/
//視頻原型類
public class Video implements Cloneable {
private String name;
private Date createTime;
/**
* 重寫克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
4.2、建立複製的客戶端類
package com.xgp.company.創建型模式.第四種_原型模式.demo1;
import java.util.Date;
/**
* 客戶端:克隆
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
//原型對象
Video v1 = new Video("狂神說java", new Date());
System.out.println("v1 = " + v1);
System.out.println("v1 = " + v1.hashCode());
System.out.println("========================");
//v1 克隆 v2
Video v2 = (Video) v1.clone();
System.out.println("v2 = " + v2);
System.out.println("v2 = " + v2.hashCode());
}
}
運行結果:
v1 = Video{name='狂神說java', createTime=Fri Feb 14 10:26:57 CST 2020}
v1 = 1836019240
========================
v2 = Video{name='狂神說java', createTime=Fri Feb 14 10:26:57 CST 2020}
v2 = 325040804
5、弊端
5.1、揭露弊端的代碼
/**
* 演示淺克隆弊端
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
//原型對象
Date date = new Date();
Video v1 = new Video("狂神說java", date);
System.out.println("v1 = " + v1);
System.out.println("v1 = " + v1.hashCode());
//v1 克隆 v2
Video v2 = (Video) v1.clone();
System.out.println("v2 = " + v2);
System.out.println("v2 = " + v2.hashCode());
System.out.println("========================");
date.setTime(22222222);
System.out.println("v1 = " + v1);
System.out.println("v1 = " + v1.hashCode());
//v1 克隆 v2
System.out.println("v2 = " + v2);
System.out.println("v2 = " + v2.hashCode());
}
5.2、弊端代碼的運行結果
v1 = Video{name='狂神說java', createTime=Fri Feb 14 10:29:02 CST 2020}
v1 = 1836019240
v2 = Video{name='狂神說java', createTime=Fri Feb 14 10:29:02 CST 2020}
v2 = 325040804
========================
v1 = Video{name='狂神說java', createTime=Thu Jan 01 14:10:22 CST 1970}
v1 = 1836019240
v2 = Video{name='狂神說java', createTime=Thu Jan 01 14:10:22 CST 1970}
v2 = 325040804
5.3、弊端分析
從運行結果可以看出,當改變一個被v1,v2都引用的時間對象時,兩者都發生了改變,沒有將引用的對象進行複製,因此稱之為淺拷貝。
5.4、模型圖如下:
通過上面的模型圖可以看到,被引用的對象還是只有一份,因此存在很大的弊端。在C++中,如果存在這種引用關閉,當程式員兩次刪除對象時,會出現第二次刪對象時發生異常的情況。雖然在java語言中,有著gc機制,不需要程式員手動的去釋放對象,不會出現重覆刪除的錯誤。但是,當被引用的對象發生改變時,很有可能會發生數據上的不正確。
6、改進(深拷貝模式)
6.1、我們期望想要改進後的模型圖如下:被引用的對象也複製了一份,互不幹擾。
6.2、實現方式:改造重寫的克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
/**
* 改造克隆方法,進行生克隆
*/
Object obj = super.clone();
Video v = (Video) obj;
//將對象的屬性進行克隆
v.createTime = (Date) this.createTime.clone();
return obj;
}
6.3、此時的運行結果為:
v1 = Video{name='狂神說java', createTime=Fri Feb 14 10:37:33 CST 2020}
v1 = 1836019240
v2 = Video{name='狂神說java', createTime=Fri Feb 14 10:37:33 CST 2020}
v2 = 325040804
========================
v1 = Video{name='狂神說java', createTime=Thu Jan 01 14:10:22 CST 1970}
v1 = 1836019240
v2 = Video{name='狂神說java', createTime=Fri Feb 14 10:37:33 CST 2020}
v2 = 325040804