設(shè)計模式之建造者模式Builder

建造者模式(Builder Pattern)

使用多個簡單的對象一步一步構(gòu)建成一個復(fù)雜的對象。一個 Builder 類會一步一步構(gòu)造最終的對象,該 Builder 類是獨立于其他對象的褐望。
將一個復(fù)雜的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
主要解決:主要解決在軟件系統(tǒng)中丙猬,有時候面臨著"一個復(fù)雜對象"的創(chuàng)建工作,其通常由各個部分的子對象用一定的算法構(gòu)成费韭;由于需求的變化茧球,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩(wěn)定星持。

優(yōu)點:

1. 建造者獨立抢埋,易擴展
2. 便于控制細節(jié)風險

缺點:

1. 產(chǎn)品必須有共同點,范圍有限制
2. 如內(nèi)部變化復(fù)雜督暂,會有很多的建造類

使用場景

根據(jù)Buidler的特性揪垄,可以在一些基本部件不會變,而其組合經(jīng)常變化的時候使用Buidler模式,與工廠模式的區(qū)別是建造者模式更加關(guān)注與零件裝配的順序逻翁。有下面兩點依據(jù)可以參考:

1. 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)
2. 需要生成的對象內(nèi)部屬性本身相互依賴

在Android中也有很多地方體現(xiàn)了這個模式饥努,如常見的對話框 AlertDialog,不會直接new 一個 AlertDialog卢未, 而是通過內(nèi)部Builder類構(gòu)造不同的dialog肪凛,內(nèi)部可以設(shè)置各種信息堰汉,如標題,消息內(nèi)容伟墙,按鈕文字等等翘鸭。類似的還有Notification的創(chuàng)建。

實現(xiàn)方式

以《21種設(shè)計模式》的章節(jié)例子快餐店的案例戳葵。

快餐店的商業(yè)案例就乓,其中,一個典型的套餐可以是一個漢堡(Burger)和一杯冷飲(Cold drink)拱烁。漢堡(Burger)可以是素食漢堡(Veg Burger)或雞肉漢堡(Chicken Burger)生蚁,它們是包在紙盒中。冷飲(Cold drink)可以是可口可樂(coke)或百事可樂(pepsi)戏自,它們是裝在瓶子中邦投。

我們將創(chuàng)建一個表示食物條目(比如漢堡和冷飲)的 Item 接口和實現(xiàn) Item 接口的實體類,以及一個表示食物包裝的 Packing 接口和實現(xiàn) Packing 接口的實體類擅笔,漢堡是包在紙盒中志衣,冷飲是裝在瓶子中。然后我們創(chuàng)建一個 Meal 類猛们,帶有 Item 的集合 和一個通過結(jié)合 Item 來創(chuàng)建不同類型的 Meal 對象的 MealBuilder念脯。

類關(guān)系圖如下:


類關(guān)系圖

定義一個表示食物條目和食物包裝的接口。

    Item.java
    public interface Item {
       public String name();
       public Packing packing();
       public float price();    
    }
    
    Packing.java
    public interface Packing {
       public String pack();
    }

創(chuàng)建實現(xiàn) Packing 接口的實體類弯淘。

    public class Wrapper implements Packing {
    
       @Override
       public String pack() {
          return "Wrapper";
       }
    }
    
    public class Bottle implements Packing {
    
       @Override
       public String pack() {
          return "Bottle";
       }
    }

創(chuàng)建實現(xiàn) Item 接口的抽象類绿店,該類提供了默認的功能。

    public abstract class Burger implements Item {
    
       @Override
       public Packing packing() {
          return new Wrapper();
       }
    
       @Override
       public abstract float price();
    }
    
    public abstract class ColdDrink implements Item {
    
        @Override
        public Packing packing() {
           return new Bottle();
        }
    
        @Override
        public abstract float price();
    }

創(chuàng)建擴展了 Burger 和 ColdDrink 的實體類庐橙。

    public class VegBurger extends Burger {
    
       @Override
       public float price() {
          return 25.0f;
       }
    
       @Override
       public String name() {
          return "Veg Burger";
       }
    }
    
    public class ChickenBurger extends Burger {
    
       @Override
       public float price() {
          return 50.5f;
       }
    
       @Override
       public String name() {
          return "Chicken Burger";
       }
    }
    
    public class Coke extends ColdDrink {
    
       @Override
       public float price() {
          return 30.0f;
       }
    
       @Override
       public String name() {
          return "Coke";
       }
    }
    
    public class Pepsi extends ColdDrink {
    
       @Override
       public float price() {
          return 35.0f;
       }
    
       @Override
       public String name() {
          return "Pepsi";
       }
    }

創(chuàng)建一個 Meal 類假勿,帶有上面定義的 Item 對象。

    import java.util.ArrayList;
    import java.util.List;
    
    public class Meal {
       private List<Item> items = new ArrayList<Item>();    
    
       public void addItem(Item item){
          items.add(item);
       }
    
       public float getCost(){
          float cost = 0.0f;
          for (Item item : items) {
             cost += item.price();
          }        
          return cost;
       }
    
       public void showItems(){
          for (Item item : items) {
             System.out.print("Item : "+item.name());
             System.out.print(", Packing : "+item.packing().pack());
             System.out.println(", Price : "+item.price());
          }        
       }    
    }

