使程序運行更高效——原型模式

《Android源碼設計模式解析與實戰》讀書筆記(四) 《Android源碼設計模式解析與實戰》PDF資料下載php

1、原型模式簡介

原型模式是一個建立型的模式。原型二字代表了該模式應該有一個樣板實例,用戶從這個樣板對象中複製出一個內部屬性一致的對象,這就是「克隆」。被複制的實例就是咱們所稱的「原型」。設計模式

原型模式多用於建立複雜的或者構造耗時的實例,由於這種狀況下,複製一個已經存在的實例可以使程序運行更高效。安全

1.一、定義

用原型實例指定建立對象的種類,並經過拷貝這些原型建立新的對象。bash

1.二、使用場景

  1. 類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等,經過原型拷貝避免這些消耗。
  2. 經過new產生一個對象須要很是繁瑣的數據準備或訪問權限,這時可使用原型模式。
  3. 一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值時,能夠考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。

注意: 經過實現Cloneable接口的原型模式在調用clone函數構造實例時並不必定比經過new操做速度快,只有當經過new構造對象較爲耗時或者說成本較高時,經過clone方法才能得到效率上的提高。服務器

2、原型模式的簡單實現

/**
 * 文檔類型,扮演的是ConcretePrototype角色,而cloneable是表明prototype角色
 */
public class WordDocument implements Cloneable {
    //文本
    private String mText;
    //圖片名列表
    private ArrayList<String> mImages = new ArrayList<>();

    public WordDocument() {
        System.out.println("----------------WordDocument構造函數-----------------");
    }

    @Override
    protected WordDocument clone() {
        try {
            WordDocument doc = (WordDocument) super.clone();
            doc.mText = this.mText;
            doc.mImages = this.mImages;
            return doc;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getText() {
        return mText;
    }

    public void setText(String mText) {
        this.mText = mText;
    }

    public ArrayList<String> getImages() {
        return mImages;
    }

    public void addImages(String img) {
        this.mImages.add(img);
    }

    /**
     * 打印文檔內容
     */
    public void showDocument() {
        System.out.println("------------Word Content Start-------------");
        System.out.println("Text:" + mText);
        System.out.println("Images List:");
        for (String imgName : mImages) {
            System.out.println("image name:"+imgName);
        }
        System.out.println("------------Word Content End---------------");
    }
}
複製代碼

Cloneable是一個標識接口,它代表這個類的對象是可拷貝的。若是沒有實現Cloneable接口卻調用了clone()函數將拋出異常。微信

調用代碼以下:網絡

WordDocument originDoc = new WordDocument();
        //2.編輯文檔,添加圖片等
        originDoc.setText("這是一篇文檔");
        originDoc.addImages("圖片1");
        originDoc.addImages("圖片2");
        originDoc.addImages("圖片3");
        originDoc.showDocument();

        //以原始文檔爲原型,拷貝一份副本
        WordDocument doc2 = originDoc.clone();
        doc2.showDocument();
        //修改文檔副本,不影響原始文檔
        doc2.setText("這是修改過的Doc2文本");
        doc2.showDocument();

        originDoc.showDocument();
複製代碼

輸出結果:ide

原型模式.png

doc2是originDoc的一份拷貝,它們的內容是同樣的,而doc2修改了文本內容之後並不會影響originDoc的文本內容,這就保證了originDoc的安全性。還須要注意,經過clone拷貝對象時並不會執行構造函數函數

原型模式的核心問題就是對原始對象進行拷貝,在這個模式的使用過程當中須要注意的一點就是:深、淺拷貝的問題性能

3、原型模式實戰

這是一個簡化版的客戶端,在用戶登錄以後,經過LoginSession保存用戶的登陸信息,這些用戶信息可能在APP的其餘模塊被用來作登陸校驗、用戶我的信息顯示等。可是,這些信息在客戶端程序是不容許修改的,而須要在其餘模塊被調用,所以,須要開放已登陸用戶信息的訪問接口。

/**
 * 用戶實體類
 */
public class User {
    public int age;
    public String name;
    public String phoneNum;
    public Address address;

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' + ", phoneNum='" + phoneNum + '\'' + ", addrss=" + address + '}'; } } 複製代碼
/**
 * 用戶地址類,存儲地址的詳細信息
 */
public class Address {
    //城市
    public String city;
    //區
    public String district;
    //街道
    public String street;

    public Address(String city, String district, String street) {
        this.city = city;
        this.district = district;
        this.street = street;
    }

    @Override
    public String toString() {
        return "Address{" +
                "city='" + city + '\'' + ", district='" + district + '\'' + ", street='" + street + '\'' +
                '}';
    }
}
複製代碼
//登錄接口
public interface Login {
    void login();
}
複製代碼
//登陸實現
public class LoginImpl implements Login {

    @Override
    public void login() {
        // 登陸到服務器,獲取到用戶信息
        User loginedUser = new User();
        //將服務器返回的完整信息設置給loginedUser對象
        loginedUser.age = 22;
        loginedUser.name = "Mr.Simple";
        loginedUser.address = new Address("北京市", "海淀區", "花園東路");
        //登陸完以後將用戶信息設置到Session中LoginSession.getLoginSession()裏
        LoginSession.getLoginSession().setLoginedUser(loginedUser);
    }
}
複製代碼
//登陸Session
public class LoginSession {
    static LoginSession sLoginSession = null;
    //已登陸用戶
    private User loginedUser;

    public LoginSession() {
    }

    public static LoginSession getLoginSession() {
        if (sLoginSession == null) {
            sLoginSession = new LoginSession();
        }
        return sLoginSession;
    }

    //設置已登陸的用戶信息,不對外開放
    void setLoginedUser(User user) {
        loginedUser = user;
    }

    public User getLoginedUser() {
        return loginedUser;
    }
}
複製代碼

LoginSession中的setLoginedUser函數是包級私有的,所以外部模塊沒法調用,這在必定程度上實現了外部客戶端程序不能修改已登陸的用戶信息。

可是,也會存在相似以下代碼:

//獲取已登陸的User對象
User newUser = LoginSession.getLoginSession().getLoginedUser();
newUser.address = new Address("北京市", "朝陽區", "大望路");
複製代碼

相似的代碼也會更新用戶的地址。所以,須要使用原型模式來進行保護性拷貝,也就是說在LoginSession的getLoginUser()函數中返回的是已登陸用戶的一個拷貝,當更新用戶地址的網絡請求完成時,在經過包級私有的LoginSession中的setLoginedUser更新用戶信息。

因而在User類中覆寫了clone方法:

/**
 * 用戶實體類
 */
public class User implements Cloneable {
    public int age;
    public String name;
    public String phoneNum;
    public Address address;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = null;
        try {
            user = (User) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' + ", phoneNum='" + phoneNum + '\'' + ", addrss=" + address + '}'; } } 複製代碼

4、總結

原型模式本質上就是對象拷貝。使用原型模式能夠解決構建複雜對象的資源消耗問題,可以在某些場景下提高建立對象的效率。還有一個重要的用途即便保護性拷貝

優勢:

  • 原型模式是在內存中二進制流的拷貝,要比直接new一個對象性能好不少,特別是要在一個循環體內產生大量的對象時,原型模式能夠更好地提現其優勢。

缺點:

  • 直接在內存中拷貝,構造函數是不會執行的,在實際開發當中應該注意這個潛在的問題。

學海無涯苦做舟

個人微信公衆號
相關文章
相關標籤/搜索