簡介:
原型二字說明了該模式應該有一個樣板實例,用戶從這個樣板對象複製(克隆)出一個內部屬性一致的對象。被複制的實例就是原型。如果對象的創建成本比較大,而同一個類的不同對象之間差別不大(大部分字段都相同),在這種情況下,我們可以利用對已有對象(原型)進行復制(或者叫拷貝)的方式來創建新對象,以達到節省創建時間的目的。這種基於原型來創建對象的方式就叫作原型設計模式(Prototype Design Pattern),簡稱原型模式。
應用場景
1,類初始化需要很消耗資源,通過原型複製創建對象,可以避免這些資源消耗;
2,通過new創建一個對象需要很繁瑣的數據準備,可通過原型模式;
3,一個類的不同對象之間差別不大,大部分字段都相同;
實現
對於熟悉 JavaScript 語言的前端程序員來說,原型模式是一種比較常用的開發模式。這是因爲,有別於 Java、C++ 等基於類的面向對象編程語言,JavaScript 是一種基於原型的面向對象編程語言。在Java語言中,最簡單的實現就是調用的clone()方法;
案例:
原型
Book.class爲原型,複製出更多的Book對象;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
public class Book implements Cloneable{
private String name;
private ArrayList<String> mImages = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getImages() {
return mImages;
}
public void setImage(String image) {
this.mImages.add(image);
}
public void showConten() {
System.out.println("**************** Start **************");
System.out.println("name: "+ this.name);
for (String img : mImages) {
System.out.println("image name: " + img);
}
System.out.println("**************** End **************");
}
@NonNull
@Override
protected Book clone() {
try {
Book book = (Book) super.clone();
book.name = this.name;
book.mImages = this.mImages;
return book;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
複製對象
public class CloneBook {
public static void main(String[] args) {
Book book = new Book();
book.setName("活法");
book.setImage("圖1");
book.setImage("圖2");
book.showConten();
Book book1 = book.clone();
book1.setName("Android");
book1.showConten();
book.showConten();
}
}
運行:
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
**************** End **************
**************** Start **************
name: Android
image name: 圖1
image name: 圖2
**************** End **************
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
**************** End **************
淺拷貝和深拷貝
原型模式有兩種實現方法,深拷貝和淺拷貝。不管淺拷貝還是深拷貝都是原型模式的實現方式;
淺拷貝:淺拷貝只會複製對象中基本數據類型數據和引用對象的內存地址,不會遞歸地複製引用對象,以及引用對象的引用對象;
上面的Book對象的拷貝就是淺拷貝,不信可以試試,代碼如下
public class CloneBook {
public static void main(String[] args) {
Book book = new Book();
book.setName("活法");
book.setImage("圖1");
book.setImage("圖2");
book.showConten();
Book book1 = book.clone();
book1.setName("Android");
book1.setImage("圖3");//修改引用對象的數據
book1.showConten();
book.showConten();
}
}
運行結果:
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
**************** End **************
**************** Start **************
name: Android
image name: 圖1
image name: 圖2
image name: 圖3
**************** End **************
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
image name: 圖3
**************** End **************
可以看出拷貝的對象book1修改了原型中的引用對象的數據(mImages),結果book對象中的引用對象的數據也改變了;book和book1同時指向mImages的同一個對象地址;
深拷貝:深拷貝得到的是一份完完全全獨立的對象。
修改Book類中的clone方法
@NonNull
@Override
protected Book clone() {
try {
Book book = (Book) super.clone();
book.name = this.name;
book.mImages = (ArrayList<String>) this.mImages.clone();
return book;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
重新運行如下代碼
public class CloneBook {
public static void main(String[] args) {
Book book = new Book();
book.setName("活法");
book.setImage("圖1");
book.setImage("圖2");
book.showConten();
Book book1 = book.clone();
book1.setName("Android");
book1.setImage("圖3");//修改引用對象的數據
book1.showConten();
book.showConten();
}
}
結果:book1修改了引用類型對象中的數據,對book沒有任何影響;這就是深拷貝;
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
**************** End **************
**************** Start **************
name: Android
image name: 圖1
image name: 圖2
image name: 圖3
**************** End **************
**************** Start **************
name: 活法
image name: 圖1
image name: 圖2
**************** End **************