Java設計模式(四)之原型模式(Proto)

目錄

對象拷貝

淺拷貝

深拷貝

原型模式


對象拷貝

        Person類定義如下: 

/**
 * Created by leboop on 2020/5/23.
 */
public class Person {
    private String name;
    private Integer age;
    private City city;

    public Person(String name, Integer age, City city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", city=" + city +
                '}';
    }
}

City類定義如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class City {
    private String cityName;

    public City(String cityName) {
        this.cityName = cityName;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    @Override
    public String toString() {
        return "City{" +
                "cityName='" + cityName + '\'' +
                '}';
    }
}

 客戶端代碼如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class ProtoMain {
    public static void main(String[] args) {
        City city=new City("shanghai");
        Person person = new Person("zs", 30, city);
    }
}

 現在有一個需求:創建一個與person對象具有相同屬性值的新對象p2,能不能直接將person對象賦值給p2呢?代碼如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class ProtoMain {
    public static void main(String[] args) {
        City city=new City("shanghai");
        Person person = new Person("zs", 30, city);
        Person p2 = person;
        System.out.println(p2);
    }
}

輸出結果如下:

Person{name='zs', age=30, city=City{cityName='shanghai'}}

從結果來看好像複製成功了。但是現在修改p2對象的age屬性值,如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class ProtoMain {
    public static void main(String[] args) {
        City city=new City("shanghai");
        Person person = new Person("zs", 30, city);
        Person p2 = person;
        p2.setAge(100);
        System.out.println(person);
    }
}

輸出結果如下:

Person{name='zs', age=100, city=City{cityName='shanghai'}}

發現person對象的age屬性值也被修改爲100,這是不允許的。那如何解決這個問題呢?這就需要淺拷貝技術。

 

淺拷貝

        JDK給我們提供了現成的拷貝技術:Cloneable接口和Object類的clone方法。Cloneable接口源碼如下:

public interface Cloneable {
}

Cloneable接口沒有任何方法和屬性,它僅僅表示一種克隆標記。Object類的clone方法源碼如下:

protected native Object clone() throws CloneNotSupportedException;

它由protected和native關鍵字修飾,表示該方法在子類實現,其他類中不可見。native表示該方法是C++底層方法。現在可以將Person類改爲如下形式:

/**
 * Created by leboop on 2020/5/23.
 */
public class Person implements Cloneable {
    private String name;
    private Integer age;
    private City city;

    public Person(String name, Integer age, City city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", city=" + city +
                '}';
    }
}

Person類現在實現了Cloneable接口和重寫了Object類的clone方法。客戶端代碼改爲:

/**
 * Created by leboop on 2020/5/23.
 */
public class ProtoMain {
    public static void main(String[] args) throws CloneNotSupportedException{
        City city=new City("shanghai");
        Person person = new Person("zs", 30, city);
        Person p2 = (Person) person.clone();
        p2.setAge(100);
        System.out.println(p2);
        System.out.println(person);
    }
}

輸出結果如下:

Person{name='zs', age=100, city=City{cityName='shanghai'}}
Person{name='zs', age=30, city=City{cityName='shanghai'}}

現在對p2的age屬性值修改已經不影響原來person對象的屬性值了,完成了屬性值的淺拷貝。有人會有疑問:既然Cloneable接口沒有任何方法,Person是否可以不實現該接口,答案是不行的,如果Person類不實現Cloneable接口,編譯時不會有問題,但是運行客戶端代碼時會拋出java.lang.CloneNotSupportedException異常,如下:

Exception in thread "main" java.lang.CloneNotSupportedException:

 那麼現在是不是已經沒有問題了呢?還不行。如果我們修改p2對象的引用類型屬性city就會出現問題。這是深拷貝技術。

 

深拷貝

        現在我們將p2對象的city屬性值修改爲beijing,如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class ProtoMain {
    public static void main(String[] args) throws CloneNotSupportedException{
        City city=new City("shanghai");
        Person person = new Person("zs", 30, city);
        Person p2 = (Person) person.clone();
        p2.setAge(100);
        p2.getCity().setCityName("beijing");
        System.out.println(p2);
        System.out.println(person);
    }
}

輸出結果如下:

Person{name='zs', age=100, city=City{cityName='beijing'}}
Person{name='zs', age=30, city=City{cityName='beijing'}}

發現person對象的city屬性值也被修改爲了beijing,這也是不允許的。如何解決呢?需要對City對象進行克隆。修改兩個地方

(1)City類實現Cloneable接口,重寫Object類的clone方法;

(2)Person類對拷貝普通屬性時,需要對引用類型屬性進行拷貝;

如下:

/**
 * Created by leboop on 2020/5/23.
 */
public class City {
    private String cityName;

    public City(String cityName) {
        this.cityName = cityName;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    @Override
    public String toString() {
        return "City{" +
                "cityName='" + cityName + '\'' +
                '}';
    }
}
/**
 * Created by leboop on 2020/5/23.
 */
public class Person implements Cloneable {
    private String name;
    private Integer age;
    private City city;

    public Person(String name, Integer age, City city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();
        p.city = (City) city.clone();

        return p;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", city=" + city +
                '}';
    }
}

此時客戶端代碼輸出結果如下:

Person{name='zs', age=100, city=City{cityName='beijing'}}
Person{name='zs', age=30, city=City{cityName='shanghai'}}

 

原型模式

        原型模式指的就是對象深拷貝,也就是創建同原對象具有相同屬性值的新對象,並且對新對象的操作不影響原對象。它如下實現

(1)需要拷貝的對象(比如person)所對應的類(比如Person類)必須實現Cloneable接口和重寫Object的clone方法;

(2)如果Person類有引用類型的屬性(比如city),該引用類型對應的類(比如City)也需要實現Cloneable接口和重寫Object的clone方法;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章