### 原型模式 #### 案例引入 ##### 克隆羊問題 有一隻羊,姓名為tom,年齡為1,顏色為白色,編寫程式創建和tom羊屬性完全相同的羊。 ##### 傳統方式解決 代碼實現 ```java public class Sheep { private String name; private ...
原型模式
案例引入
克隆羊問題
有一隻羊,姓名為tom,年齡為1,顏色為白色,編寫程式創建和tom羊屬性完全相同的羊。
傳統方式解決
代碼實現
public class Sheep {
private String name;
private int age;
private String color;
public Sheep() {
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
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;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
//測試
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
/**
* public String toString() {//Object類的toString()方法
* return getClass().getName() + "@" + Integer.toHexString(hashCode());//會輸出全類名@符還有16進位的hashCode值
* }
*/
System.out.println(sheep);//輸出的對象的hashcode的值不相同,輸出對象時,會預設的調用對象的toString()方法
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
}
}
傳統實現方式分析
- 1.優點是好理解,簡單易操作。
- 2.缺點進行新對象創建時,總是需要重新獲取原始對象的屬性,如果創建的對象複雜時,效率很低。
- 3.缺點,總是需要重新初始化對象(new操作),而不是動態的根據已有對象去創建,不靈活。
- 4.改進思路,Java中Object類是所有類的基類,Object類提供了一個clone()方法,該方法可以將一個Java對象賦值一份,但是需要想使用這個方法的類必須要實現一個Cloneable介面,
該介面才能複製,且具有複製的能力。
原型模式
基本介紹
- 1.原型模式(Prototype Pattern)是指,用原型實例創建對象的種類,並且通過拷貝這些原型,創建新的對象。
- 2.原型模式是一種設計型模式,允許一個對象再創建另一個可對象,無需知道創建的細節。
- 3.工作原理是,通過將一個原型對象通過clone或者其他自己寫的克隆方法,拷貝自身。
用原型模式實現案例
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
private String address = "蒙古羊";
private Sheep friend;
public Sheep() {
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
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;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", address='" + address + '\'' +
", friend=" + friend +
'}';
}
@Override
protected Object clone() {
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
}catch (Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack",1,"黑色"));
Sheep sheep1 = (Sheep)sheep.clone();
Sheep sheep2 = (Sheep)sheep.clone();
System.out.println("sheep1=" + sheep1 + "sheep1.friend\n" + sheep1.getFriend().hashCode());//輸出的hashCode值相同
System.out.println("sheep2=" + sheep2 + "sheep2.friend\n" + sheep2.getFriend().hashCode());
}
}
原型模式在Spring框架中的使用
//AbstractBeanFactory類doGetBean()的部分代碼
else if (mbd.isPrototype()) {//判斷當前bean是否是原型類型
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
深拷貝和淺拷貝
淺拷貝介紹
- 1.淺拷貝,對於基本數據類型的屬性,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。
- 2.對於數據類型是引用數據類型的成員變數,比如說成員變數是某個數組,某個類的對象等,淺拷貝會進行引用傳遞,也就是只是將該成員變數的引用值(記憶體地址)複製給新的對象。實際上兩個對象的該成員變數都指向一個實例。在這種情況下,一個對象對於引用類型的屬性的改變,就會影響到另一個對象的引用屬性。
- 3.前面克隆羊就是淺拷貝。
- 4.淺拷貝是使用預設的clone方法來實現 sheep = (sheep) super.clone();
深拷貝介紹
- 1.複製對象的所有屬性,進行拷貝,就是說,基本數據類型拷貝成員變數值,引用數據類型的屬性都申請存儲空間,並複製每個引用類型屬性所引用的對象,進行拷貝。也就是說對象進行深拷貝要對整個對象(包括對象的引用屬性)進行拷貝。
- 2.深拷貝實現方式,重寫clone()和通過對象序列化和反序列化實現深拷貝(推薦)
序列化,簡單的說,就是將對象存儲到磁碟。反序列化,將磁碟存儲的對象讀取到記憶體。
代碼演示
public class Money implements Serializable, Cloneable{
private int account;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Person implements Serializable, Cloneable {
private String name;
private int age;
private Money money;
private static final long serialVersionID = 1L;
public Person(String name, int age, Money money) {
this.name = name;
this.age = age;
this.money = money;
}
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;
}
public Money getMoney() {
return money;
}
public void setMoney(Money money) {
this.money = money;
}
//1.重寫clone方法完成深拷貝
//這種方式存在問題,如果一個類中存在引用屬性且沒有為該屬性複製
//就會導致在進行克隆時會出現NullPointException,可以在clone前進行驗證解決
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = null;
//1.完成基本數據類型的clone
person = (Person) super.clone();
//2.對引用類型的屬性,進行單獨處理
person.money = (Money) money.clone();
return person;
}
//方式2 通過對象的序列化和反序列化實現
public Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Object object = null;
try{
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//當前對象序列化到對象輸出流中
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);//從輸出流中反序列化到輸入流中
object = ois.readObject();//讀取對象
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return object;
}
}
//測試
public class TestDeepClone {
public static void main(String[] args) throws CloneNotSupportedException {
Money money = new Money();
Person person = new Person("wind", 27, null);
Person clone = (Person) person.clone();
// Person clone = (Person) person.deepClone();
System.out.println(person.getMoney().hashCode() + "-" + clone.getMoney().hashCode());
}
}
註意事項和細節
- 1.創建新的對象比較複雜,可以利用原型模式簡化對象的創建過程,同時也能提高效率。
- 2.不用再重新初始化對象,而是動態的根據對象的運行進行創建。
- 3.如果原始對象發送變化(增加或減少屬性),克隆對象的也不要發送變化,無需修改克隆的代碼。
- 4.實現深拷貝,需要比較複雜的代碼。
- 5.缺點,要為需要克隆的類,配一個克隆方法,這對新創建的類不難,但是對已有的類,需要修改源代碼,違反了ocp。
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。