1. 程式人生 > 實用技巧 >java設計模式3-原型模式

java設計模式3-原型模式

原型模式

克隆羊問題

現在有一隻羊,姓名為tom,年齡為1,顏色為白色,請編寫程式建立和tom羊屬性完全相同的10只羊.

傳統方法解決克隆羊問題

思路分析

程式碼

Sheep

public class Sheep {
    private String name;
    private int age;

    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    
public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public Sheep(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + '}'; }
public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class Client {
    public static void main(String[] args) {
        
//傳統的方法 Sheep sheep = new Sheep("tom", 1, "白色"); Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); //..... System.out.println(sheep); System.out.println(sheep2); System.out.println(sheep3); System.out.println(sheep4); } }

傳統方法的優缺點

1、優點是比較好理解,簡單易操作

2、在建立新的物件時,總是需要重新獲取原始物件的屬性,如果建立的物件比較複雜時,效率較低

3、總是需要重新初始化物件,而不是動態地獲得物件執行時的狀態,不夠靈活

4、改進思路

  a)javaObject類是所有類的根類,Object類提供了一個clone()方法,該方法可以將一個java物件複製一份,但是需要實現clonejava類必須要實現一個介面Cloneable,該介面表示該類能夠複製且具有複製能力-》原型模式.

原型方法

基本介紹

1、原型模式是指:用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件

2、原型模式是一個建立型設計模式,允許一個物件再建立另外一個可定製的物件,無需知道如何建立的細節

3、工作原理:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件拷貝它們自己來實施建立,即物件.clone()

4、形象的理解:孫大聖拔出毛,變出其他孫大聖.

uml類圖

1、Sheep:原型類,生命一個克隆自己的介面

2、Sheep:具體的原型類,實現一個克隆自己的操作

3、Client:讓一個原型物件克隆自己,從而建立一個新的物件(屬性一樣)

原型模式解決克隆羊問題的應用例項

使用原型模式改進傳統方法,讓程式具有更高的效率和擴充套件性.

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";
    public Sheep friend; //是物件, 克隆是會如何處理, 預設是淺拷貝

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.address = address;
        this.friend = friend;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", address='" + address + '\'' +
                ", friend=" + friend +
                '}';
    }

    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
    protected Object clone() throws CloneNotSupportedException {

        Sheep sheep = null;
        try{
            sheep = (Sheep) super.clone();
        }catch(Exception e){
            throw  new RuntimeException(e);
        }
        return sheep;
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("原型模式完成物件的建立"); // TODO Auto-generated method stub
        Sheep sheep = new Sheep("tom", 1, "白色");
        sheep.friend = new Sheep("jack", 2, "黑色");
        Sheep sheep2 = (Sheep) sheep.clone(); //克隆
        Sheep sheep3 = (Sheep) sheep.clone(); //克隆
        Sheep sheep4 = (Sheep) sheep.clone(); //克隆
        Sheep sheep5 = (Sheep) sheep.clone(); //克隆
        System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
        System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
        System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
    }
}

原型模式在Spring框架中原始碼分析

Spring中原型bean的建立,就是原型模式的應用

淺拷貝和深拷貝

1、對於資料型別是基本型別的成員變數,淺拷貝會直接進行值傳遞,也就將該屬性值複製一份新的物件.

2、對於資料型別是引用資料型別的成員變數,比如說成員變數是某個陣列、某個類的物件例項等,那麼淺拷貝會進行引用傳遞,也就是隻是將該成員變數的引用值(記憶體地址)複製一份給新的物件.因為實際上兩個物件的該成員變數都指向同一個例項,在這個情況下,在一個物件中修改該成員變數會影響到另一個物件的該成員變數值

3、淺拷貝可以使用預設的clone方法實現

深拷貝的基本介紹

1、複製物件的所有基本資料型別的成員變數值

2、為所有引用資料型別的成員變數申請儲存空間,並複製每個引用資料型別成員變數所引用的物件,直到該物件可達的所有物件.也就是說,物件進行深拷貝要對整個物件(包括物件的引用型別)進行拷貝

3、實現方法

  a)重寫clone方法

  b)通過物件序列化實現(推薦)

public class DeepCloneableTarget implements Serializable, Cloneable {
    private String cloneName;
    private String cloneClass;

    //構造器
    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    //因為該類的屬性,都是 String , 因此我們這裡使用預設的 clone 完成即可 @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class DeepProtoType implements Serializable, Cloneable {
    public String name; //String 屬性
    public DeepCloneableTarget deepCloneableTarget;// 引用型別

    public DeepProtoType() {
        super();
    }

    //深拷貝 - 方式 1 使用clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //這裡完成對基本資料型別(屬性)和 String 的克隆
        deep = super.clone();
        //對引用型別的屬性,進行單獨處理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deepProtoType;
    }

    //深拷貝 - 方式 2 通過物件的序列化實現 (推薦)
    public Object deepClone() {
        //建立流物件
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //當前這個物件以物件流的方式輸出
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();
            return copyObj;
        } catch (
                Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        } finally {
            //關閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                // TODO: handle exception
                System.out.println(e2.getMessage());
            }
        }
    }
}

原型模式的注意事項和細節

1、建立新的物件比較複雜時,可以利用原型模式簡化物件的建立過程,同時也能夠提高效率

2、不用重新初始化物件,而是動態地獲得物件執行時的狀態

3、如果原始物件發生變化(增加或者減少屬性),其他克隆物件也會發生相應的變化,無需修改程式碼

4、在實現深克隆的時候可能需要比較複雜的程式碼

5、缺點:需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改其原始碼,違背了ocp原則.