java設計模式之原型模式

原型模式的定義:

  原型模式指原型實例指定建立對象的種類,而且經過複製這些原型建立新的對象,屬於建立型設計模式。原型模式的核心在於複製原型對象。java

以系統中已存在的一個對象原型,直接基於內存二進制流進行復制,不須要再經歷耗時的對象初始化過程,性能將大大提高。設計模式

原型模式的應用場景:

  • 建立對象成本大,須要優化資源。
  • 建立一個對象須要繁瑣的數據準備或權限訪問,須要提升性能或提升安全性。
  • 系統中大量使用該類對象,且各個調用者都須要給它的屬性從新賦值。

分析JDK淺克隆API帶來的問題:

  在java提供的API中,不須要手動建立抽象原型接口,java內置了Cloneable抽象原型接口,自定義的類型只須要實現該接口並重寫Object.clone方法便可完成安全

對本類的複製。ide

  經過查看JDK的源碼發現,其實Cloneable是一個空接口。java之因此提供Cloneable接口,只是爲了在運行時通知java虛擬機能夠安全的在該類上使用clone性能

方法。若是沒有實現Cloneable接口,調用clone()方法會拋出CloneNotSupportedException異常。優化

 

 若是使用clone()方法,須要知足如下條件:this

  1. 對於任何對象o,都有o.clone() != o,也就是說克隆產生的對象和原對象不是同一個對象。
  2. 對於任何對象o,都有o.clone().getClass() == o.getClass(),也就是說克隆產生的對象和原對象類型相同。
  3. 對於任何對象o,應該有o.clone().equals(o)成立,爲何這樣不是絕對的呢,由於若是在不重寫hashcode和equals的狀況下,是不能保證兩個對象equals相等的。

那麼在java中淺克隆會帶來什麼問題呢,看下面的例子:spa

建立一個User類,並重寫clone方法:prototype

package com.liuyi.designmode.creational.prototype;

import lombok.Data;

import java.util.List;

/**
 * @ClassName User
 * @description:
 * @author:liuyi
 * @Date:2020/11/11 20:55
 */
@Data
public class User implements Cloneable{
    private Integer id;

    private String name;

    private List<String> phoneList;

    /* *
     * @Author liuyi
     * @Description //TODO 重寫淺克隆方法
     * @Date 2020/11/11 21:11 
     * @Param []
     * @return com.liuyi.designmode.creational.prototype.User
     **/
    @Override
    protected User clone(){
        try {
            return (User) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}

建立一個對象,而後克隆一個對象,再修改對應的屬性值:設計

package com.liuyi.designmode.creational.prototype;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PrototypeTest
 * @description:
 * @author:liuyi
 * @Date:2020/11/11 21:04
 */
public class PrototypeTest {
    public static void main(String[] args) {
        //建立對象
        User user = new User();
        user.setId(1);
        user.setName("劉一");
        List<String> PhoneList = new ArrayList<>();
        PhoneList.add("1457697");
        PhoneList.add("545452");
        PhoneList.add("5155445645");
        user.setPhoneList(PhoneList);
        //打印以前的對象信息
        System.out.println(user);
        //克隆對象
        User clone = user.clone();
        //修改id爲2
        clone.setId(2);
        //刪除list對象的一個值
        clone.getPhoneList().remove(1);
        //分別打印
        System.out.println(user);
        System.out.println(clone);

    }
}

咱們來看執行結果:

 

 能夠看到,我對克隆以後的值進行修改,基礎數據類型id改變了,沒有影響原對象,可是改變引用類型List,則原對象的值也發生了改變。這就叫淺拷貝,這也是

淺克隆最致命的問題。那麼怎麼解決這個問題呢,使用深克隆能夠解決該問題。

使用序列化實現深克隆:

  在上面的基礎上添加一個deepClone()方法,代碼以下:

 /* *
     * @Author liuyi
     * @Description 深克隆
     * @Date 2020/11/11 21:40 
     * @Param []
     * @return com.liuyi.designmode.creational.prototype.User
     **/
    public User 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 (User)ois.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

把clone方法修改成deepClone方法的結果以下:

 

 此次修改克隆對象沒有影響到原對象,達到了咱們的目的。

原型模式的優勢:

  • java自帶的原型模式基於內存二進制流的複製,在性能上比直接new一個對象更佳。
  • 可使用深克隆方式保存對象的狀態,以便在須要的時候使用,例如恢復的歷史某個狀態,可輔助實現撤銷操做。

原型模式的缺點:

  • 須要對每一個類都重寫clone方法才能實現。
  • clone位於類的內部,如須要修改,則須要修改代碼,違背開閉原則。
  • 當實現深克隆時候,須要編寫複雜的代碼,且當對象存在多重嵌套是,須要每一層對象都實現深克隆,實現起來比較麻煩。
相關文章
相關標籤/搜索