天天看點

建立型設計模式之原型模式原型模式原型模式的基本使用JDK自帶原型接口的使用淺克隆與深度克隆單例的破壞

原型模式

概述

原型模式(Prototype Pattern)是屬于

建立型模式

它指用原型執行個體指定建立對象的種類,并且通過拷貝這些原型建立新的對象。
原型模式的核心在于拷貝原型對象。以存在的一個對象為原型,直接基于記憶體二進制流進行拷貝,無需再經曆耗時的對象初始化過程(不調用構造函數),性能提升許多。
當直接建立對象的代價比較大時,則采用這種模式。利用目前系統中已存在的對象作為原型,對其進行克隆,避免初始化的過程。

優缺點

優點:

1.java自帶的原型模式是基于記憶體二進制流的拷貝,比直接new一個對象性能上提升許多

2.使用原型模式将對象複制一份并将其狀态儲存起來,簡化建立對象的過程,以便在需要的時候使用
           

缺點:

1.需要為每一個類配置一個克隆方法

2克隆方法位于類的内部,當對已有類進行改造的時候,需要修改代碼,違反開閉原測

3.在實作深克隆時需要編寫較為複雜的代碼,而且當對象之間存在多重嵌套引用時,為了實作深克隆,每一層對象對應的類都必須支特深克隆,實作比較麻煩
           

應用場景

1.類初始化消耗資源較多

2.new産生的一個對象需要非常繁瑣的過程(資料準備或通路權限)

3.構造函數處比較複雜

4.循環體中生産大量對象時

5.資源優化場景

6.性能和安全要求的場景
           

主要角色

1.客戶(Client)角色

用戶端類向原型管理器提出建立對象的請求。

2.抽象原型(Prototype)角色

這是一個抽象角色,此角色給出所有的具體原型類所需的接口。

3.具體原型(Concrete Prototype)角色

被複制的對象。此角色需要實作抽象的原型角色所要求的接口。

4.原型管理器(Prototype Manager)角色

建立具體原型類的對象,并記錄每一個被建立的對象。

原型模式的基本使用

建立原型接口

public interface IPrototype<T> {
    T clone();
}
           

建立具體需要克隆對象

@Data
@ToString
public class ConcretePrototype implements IPrototype {

    private int age;
    private String name;

    @Override
    public ConcretePrototype clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        return concretePrototype;
    }
}
           

使用

public static void main(String[] args) {
        //建立原型對象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(20);
        prototype.setName("jack");
        System.out.println(prototype);

        //拷貝原型對象
        ConcretePrototype cloneType = prototype.clone();
        System.out.println(cloneType);
    }
           

JDK自帶原型接口的使用

原型模式就是如此簡單,通常開發中不會這樣使用。其實JDK提供了一個現成的API接口,那就是Cloneable接口。
@Data
@ToString
public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
           

淺克隆與深度克隆

淺克隆

添加一個hobby屬性,當給克隆對象的hobby屬性添加一項時,最終結果會導緻原型對象發生變化,也就是hobby屬性用于一個記憶體位址。這就是淺克隆。
@Data
@ToString
public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobby;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
           
public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(22);
        prototype.setName("jack");
        List<String> hobby = new ArrayList<String>();
        hobby.add("java");
        hobby.add("python");
        hobby.add("go");
        prototype.setHobby(hobby);

        // 拷貝原型對象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.getHobby().add("php");


        System.out.println("原型對象:" + prototype);
        System.out.println("克隆對象:" + cloneType);
        System.out.println(prototype == cloneType);
        System.out.println(prototype.getHobby() == cloneType.getHobby());
    }
           
原型對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
克隆對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
true
           

深度克隆

可使用序列化方式實作對象的深度克隆

@Data
@ToString
public class ConcretePrototype implements Cloneable, Serializable {

    private int age;
    private String name;
    private List<String> hobby;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public ConcretePrototype deepClone() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            return (ConcretePrototype) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
           
public static void main(String[] args) {
        //建立原型對象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(22);
        prototype.setName("jack");
        List<String> hobby = new ArrayList<String>();
        hobby.add("java");
        hobby.add("python");
        hobby.add("go");
        prototype.setHobby(hobby);

        // 拷貝原型對象
        ConcretePrototype cloneType = prototype.deepClone();
        cloneType.getHobby().add("php");

        System.out.println("原型對象:" + prototype);
        System.out.println("克隆對象:" + cloneType);
        System.out.println(prototype == cloneType);
        System.out.println(prototype.getHobby() == cloneType.getHobby());

    }
           
原型對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go])
克隆對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
false
           
public ConcretePrototype deepClone2() {
        try {
            ConcretePrototype result = (ConcretePrototype) super.clone();
            result.hobby = (List) ((ArrayList) result.hobby).clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
           

單例的破壞

1.讓單例類不實作Cloneable接口

2.重寫clone方法,在clone方法中傳回單例對象即可
           
@Data
@ToString
public class ConcretePrototype implements Cloneable {

    private static  ConcretePrototype instance = new ConcretePrototype();

    private ConcretePrototype(){}

    public static ConcretePrototype getInstance(){
        return instance;
    }

    @Override
    public ConcretePrototype clone() {
       return instance;
    }
}