創(chuàng)建一個 MealBuilder 類怕午,實際的 builder 類負責創(chuàng)建 Meal 對象废登。

public class MealBuilder {

   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   

   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}

BuiderPatternDemo 使用 MealBuider 來演示建造者模式(Builder Pattern)

    public class BuilderPatternDemo {
       public static void main(String[] args) {
          MealBuilder mealBuilder = new MealBuilder();
    
          Meal vegMeal = mealBuilder.prepareVegMeal();
          System.out.println("Veg Meal");
          vegMeal.showItems();
          System.out.println("Total Cost: " +vegMeal.getCost());
    
          Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
          System.out.println("\n\nNon-Veg Meal");
          nonVegMeal.showItems();
          System.out.println("Total Cost: " +nonVegMeal.getCost());
       }
    }
    
驗證輸出:
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0


Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

Glide建造者模式的應(yīng)用

以流行的圖片加載框架Glide 為例,來學習實現(xiàn)方式郁惜。Glide里 Builder模式的應(yīng)用,典型的用法如下

  GlideApp
    .with(contex)
    .load(url)
    .centerCrop()
    .placeholder(R.drawable.loading_spinner)
    .into(myImageView);

GlidApp.with(xxx)返回的是GlideRequests甲锡, GlideRequests 是建造者模式Builder封裝的一個管理器兆蕉,最終通過重載方法 into 完成要構(gòu)造的復(fù)雜對象 ViewTarget。 ViewTarget封裝了我們的ImageView等可以加載圖片的View缤沦。要構(gòu)造的復(fù)雜對象有了虎韵,那我們就要找它的 Builder了。

通過Glide的with方法缸废,獲取到GlideRequests包蓝。

GlidApp.java

  @NonNull
  public static GlideRequests with(@NonNull Context arg0) {
    return (GlideRequests) Glide.with(arg0);
  }

Glide.java

  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

看看 GlideRequests 內(nèi)部是什么樣的驶社?可以發(fā)現(xiàn)都是方法的重寫,做強制轉(zhuǎn)型為 GlideRequest测萎。注意GlideRequest不是GlideRequests, 少了一個s

  @Override
  @NonNull
  @CheckResult
  public GlideRequest<Bitmap> asBitmap() {
    return (GlideRequest<Bitmap>) super.asBitmap();
  }

找到它的父類RequestManager, 發(fā)現(xiàn)這里的方法返回的都是 RequestBuilder亡电,RequestBuilder 是 GlideRequest 的父類。RequestBuilder 里有各種配置硅瞧,各種加載圖片的方式份乒,只下載圖片的方式,加載縮略圖的方式等等

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
    return loadGeneric(uri);
  }
  
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable byte[] model) {
    RequestBuilder<TranscodeType> result = loadGeneric(model);
    if (!result.requestOptions.isDiskCacheStrategySet()) {
        result = result.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
    }
    if (!result.requestOptions.isSkipMemoryCacheSet()) {
      result = result.apply(skipMemoryCacheOf(true /*skipMemoryCache*/));
    }
    return result;
  }
  
  @NonNull
  @CheckResult
  protected RequestBuilder<File> getDownloadOnlyRequest() {
    return new RequestBuilder<>(File.class, this).apply(DOWNLOAD_ONLY_OPTIONS);
  }

RequestBuilder 也就是我們要找的 Builder腕唧,故Glide的 Builder模式是以 RequestBuilder(或子類GlideRequest等) 為Builder或辖,構(gòu)建復(fù)雜的 ViewTarget 對象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枣接,一起剝皮案震驚了整個濱河市颂暇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌但惶,老刑警劉巖耳鸯,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異榆骚,居然都是意外死亡片拍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門妓肢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捌省,“玉大人,你說我怎么就攤上這事碉钠「倩海” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵祝高,是天一觀的道長工闺。 經(jīng)常有香客問我瓣蛀,道長,這世上最難降的妖魔是什么叠殷? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任林束,我火速辦了婚禮,結(jié)果婚禮上壶冒,老公的妹妹穿的比我還像新娘。我一直安慰自己避除,他們只是感情好胸嘁,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布群井。 她就那樣靜靜地躺著,像睡著了一般书斜。 火紅的嫁衣襯著肌膚如雪酵使。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音缺脉,去河邊找鬼。 笑死攻礼,一個胖子當著我的面吹牛礁扮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播负蠕,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼倦畅,長吁一口氣:“原來是場噩夢啊……” “哼叠赐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赛不,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤踢故,失蹤者是張志新(化名)和其女友劉穎惹苗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淋纲,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡洽瞬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年伙窃,在試婚紗的時候發(fā)現(xiàn)自己被綠了样漆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氛濒。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖京景,靈堂內(nèi)的尸體忽然破棺而出骗奖,到底是詐尸還是另有隱情,我是刑警寧澤执桌,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布仰挣,位于F島的核電站,受9級特大地震影響错蝴,放射性物質(zhì)發(fā)生泄漏洲愤。R本人自食惡果不足惜柬赐,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一肛宋、第九天 我趴在偏房一處隱蔽的房頂上張望束世。 院中可真熱鬧良狈,春花似錦、人聲如沸遇西。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渗常,卻和暖如春汗盘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背癌椿。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工踢俄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留都办,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓世舰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胰蝠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容