原型模式
因爲類初始化需要消耗非常多的資源,包括數據、硬件資源等,通過原型拷貝則可以避免這些消耗,而且new一個對象會經過非常繁瑣的數據準備或訪問權限,假若一個對象需要供給其他對象訪問,而且各個對象都需要修改其值時,可以拷貝多個對象供調用者訪問,即保護性拷貝,以上就是用到原型模型的場景。總而言之,及時你需要用到一個對象,還需要修改該對象部分值,保留部分值,你就可以用它。下面就結合代碼來說明一下
我們有一個對象,包含了一些文本和一些對象
原型模型,主要是實現Cloneable接口和覆寫Object的clone方法
1.淺拷貝
我們還是寫一輛車,有品牌,重量,供應商,和擁有者的信息
/**
* @author: hx
* @Time: 2019/5/6
* @Description: Car
*/
public class Car implements Cloneable {
private String brand;
private int weight;
private ArrayList<String> supplier = new ArrayList<>();
private Owner mOwner = new Owner();
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public List<String> getSupplier() {
return supplier;
}
public void setSupplier(String... factoryName) {
for (int i = 0; i < factoryName.length; i++) {
supplier.add(factoryName[i]);
}
}
public Owner getOwner() {
return mOwner;
}
public void setOwner(String name, int age) {
mOwner.setName(name);
mOwner.setAge(age);
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", weight=" + weight +
", supplier=" + supplier +
", mOwner=" + mOwner +
'}';
}
@Override
public Car clone() throws CloneNotSupportedException {
System.out.println("clone時不執行構造函數");
Car car = (Car) super.clone();
return car;
}
static class Owner {
private String name;
private int 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;
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Car car1 = new Car();
car1.setBrand("保時捷");
car1.setWeight(1895);
car1.setOwner("張三",33);
car1.setSupplier("車輪","方向盤");
System.out.println(car1.toString());
System.out.println("----------------------");
Car car2 = car1.clone();
System.out.println(car2.toString());
System.out.println("----------------------");
}
輸出結果:
Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤], mOwner=Owner{name='張三', age=33}}
----------------------
clone時不執行構造函數
Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤], mOwner=Owner{name='張三', age=33}}
----------------------
看起來並沒有什麼問題,對吧,我們來定製一下第二輛車的信息
public static void main(String[] args) throws CloneNotSupportedException {
Car car1 = new Car();
car1.setBrand("保時捷");
car1.setWeight(1895);
car1.setOwner("張三",33);
car1.setSupplier("車輪","方向盤");
System.out.println("car1="+car1.toString());
System.out.println("----------------------");
Car car2 = car1.clone();
car2.setWeight(2000);
car2.setOwner("李四",50);
car2.setSupplier("底盤");
System.out.println("car1="+car1.toString());
System.out.println("----------------------");
System.out.println("car2="+car2.toString());
System.out.println("----------------------");
}
輸出結果:
car1=Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤], mOwner=Owner{name='張三', age=33}}
----------------------
clone時不執行構造函數
car1=Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤, 底盤], mOwner=Owner{name='李四', age=50}}
----------------------
car2=Car{brand='保時捷', weight=2000, supplier=[車輪, 方向盤, 底盤], mOwner=Owner{name='李四', age=50}}
----------------------
可以看到,我們只修改了第二輛車的信息,但是第一輛車的也一起改了,只是第一輛車的原本基本類型的值沒有修改,所以可以得出結論,淺拷貝,在拷貝引用的時候,並沒有拷貝對象,只是拷貝了對象的引用,所以我們需要對非基本類型也進行拷貝,即深拷貝。
2.深拷貝
在運用原型模式時建議還是用深拷貝,下面我們把上面的淺拷貝改成深拷貝
要注意的是,拷貝的時候是不走構造函數的
/**
* @author: hx
* @Time: 2019/5/6
* @Description: Car
*/
public class Car implements Cloneable {
private String brand;
private int weight;
private ArrayList<String> supplier = new ArrayList<>();
private Owner mOwner = new Owner();
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public List<String> getSupplier() {
return supplier;
}
public void setSupplier(String... factoryName) {
for (int i = 0; i < factoryName.length; i++) {
supplier.add(factoryName[i]);
}
}
public Owner getOwner() {
return mOwner;
}
public void setOwner(String name, int age) {
mOwner.setName(name);
mOwner.setAge(age);
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", weight=" + weight +
", supplier=" + supplier +
", mOwner=" + mOwner +
'}';
}
@Override
public Car clone() throws CloneNotSupportedException {
System.out.println("clone時不執行構造函數");
Car car = (Car) super.clone();
car.supplier = (ArrayList<String>) this.supplier.clone();
car.mOwner = (Owner) this.mOwner.clone();
return car;
}
static class Owner implements Cloneable{
private String name;
private int 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;
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Owner owner = (Owner) super.clone();
return owner;
}
}
}
看一下運行結果
public static void main(String[] args) throws CloneNotSupportedException {
Car car1 = new Car();
car1.setBrand("保時捷");
car1.setWeight(1895);
car1.setOwner("張三",33);
car1.setSupplier("車輪","方向盤");
System.out.println("car1="+car1.toString());
System.out.println("----------------------");
Car car2 = car1.clone();
car2.setWeight(2000);
car2.setOwner("李四",50);
car2.setSupplier("底盤");
System.out.println("car1="+car1.toString());
System.out.println("----------------------");
System.out.println("car2="+car2.toString());
System.out.println("----------------------");
}
輸出結果:
car1=Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤], mOwner=Owner{name='張三', age=33}}
----------------------
clone時不執行構造函數
car1=Car{brand='保時捷', weight=1895, supplier=[車輪, 方向盤], mOwner=Owner{name='張三', age=33}}
----------------------
car2=Car{brand='保時捷', weight=2000, supplier=[車輪, 方向盤, 底盤], mOwner=Owner{name='李四', age=50}}
----------------------
可以看到,深拷貝的之後,再修改拷貝對象就不會對原始對象的值有所影響。
爲了防止遺漏,一般都是將所有的值都會進行一次賦值,即
@Override
public Car clone() throws CloneNotSupportedException {
System.out.println("clone時不執行構造函數");
Car car = (Car) super.clone();
car.brand = this.brand;
car.weight = this.weight;
car.supplier = (ArrayList<String>) this.supplier.clone();
car.mOwner = (Owner) this.mOwner.clone();
return car;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Owner owner = (Owner) super.clone();
owner.name = this.name;
owner.age = this.age;
return owner;
}
在android中,其實還有的會將賦值行爲放到構造函數中,然後在clone方法中直接new一個自己的對象。如Intent對象
/**
* Copy constructor.
*/
public Intent(Intent o) {
this(o, COPY_MODE_ALL);
}
private Intent(Intent o, @CopyMode int copyMode) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
this.mCategories = new ArraySet<>(o.mCategories);
}
if (copyMode != COPY_MODE_FILTER) {
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
this.mLaunchToken = o.mLaunchToken;
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (copyMode != COPY_MODE_HISTORY) {
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
} else {
if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
this.mExtras = Bundle.STRIPPED;
}
// Also set "stripped" clip data when we ever log mClipData in the (broadcast)
// history.
}
}
}
@Override
public Object clone() {
return new Intent(this);
}
執行Intent的clone方法時,實際上是重新new了一個對象,具體是選擇clone對象還是new一個對象,就看實際創建對象的成本和場景,如果構造很麻煩或者成本太高,還是選擇clone效率會高一點。
優點
- 可以解決複雜對象創建時消耗過多的問題,在某些場景下提升創建對象的效率。
- 保護性拷貝,可以防止外部調用者對對象的修改,保證這個對象是隻讀的。
缺點
- 拷貝對象時不會執行構造函數。
- 有時需要考慮深拷貝和淺拷貝的問題。