編輯:關於Android編程
Builder模式是一種設計模式,最初被介紹於《設計模式:可復用面向對象軟件的基礎》,目前在Java及Android中用處更是十分廣泛,因此基本的了解與學習應當掌握。
首先從它的定義開始介紹:
Builder模式:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
一般而言,Builder模式主要由四個部分組成:
Product :被構造的復雜對象,ConcreteBuilder 用來創建該對象的內部表示,並定義它的裝配過程。 Builder :抽象接口,用來定義創建 Product 對象的各個組成部分的組件。 ConcreteBuilder : Builder接口的具體實現,可以定義多個,是實際構建Product 對象的地方,同時會提供一個返回 Product 的接口。 Director : Builder接口的構造者和使用者。以代碼的形式來進行說明,首先創建Product 類:
public class Product { private String partOne; private String partTwo; public String getPartOne() { return partOne; } public void setPartOne(String partOne) { this.partOne = partOne; } public String getPartTwo() { return partTwo; } public void setPartTwo(String partTwo) { this.partTwo = partTwo; } }
創建Builder接口:
public interface Builder { public void buildPartOne(); public void buildPartTwo(); public void getProduct(); }
創建兩個ConcreteBuilder 類,即實現Builder接口:
public class ConcreteBuilderA implements Builder { private Product product; @Override public void buildPartOne() { } @Override public void buildPartTwo() { } @Override public Product getProduct() { return product; } }
public class ConcreteBuilderB implements Builder { private Product product; @Override public void buildPartOne() { } @Override public void buildPartTwo() { } @Override public Product getProduct() { return product; } }
最後創建Director類
public class Director { private Builder builder; public Director(Builder builder){ this.builder = builder; } public void buildProduct(){ this.builder.buildPartOne(); this.builder.buildPartTwo(); } public Product getProduct(){ return this.builder.getProduct(); } }
以上代碼只是最基本的展示了Builder模式,基於定義的介紹。這種基本模式重點在於:抽象出對象創建的步驟,並通過調用不同的具體實現從而得到不同的結果。 但是在實際運用中並非是以上形式,而是進化成另一種體現形式,目的在於 減少對象創建過程中引入的多個重載構造函數、可選參數以及setter過度使用導致不必要的復雜性。
下面還是以代碼的形式來進行講解,只是這次更加具體到一個熟悉的對象User,假以它有以下屬性,且都是不可變(final)的。【應盡量將屬性值定義為不可變】
public class User { private final String mName; //必選 private final String mGender; //可選 private final int mAge; //可選 private final String mPhone; //可選 }
這個 User類中 mName屬性是必選的,其余的可選,接下來該如何構造這個實例呢?在此有兩個前提:
所有屬性值以聲明為 final,因此必須在構造函數中對這些屬性初始化,否則編譯不通過。 可是以上屬性值分為必選和可選,所以構造函數需要提供不同的參數組合的方法。(即可選參數可以忽略)因此,綜上兩個前提,最直接的一個方案是定義多個重載的構造函數,其中一個構造函數只接收必選參數,其余的構造函數不僅要接收必選參數,還要接收不同可選參數的組合,代碼如下:
public class User { private final String mName; //必選 private final String mGender; //可選 private final int mAge; //可選 private final String mPhone; //可選 public User(String mName) { this(mName, ""); } public User(String mName,String mGender) { this(mName, mGender, 0); } public User(String mName, String mGender, int mAge) { this(mName, mGender, mAge, ""); } public User(String mName, String mGender, int mAge, String mPhone) { this.mName = mName; this.mGender = mGender; this.mAge = mAge; this.mPhone = mPhone; } }
這種構造函數的方式雖然簡單,但是 只適用於少量屬性的情況,一旦屬性增多,構造函數的數量也會隨著線性增長,因此並不好閱讀及維護。
第二種方案是遵循 JavaBeans 規范,定義一個默認的無參數構造函數,並為類的每個屬性都提供getters 和 setters函數,代碼如下:
public class User { private String mName; //必選 private String mGender; //可選 private int mAge; //可選 private String mPhone; //可選 public String getName() { return mName; } public void setName(String mName) { this.mName = mName; } public String getGender() { return mGender; } public void setGender(String mGender) { this.mGender = mGender; } public int getAge() { return mAge; } public void setAge(int mAge) { this.mAge = mAge; } public String getPhone() { return mPhone; } public void setPhone(String mPhone) { this.mPhone = mPhone; } }
這種方案相較於第一種方案的好處是易於閱讀和維護,使用者可以創建一個空實例User,並只設置需要的屬性值,可是此方案有兩個缺點:
User類是可變的,禁锢了對象的可變性。 User類的實例狀態不連續。如果想要創建一個同時具有所有屬性的類實例,那麼直到第五個屬性值的 set函數被調用時,該類實例才具有完整連續的狀態。這也就意味著類的調用者可能會看到類實例的不連續狀態。了解了以上兩種比較常見但是效果卻不太理想的方案後,正式引出第三種方案 —— 進化後的 Builder模式,既有以上兩種方案的優點,又摒棄它們的缺點。代碼如下:
public class User { private final String mName; //必選 private final String mGender; //可選 private final int mAge; //可選 private final String mPhone; //可選 public User(UserBuilder userBuilder) { this.mName = userBuilder.name; this.mGender = userBuilder.gender; this.mAge = userBuilder.age; this.mPhone = userBuilder.phone; } public String getName() { return mName; } public String getGender() { return mGender; } public int getAge() { return mAge; } public String getPhone() { return mPhone; } public static class UserBuilder{ private final String name; private String gender; private int age; private String phone; public UserBuilder(String name) { this.name = name; } public UserBuilder gender(String gender){ this.gender = gender; return this; } public UserBuilder age(int age){ this.age = age; return this; } public UserBuilder phone(String phone){ this.phone = phone; return this; } public User build(){ return new User(this); } } }
從以上代碼可以看出這幾點:
User類的構造函數是私有的,這意味著調用者不可直接實例化這個類。 User類是不可變的,其中必選的屬性值都是 final 的並且在構造函數中設置;同時對所有的屬性取消 setters函數,只保留 getter函數。 UserBuilder 的構造函數只接收必選的屬性值作為參數,並且只是將必選的屬性設置為 fianl,來保證它們在構造函數中設置。接下來,User類的使用方法如下:
public User getUser(){ return new User.UserBuilder("gym") .gender("female") .age(20) .phone("12345678900") .build(); }
以上通過 進化的Builder模式形象的體現在User的實例化。
分析比較了以上三個方案後,也學習了解了 化Builder模式的好處和運用方法,可是你會發現它需要編寫很多樣板代碼,需要在內部類 UserBuilder 中重復外部類User的屬性定義。其實在AS中,可以通過安裝 InnerBuilder 的插件來簡化 Builder模式的創建過程,以下為安裝使用步驟:
完成以上步驟後,即可自動生成 進化Builder模式 代碼,可能稍有不同,根據自身需求修改即可。
正如開頭所說,Builder模式 被廣泛運用於Android中的各個領域,無論是Android SDK 或各種開元函數庫中,接下來關於Builder模式的運用舉幾個例子:
AlertDialog alertDialog = new AlertDialog.Builder(this). setTitle("標題"). setMessage("內容"). setIcon(R.drawable.ic_logo). create(); alertDialog.show();
private Request(Builder builder){ this.url = builder.url; this.method = builder.method ; this.headers = builder.headers.build() ; this.body = builder.body ; this.tag = builder.tag != null ? builder.tag : this; }
其實以上內容幾乎都是書上的,前幾天讀到了這一章內容,講的十分淺顯易懂,之前自己沒怎麼理解這塊內容,趁這次機會詳細學習了一遍,以上內容及代碼均手碼出來,包括安裝插件,親自動手實踐學習理解更深,在此謝過 顧浩鑫coder。
希望對你有幫助 :)
在泰國舉行的谷歌開發者論壇上,谷歌為我們介紹了一個名叫Glide的圖片加載庫,作者是bumptech。這個庫被廣泛的運用在google的開源項目中,包括2014年goog
《最近做一個小功能遇到這麼一個問題,listview 與 baseadapter結合使用,關於點贊的的時候 圖片重復問題,比如:我在第1個item 點贊然後 心型換成了紅
之前一篇文章研究完橫向二級菜單,發現其中使用了SparseArray去替換HashMap的使用.於是乎自己查了一些相關資料,自己同時對性能進行了一些測試。首先先說一下Sp
最近因公司項目要求需要寫一個播放器,自帶的又不太好用,也不太好看。自能自定義啦。查看了很多資料,都沒有完善的,還好得以為前輩的指點得以完成,感謝Yang。本篇裡面我有可能