设计模式之二 建造者(Builder)

引言

见藐小之物必细察其纹理,故时有物外之趣。-------------------- 《童趣》

    最近在写自己项目用上rxjava跟retrofit,然后就是一把梭。尽管"."就完事了实现功能之后就想了一下为什么可以这样操作。所以收集资料然后写下此篇。

什么是建造者模式(Builder)

    先看一下我们的代码:

  1. 首先是一般的构造器:
        
    public Person(String name,String age,String sex);类似于这种的代码在我们的项目中是很常见的,我们用这个来构造一个有参对象,调用的时候只需要get 就可以得到对应的值,行吧两个参数咋可以搞定那么三个呢,四个呢。。。20个呢。。所以这个时候直接构造函数就不行了。那么我们会采用什么
    Persion persion = new Persion(); persion.setName(); persion.setAge();
        这是我们常用的手段,但是调用一些系统的API的时候:

    AlertDialog alertDialog = new AlertDialog.Builder(this).create();
    alertDialog.setTitle("");
    alertDialog.setIcon(R.drawable.a);
    

    这是什么个操作 怎么跟我们之前使用的不一样。我们点进去看看。

 public static class Builder {
    private final AlertParams P;
    private final int mTheme;

    public Builder(@NonNull Context context) {
        this(context, AlertDialog.resolveDialogTheme(context, 0));
    }
    //这里省去部分代码 只截取其中的一部分 这里的setTitle 跟setIcon setView 一样 都是返回的this。
    public AlertDialog.Builder setTitle(@StringRes int titleId) {
        this.P.mTitle = this.P.mContext.getText(titleId);
        return this;
    }

    这是builder 然后我们看看后面的Create是个啥:

       public AlertDialog create() {
        AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
        this.P.apply(dialog.mAlert);
        dialog.setCancelable(this.P.mCancelable);
        if (this.P.mCancelable) {
            dialog.setCanceledOnTouchOutside(true);
        }

        dialog.setOnCancelListener(this.P.mOnCancelListener);
        dialog.setOnDismissListener(this.P.mOnDismissListener);
        if (this.P.mOnKeyListener != null) {
            dialog.setOnKeyListener(this.P.mOnKeyListener);
        }
        //这边直接返回的是一个dialog 也就是我们刚开始new 的对象
        return dialog;
    }

        看上去好像就是多了些Build和Create() 然后后面的用法跟之前的并没有什么差异反而看上去更麻烦了。Google这样设计肯定有他的道理,我们肯定看过一些代码 比如:

        new AlertDialog.Builder(this)
            .setIcon(R.mipmap.ic_launcher)
            .setMessage("Hello World")
            .setTitle("Test")
            .create()
            .show();

        这样就可以直接弹出一个对话框,这样是不是就很舒服 很快了 结构也非常的清晰。这就是们的Builder 的一个例子。

为什么用建造者

        如果一个对象要很多参数,默认的或者不定的,就可以然后用上述的例子来初始化。

如何使用

        我们看一个例子,首先要有一个具体的Builder 这里我写成类似菜单,名字,价钱,做的时间,几桌

 public abstract class Builder {
       public abstract void Name(String name);
       public abstract void Cost(String cost);
       public abstract void Time(String time);
       public abstract void Table();
       public abstract Menu createMenu(); 
 }

        再来一个对象

public abstract class Menu {
protected String name ;
protected String cost;
protected String time;
protected String table;

public Menu() {
}
//我们得要知道是哪桌点的菜吧
public abstract void setTable();
//set()/get()省掉。

@Override
public String toString() {
    return "Menu{" +
            "name='" + name + '\'' +
            ", cost='" + cost + '\'' +
            ", time='" + time + '\'' +
            ", table='" + table + '\'' +
            '}';
}
}

        好了 现在第一桌来了 ,我们要记一下第一桌的情况

public class FirstMenu extends Menu {
public FirstMenu() {
}
@Override
public void setTable() {
   table = "第一桌";
 }
}

        有对象,有上面FirstMenu的抽象实现方法,我们需要一个set方法吧,那来吧

public class MenuBuilder extends Builder {
private Menu menu = new FirstMenu();
@Override
public void Name(String name) {
    menu.setName(name);
}
@Override
public void Cost(String cost) {
    menu.setCost(cost);
}
@Override
public void Time(String time) {
     menu.setTime(time);
}
@Override
public void Table() {
    menu.setTable();
}
//注意这里返回的是menu 是不是跟上面 new AlertDialog.Builder(this).create(); 这个create() 有联想
@Override
public Menu createMenu() {
  return menu;
}
}

        这下菜单有了,怎么设置有了,那么来个设置点菜的吧,来个服务员吧。 (专业术语这个角色叫Director)

public class Waiter {

Builder builder =null;

public Waiter(Builder builder) {
    this.builder = builder;
   }
public void construct(String name  , String cost ,String time){
    builder.Name(name);
    builder.Cost(cost);
    builder.Time(time);
    builder.Table();
    }
}

        ok角色到齐了,那么怎么让他们动起来呢。

    private void Test(){
      Builder builder = new MenuBuilder();
      Waiter waiter = new Waiter(builder);
      waiter.construct("韭菜炒鸡蛋","18","5分钟");
    //这边就能打印出第一桌 然后上述的三个
    }

        这些就是简单的Builder 的使用例子,以后第二桌第三桌若干桌,只要继续一下,自己手动写一下第几桌然后就可以让服务员过来点菜了。(智能一点的就不需要自己写第几桌,也让服务员写 也就是把Menu里面的setTable()方法给弄成跟name,cost 一样的形式)。这样的好像变的更加复杂了。想想,我明明可以写一个对象自己设置就完事了。。而且也是用的上面的construct(),远不如我对象直接来构造器。好吧我们可能要修改一下。
我们来吧MenuBuilder中的方法改成如下形式:

   @Override
   public MainBuilderOne Name(String name) {
      menu.setName(name);
      return this;
    }

借助于上面的思想

    AlertDialog.Builder setTitle(@StringRes int titleId) {
    this.P.mTitle = this.P.mContext.getText(titleId);
    return this;

好了咋们现在也可以直接一把梭

 new MainBuilder()
            .Cost("12")
            .Name("西红柿鸡蛋面")
            .Time("10分钟")
            .Table()
            .createMenu());

总结

        XXX.XXX().XXXX() 这种操作在敲代码的时候很常用的,我们这样理解一下就不仅仅会用一些别人封装好的方法,我们还可以知道他内部的实现,原来这边是这样设计的,让我们在枯燥的写代码的时候能有一些新奇的感受。不至于那么枯燥。下面还有几个常见的如有兴趣可以去看看源码设计。这里再贴一个简单封装的HtppManager:

    public class HttpManager {
     //超时时长
      public static final int DEFAULT_TIME_OUT = 5;
      public static final int DEFAULT_TIME = 10;
      private Retrofit retrofit ;
      /**
       * 初始化http 各种
       */
      private HttpManager() {
          OkHttpClient.Builder builder = new OkHttpClient.Builder();
          builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);
          builder.readTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);
          builder.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);
          retrofit = new Retrofit.Builder()
                  .client(builder.build())
                  .baseUrl(GeeksApis.HOST)
                  .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                  .addConverterFactory(GsonConverterFactory.create())
                  .build();
      }
      private static class SingletonHolder{
          private static final HttpManager INSTANCE = new HttpManager();
      }
      public static HttpManager getInstance(){
          return SingletonHolder.INSTANCE;
      }
      public <T> T create(Class<T> service){
          return retrofit.create(service);
      }
 }

用到了单例,然后HTTPClient 和Retrofit2都是用到的建造者模式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容