序言
在看Retrofit源碼時,可以看到里面用到了大量的設計模式,如果我們非常了解設計模式對理解是很有幫助的,在Rerofit里有用到建造者模式。
定義
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以生成不同的表示。建造者模式也是對象創建行模式。
說明: 單看這個定義初學者也是很蒙, 大概意思是不需要關心復雜對象創建過程,只需要知道建造者類型即可,建造者才關心這個復雜對象是如何一步一步創建一個復雜對象的。 根據不同類型的建造者生成不同的復雜的對象。也是有具有良好的擴展性。
建造者模式類圖
在建造者模式中包含如下幾個角色:
- Builder(抽象的建造者) : 它是創建Product對象各個部分的抽象接口。 在該接口一般有兩類接口 ,一種是biuldPartX()方法,用于創建該復雜對象各個部分; 另一種方法是getResult, 它用于返回這個復雜對象。 Build可以是一個接口,也可以是抽象類。
- ConcreteBuilder(具體的建造者): 它實現了Builder接口,具體實現該復雜對象的各個部分,然后返回具體的復雜對象。
- Product(產品角色): 它是復雜對象,里面包含該對象有哪些部分組成。
- Director(指揮者):它負責對象的建造次序。指揮者和抽象的建造者存在關聯關系,可以在construct中完成復雜對象的創建過程。
上面有提到復雜對象,那什么是復雜對象呢,簡單來說就是包含多個成員屬性的對象。代碼示例如下:
//產品
class Product{
private String partA;//定義產品的屬性,屬性可以是其它類型, 值類型和引用類型都行。
private String partB;
private String partC;
//Setter和Getter方法省略就不寫了
}
在抽象建造者定義產品的創建方法和獲取產品對象的方法。代碼示例如下:
//抽象建造者
interface Builder{
void buildPartA();//創建產品的屬性
void buildPartB();
void buildPartC();
Product getResult(); //返回產品對象
}
抽象建造者中申明了一系列的buildPartX()方法,但是具體的實現還是需要具體的建造者。 不同的建造者實現的buildPartX有所區別。代碼示例如下:
//具體的建造者 A
public class AConcreteBuilder implements Builder {
Product productA=new Product();
@Override
public void buildPartA() {
productA.setPartA("PartA from AConcreteBuilder");
}
@Override
public void buildPartB() {
productA.setPartA("PartB from AConcreteBuilder");
}
@Override
public void buildPartC() {
productA.setPartA("PartC from AConcreteBuilder");
}
@Override
public Product getResult() {
return productA;
}
}
//具體的建造者 B
public class BConcreteBuilder implements Build {
Product productB=new Product();
@Override
public void buildPartA() {
productB.setPartA("PartA from BConcreteBuilder");
}
@Override
public void buildPartB() {
productB.setPartA("PartB from BConcreteBuilder");
}
@Override
public void buildPartC() {
productB.setPartA("PartC from BConcreteBuilder");
}
@Override
public Product getResult() {
return productB;
}
}
我們可以看到每個建造者實現的東西是不同的。但是返回的產品還是一類的,只是產品的屬性不一樣。
在建造者模式還有個Director角色(導演者或指揮者),該類主要有兩個作用: 其一就是控制產品的創建過程,包括biuldPartX方法是否被調用以及調用的順序; 其二 就是隔離了客戶與創建的過程, Director只需要指揮抽象的建造者(Build),客戶端需要具體的建造者然后調用指揮者的相關方法,返回一個完整的對象(Product)。 代碼示例如下
//指揮者
public class Director {
private Builder mBuilder; //抽象的建造者
public Director(Builder build) {
this.mBuilder = build;
}
public Product construct(){
mBuilder.buildPartA();
mBuilder.buildPartB();
mBuilder.buildPartC();
return mBuilder.getResult();
}
}
//客戶端 代碼片段如下
Builder Abuilder=new AConcreteBuilder();//創建具體的建造者
Director director=new Director(Abuilder);//創建指揮者
Product productA=director.construct();//返回產品A
Builder Bbuilder=new BConcreteBuilder();//創建具體的建造者
Director director=new Director(Bbuilder);//創建指揮者
Product productA=director.construct();//返回產品B
例子
如某個公司要開發個APP(Product),就需要開發者(Builder), 如果有多端的話就需要不同的開發者(ConcreteBuilder),技術經理(Director)就充當指揮者來指示不同的開發者來開發。
APP (Product)
public class APP {
String code;//代碼
String note;//注釋及文檔
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
@Override
public String toString() {
return "APP = {" +
"code='" + code + '\'' +
", note='" + note + '\'' +
'}';
}
}
開發者 (Builder)
public interface Developer {
void writeCode();//寫代碼
void writeNote();//寫文檔及注釋
APP getAPP();//返回APP
}
Andoird開發者(ConcreteBuilder)
public class AndroidDeveloper implements Developer {
APP app = new APP();
@Override
public void writeCode() {
app.setCode("Android code");
}
@Override
public void writeNote() {
app.setNote("Android note ");
}
@Override
public APP getAPP() {
return app;
}
}
技術經理 (指揮者)
public class TechnicalManager {
Developer mDeveloper;
public void setDeveloper(Developer mDeveloper) {
this.mDeveloper = mDeveloper;
}
public APP getAPP() {
mDeveloper.writeCode();
mDeveloper.writeNote();
return mDeveloper.getAPP();
}
}
測試:
public static void main(String[] args) {
TechnicalManager manager = new TechnicalManager();//創建技術經理對象(指揮者)
AndroidDeveloper javaDeveloper = new AndroidDeveloper();//創建Android開發者(具體的建造者)
manager.setDeveloper(javaDeveloper);//技術經理指揮Android程序員去干活
APP app = manager.getAPP(); //完成 Android端 APP
System.out.println(app);
}
運行結果:
APP = {code='Android code', note='Android note'}
如果公司想在蘋果商店也要上款APP,那就需要ios開發,這樣我們就不用修改源代碼,只需要另創建一個ios的建造者就OK。
ios 開發者(ConcreteBuilder)
public class IosDeveloper implements Developer {
APP app = new APP();
@Override
public void writeCode() {
app.setCode("ios code");
}
@Override
public void writeNote() {
app.setNote("ios note");
}
@Override
public APP getAPP() {
return app;
}
}
測試:
public static void main(String[] args) {
TechnicalManager manager = new TechnicalManager();//創建技術經理對象(指揮者)
IosDeveloper iosDeveloper = new IosDeveloper();//創建ios開發者(具體的建造者)
manager.setDeveloper(iosDeveloper);//技術經理指揮ios程序員去干活
APP app = manager.getAPP(); //完成 ios端 APP
System.out.println(app);
}
運行結果:
APP {code='ios code', note='ios note'}
如果需要其他端的APP,只需另創建個建造者就可以完成APP。
但是我們看大多數的源碼,里面的建造者模式是沒有指揮者的(Director),這個又是怎樣實現的呢。 在Android中的彈框(dialog)的實現就用了建造者模式,我們就大致實現下它是怎么創建的。 源碼中還是很復雜的,這里只是簡單實現。
在Android中創建一個dialog的代碼
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("消息")
.setIcon(R.drawable.icon);
AlertDialog alertDialog=builder.create();
這是Android創建dialog最簡單的方式。 現在我們就實現這樣的寫法。
// Product
public class AlertDialog{
String msg;//消息
String tilte;//標題
int icon; //圖標
@Override
public String toString() {
return "AlertDialog{" +
"msg='" + msg + '\'' +
", tilte='" + tilte + '\'' +
", icon=" + icon +
'}';
}
//建造者 (Builder)
static class Builder{
private AlertDialog mDialog=new AlertDialog();
public Builder setIcon(int icon){
mDialog.icon=icon;
return this;
}
public Builder setTitle(String title){
mDialog.tilte=title;
return this;
}
public Builder setMsg(String msg){
mDialog.msg=msg;
return this;
}
public AlertDialog create(){
return mDialog;
}
}
}
測試 :
public static void main(String args[]) {
//因為Builder是AlertDialog的成員內部類,所以這樣調用AlertDialog.Builder
AlertDialog.Builder builder = new AlertDialog.Builder();
builder.setIcon(1)
.setMsg("消息")
.setTitle("提示");
AlertDialog dialog = builder.create();
System.out.println(dialog);
}
運行結果:
AlertDialog{msg='消息', tilte='提示', icon=1}
可以看到我們也按照Android源碼這樣的調用方法創建了自己的dialog。 Android中的dialog實現比我這復雜,dialog屬性更多,邏輯更復雜。
我們可以再看看Retrofit中使用的建造者模式
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
這是創建Retrofit對象。可以看到也是用了建造者模式。 這里就不說了,下次分析源碼時再說。
總結:建造者模式只要明白四個角色Product、Builder、ConcreteBuilder、Director的作用,就差不多明白建造者模式是怎么回事了。
優點:
- 將產品的本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
- 每一個具體建造者都是相對獨立的,不依賴其他的具體建造者,可以方便地替換和增加新的具體建造者,用戶使用不同的具體建造者就能得到不同的產品對象。 由于指揮者(Director)是針對抽象的建造者(Builder)編程,增加新的建造者無需修改原來的建造者,符合‘開閉原則’。
缺點:
- 所創建的產品具有較多的共同點,其組成部分相似。 如果產品之間的差異性很大的話,就不適用建造者模式。
- 產品的內部變化復雜的話,可能會導致定義很多的具體建造者來應對這樣的變化,這樣會導致系統的復雜性。