設計模式-原型模式(Prototype)
阿新 • • 發佈:2018-11-24
概述
- 定義 : 指原型例項指定建立物件的種類, 並且通過拷貝這些原型建立新的物件
- 不需要知道任何建立細節, 不呼叫建構函式
- 型別 : 建立型
適用場景
- 類初始化消耗較多資源
- new產生一個物件需要非常繁瑣的過程(資料準備, 訪問許可權等)
- 建構函式比較複雜
- 迴圈體中生產大量物件時
優點
- 原型模式在效能上比直接new一個物件效能高
- 簡化建立過程
缺點
- 必須配備克隆方法
- 對克隆複雜物件或克隆出的物件進行復雜改造時, 容易引入風險
- 深克隆, 淺克隆要運用得當
模式角色
- Prototype : 宣告一個克隆自身的介面, 需要注意的是, 在Java中Object類中存在了clone方法, 所以這個角色就相當於Object類的角色了, 使用時不需要再宣告介面了, 直接覆蓋Object的clone方法即可
- ConcretePrototype : 實現一個克隆自身的操作
- Client : 即客戶端, 讓一個原型克隆自身從而建立一個新的物件
程式碼實現
- java中要實現clone, 需要實現Cloneable介面, 這是一個標記介面
- 重寫Object類的clone方法
/**
* 原型模式
*
* @author 七夜雪
* @create 2018-11-23 10:51
*/
public class Person implements Cloneable {
private String name;
private Integer age;
private String address;
private String company;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString () {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", company='" + company + '\'' +
", hashCode='" + hashCode() + '\'' +
'}';
}
// ..............Setter, Getter方法省略.................
}
測試程式碼:
public static void main(String[] args) throws Exception {
// 原型
Person prototype = new Person();
prototype.setName("蕭憶情");
prototype.setAddress("洛陽");
prototype.setCompany("聽雪樓");
prototype.setAge(18);
Person clone = (Person)prototype.clone();
System.out.println("原型:" + prototype);
System.out.println("拷貝:" + clone);
}
輸出結果:
原型:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', hashCode='356573597'}
拷貝:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', hashCode='1735600054'}
- 對於都是簡單屬性的類, 這種操作並沒有什麼問題, 如果屬性中存在複雜物件, 就可能會存在問題, 現在我們在Person類中增加一個Date型別的birthday屬性, 然後修改一下測試方法試一下:
public static void main(String[] args) throws Exception {
// 原型
Person prototype = new Person();
prototype.setName("蕭憶情");
prototype.setAddress("洛陽");
prototype.setCompany("聽雪樓");
prototype.setAge(18);
prototype.setBirthday(new Date());
Person clone = (Person)prototype.clone();
System.out.println("原型:" + prototype);
System.out.println("拷貝:" + clone);
// 這裡如果直接呼叫clone.setBirthday()方法是並沒有問題的, 因為prototype和clone是兩個不同的物件了,直接呼叫set方法的話, 只是會把clone物件的Birthday屬性指向一個新的Date物件
clone.getBirthday().setTime(211111212121L);
System.out.println("--------------更新了clone物件的birthday屬性---------------");
System.out.println("原型:" + prototype);
System.out.println("拷貝:" + clone);
}
輸出結果:
原型:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Fri Nov 23 11:15:11 CST 2018', hashCode='1836019240'}
拷貝:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Fri Nov 23 11:15:11 CST 2018', hashCode='325040804'}
--------------更新了clone物件的birthday屬性---------------
原型:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='1836019240'}
拷貝:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='325040804'}
從上面的輸出結果可以看出, 這種情況下prototype和clone物件的birthday屬性還是同一個物件, 這種就是淺克隆, 又叫淺拷貝, 這種情況就需要改寫clone方法, 對birthday屬性再進行一次clone操作, 修改後的clone方法如下:
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
clone.birthday = (Date) this.birthday.clone();
return clone;
}
修改完之後, 再執行上面的測試用例, 就會發現結果已經正確了:
原型:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='1836019240'}
拷貝:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='325040804'}
--------------更新了clone物件的birthday屬性---------------
原型:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='1836019240'}
拷貝:Person{name='蕭憶情', age=18, address='洛陽', company='聽雪樓', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='325040804'}