《Android源碼設計模式解析與實戰》讀書筆記(四) 《Android源碼設計模式解析與實戰》PDF資料下載php
原型模式是一個建立型的模式。原型二字代表了該模式應該有一個樣板實例,用戶從這個樣板對象中複製出一個內部屬性一致的對象,這就是「克隆」。被複制的實例就是咱們所稱的「原型」。設計模式
原型模式多用於建立複雜的或者構造耗時的實例,由於這種狀況下,複製一個已經存在的實例可以使程序運行更高效。安全
用原型實例指定建立對象的種類,並經過拷貝這些原型建立新的對象。bash
注意: 經過實現Cloneable接口的原型模式在調用clone函數構造實例時並不必定比經過new操做速度快,只有當經過new構造對象較爲耗時或者說成本較高時,經過clone方法才能得到效率上的提高。服務器
/**
* 文檔類型,扮演的是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
doc2是originDoc的一份拷貝,它們的內容是同樣的,而doc2修改了文本內容之後並不會影響originDoc的文本內容,這就保證了originDoc的安全性。還須要注意,經過clone拷貝對象時並不會執行構造函數。函數
原型模式的核心問題就是對原始對象進行拷貝,在這個模式的使用過程當中須要注意的一點就是:深、淺拷貝的問題。性能
這是一個簡化版的客戶端,在用戶登錄以後,經過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 + '}'; } } 複製代碼
原型模式本質上就是對象拷貝。使用原型模式能夠解決構建複雜對象的資源消耗問題,可以在某些場景下提高建立對象的效率。還有一個重要的用途即便保護性拷貝。
優勢:
缺點: