1. 程式人生 > >Android開發中無處不在的設計模式——Builder模式

Android開發中無處不在的設計模式——Builder模式

上一篇文章介紹了單例模式,這一篇繼續介紹一個常見的模式——Builder模式。

那麼什麼是Builder模式呢。你通過搜尋,會發現大部分網上的定義都是

將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示

但是看完這個定義,並沒有什麼卵用,你依然不知道什麼是Builder設計模式。在此個人的態度是學習設計模式這種東西,不要過度在意其定義,定義往往是比較抽象的,學習它最好的例子就是通過樣例程式碼。

我們通過一個例子來引出Builder模式。假設有一個Person類,我們通過該Person類來構建一大批人,這個Person類裡有很多屬性,最常見的比如name,age,weight,height等等,並且我們允許這些值不被設定,也就是允許為null,該類的定義如下。

public class Person {
    private String name;
    private int age;
    private double height;
    private double weight;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public
void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public double getWeight() { return weight; } public void setWeight(double weight) { this
.weight = weight; } }

然後我們為了方便可能會定義一個構造方法。

    public Person(String name, int age, double height, double weight) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

或許為了方便new物件,你還會定義一個空的構造方法

    public Person() {
    }

甚至有時候你很懶,只想傳部分引數,你還會定義如下類似的構造方法。

  public Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

於是你就可以這樣建立各個需要的物件

Person p1=new Person();
Person p2=new Person("張三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("趙六",17,170,65.4);

可以想象一下這樣建立的壞處,最直觀的就是四個引數的建構函式的最後面的兩個引數到底是什麼意思,可讀性不怎麼好,如果不點選看原始碼,鬼知道哪個是weight哪個是height。還有一個問題就是當有很多引數時,編寫這個建構函式就會顯得異常麻煩,這時候如果換一個角度,試試Builder模式,你會發現程式碼的可讀性一下子就上去了。

我們給Person增加一個靜態內部類Builder類,並修改Person類的建構函式,程式碼如下。

public class Person {
    private String name;
    private int age;
    private double height;
    private double weight;

    privatePerson(Builder builder) {
        this.name=builder.name;
        this.age=builder.age;
        this.height=builder.height;
        this.weight=builder.weight;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    static class Builder{
        private String name;
        private int age;
        private double height;
        private double weight;
        public Builder name(String name){
            this.name=name;
            return this;
        }
        public Builder age(int age){
            this.age=age;
            return this;
        }
        public Builder height(double height){
            this.height=height;
            return this;
        }

        public Builder weight(double weight){
            this.weight=weight;
            return this;
        }

        public Person build(){
            return new Person(this);
        }
    }
}

從上面的程式碼中我們可以看到,我們在Builder類裡定義了一份與Person類一模一樣的變數,通過一系列的成員函式進行設定屬性值,但是返回值都是this,也就是都是Builder物件,最後提供了一個build函式用於建立Person物件,返回的是Person物件,對應的建構函式在Person類中進行定義,也就是建構函式的入參是Builder物件,然後依次對自己的成員變數進行賦值,對應的值都是Builder物件中的值。此外Builder類中的成員函式返回Builder物件自身的另一個作用就是讓它支援鏈式呼叫,使程式碼可讀性大大增強。

於是我們就可以這樣建立Person類。

Person.Builder builder=new Person.Builder();
Person person=builder
        .name("張三")
        .age(18)
        .height(178.5)
        .weight(67.4)
        .build();

有沒有覺得建立過程一下子就變得那麼清晰了。對應的值是什麼屬性一目瞭然,可讀性大大增強。

其實在Android中, Builder模式也是被大量的運用。比如常見的對話方塊的建立

AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("標題")
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setView(R.layout.myview)
        .setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        })
        .setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        })
        .create();
dialog.show();

其實在java中有兩個常見的類也是Builder模式,那就是StringBuilder和StringBuffer,只不過其實現過程簡化了一點罷了。

我們再找找Builder模式在各個框架中的應用。

如Gson中的GsonBuilder,程式碼太長了,就不貼了,有興趣自己去看原始碼,這裡只貼出其Builder的使用方法。

GsonBuilder builder=new GsonBuilder();
Gson gson=builder.setPrettyPrinting()
        .disableHtmlEscaping()
        .generateNonExecutableJson()
        .serializeNulls()
        .create();

還有EventBus中也有一個Builder,只不過這個Builder外部訪問不到而已,因為它的建構函式不是public的,但是你可以在EventBus這個類中看到他的應用。

public static EventBusBuilder builder() {
    return new EventBusBuilder();
}
public EventBus() {
    this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
    subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
    typesBySubscriber = new HashMap<Object, List<Class<?>>>();
    stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
    mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

再看看著名的網路請求框架OkHttp

Request.Builder builder=new Request.Builder();
Request request=builder.addHeader("","")
    .url("")
    .post(body)
    .build();

除了Request外,Response也是通過Builder模式建立的。貼一下Response的建構函式

  private Response(Builder builder) {
    this.request = builder.request;
    this.protocol = builder.protocol;
    this.code = builder.code;
    this.message = builder.message;
    this.handshake = builder.handshake;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.networkResponse = builder.networkResponse;
    this.cacheResponse = builder.cacheResponse;
    this.priorResponse = builder.priorResponse;
  }

可見各大框架中大量的運用了Builder模式。最後總結一下

  • 定義一個靜態內部類Builder,內部的成員變數和外部類一樣
  • Builder類通過一系列的方法用於成員變數的賦值,並返回當前物件本身(this)
  • Builder類提供一個build方法或者create方法用於建立對應的外部類,該方法內部呼叫了外部類的一個私有建構函式,該建構函式的引數就是內部類Builder
  • 外部類提供一個私有建構函式供內部類呼叫,在該建構函式中完成成員變數的賦值,取值為Builder物件中對應的值