設計模式之建造者模式

lijulin 8年前發布 | 8K 次閱讀 建造者模式 Java開發

建造者模式(Builder Pattern)也叫做生成器模式,今天讓我們一起學習一下建造者模式。

一、基本介紹

建造者模式的定義為: 將一個復雜對象的構建和它的表示分離開,使得同樣的構建過程可以創建不同的表示。

建造者模式主要由4個角色來組成:

1 . 抽象建造者(Builder)角色:該角色用于規范產品的各個組成部分,并進行抽象,一般獨立于應用程序的邏輯。

2 . 具體建造者(Concrete Builder)角色:該角色實現抽象建造者中定義的所有方法,并且返回一個組建好的產品實例。

3 . 產品(Product)角色:該角色是建造者中的復雜對象,一個系統中會有多于一個的產品類,這些產品類并不一定有共同的接口,完全可以是不相關聯的。

4 . 導演者(Director)角色:該角色負責安排已有模塊的順序,然后告訴Builder開始建造。

二、代碼實現建造者模式

上面說的東西都只是理論,多少有些空洞,現在我們就通過一個簡單的例子來體驗一下建造者模式。

1 . 創建產品類 Product.java 類

/**
 * 產品類
 *
 */
public class Product {
    //業務處理方法
}

由于我們的目的是最終生產產品,所以產品類中的邏輯實現我們暫且不關注,具體的方法要根據業務來寫。

2 . 創建抽象建造者類 Builder.java 類

/**
 * 抽象建造者類
 *
 */
public abstract class Builder {
    //設置產品的不同部分,以獲得不同的產品
    public abstract void setPart1();
    public abstract void setPart2();
    public abstract void setPart3();

    //建造產品
    public abstract Product builderProduct();
}

該類是一個抽象類,其中我們聲明了4個抽象方法,前面三個是負責給產品添加不同的部件,第四個方法是負責建造產品。但這只是一個框架,還沒有具體的實現。

3 . 創建具體建造者類 ConcreteBuilder.java 類

/**
 *具體建造者類
 *
 */
public class ConcreteBuilder extends Builder{
    //一個產品
    private Product product = new Product();

    //開始安裝產品的部件
    @Override
    public void setPart1() {
        //為product安裝部件1
    }

    @Override
    public void setPart2() {
        //為product安裝部件2
    }

    @Override
    public void setPart3() {
        //為product安裝部件3
    }

    //建造一個產品
    @Override
    public Product builderProduct() {
        // TODO Auto-generated method stub
        return product;
    }
}

該類會繼承自抽象建造者類 Builder ,并實現其中的方法。開始先聲明一個產品,然后在各個 setPart3 方法中添加具體的邏輯,然后在 builderProduct() 方法中返回生產好的產品。

4 . 創建導演類 Director() 類

上面我們已經實現了具體的建造者類,也具體寫好了安裝每個部件的方法,最后一步就是由導演類來知道具體構建者類如何制造產品啦。制造完的產品會交給導演類負責處理。

/**
 * 導演類
 *
 */
public class Director1 {
    private Builder builder = new ConcreteBuilder();

    //構建產品,調用各個添加部件的方法
    public Product build(){
        builder.setPart1();
        builder.setPart2();
        builder.setPart3();
        //調用構建產品的方法來構建產品
        return builder.builderProduct();
    }
}

這個類很簡單,其實就是獲得一個具體的建造者對象,然后調用具體的方法給產品安裝部件,安裝完成后調用 builder.builderProduct() 方法獲得建造好的產品,此處導演類是在 build() 方法中完成了建造過程,同時將獲得的建造好的產品返回出去,以供其他模塊使用該產品。

此處的導演類起到了封裝左右,可以避免高層模塊深入到建造者內部的實現類,而且導演類可以有多個,根據業務邏輯分別用來建造不同的產品并輸出。

三、建造者模式的優點

建造者模式的有點主要有以下幾點:

1 . 封裝性 。使用建造者模式可以使客戶端不必知道產品的內部實現細節

2 . 獨立易擴展 。由于建造過程是獨立的,更利于后期擴展

3 . 便于控制細節風險 。由于具體的產品建造是獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響

四、建造者模式的使用場景

說了這么多建造者模式的好處,那我們應該在什么場合使用它們呢,看下面:

1 . 相同的方法,不同的執行順序,產生不同的結果。這種情況我們只要利用不同的導演類來控制建造過程,就可以產生不同的產品,其他部分不用修改

2 . 多個零件和部件,都可以裝配到一個對象中,裝配不同的零件,產生不同的運行結果,我們同樣可以通過修改導演類來產生不同的產品

3 . 產品類非常復雜,此時我們也可以將產品的建造方法和具體的建造順序分離開來處理

4 . 在對象創建過程中會用到系統的一些其他對象,這些對象在產品對象的創建過程中不容易得到,可以采用建造者模式封裝該對象的創建過程

注:建造者模式關注的是零件的類型和裝配工藝的順序

五、建造者模式實站

說了半天建造產品,沒行動有卵用,來來來,咋們就用剛學的 建造者模式生產兩臺不同類型的電腦 練練手,代碼敲起來

1 . 創建產品父類 Computer 類

該類是我們建造的計算機的父類,其中包含了計算機的公共屬性以及屬性的get和set方法

package cn.codekong.start;

/**
 * 計算機類
 */
public class Computer {
    //型號
    private String type;
    //CPU
    private String cpu;
    //內存
    private String ram;
    //硬盤
    private String hardDisk;
    //顯示器
    private String monitor;
    //操作系統
    private String os;

    //對應的get和set方法
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getCpu() {
        return cpu;
    }
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
    public String getRam() {
        return ram;
    }
    public void setRam(String ram) {
        this.ram = ram;
    }
    public String getHardDisk() {
        return hardDisk;
    }
    public void setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
    }
    public String getMonitor() {
        return monitor;
    }
    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }
    public String getOs() {
        return os;
    }
    public void setOs(String os) {
        this.os = os;
    }
}

2 . 創建具體的產品類 T410 類和 X201 類

這兩個類均繼承自上面的 Computer 類,并且我們在該類在添加了兩臺計算機特有的屬性, T410 計算機用于獨立顯卡,而 X201 沒有,同時重寫了它們的 toString() 方法,返回它們的參數,便于最后我們建造完計算機后輸出它們各自的配置參數.

T410.java
package cn.codekong.start;

public class T410 extends Computer{
    //顯卡
    private String graphicCard;
    public T410() {
        this.setType("Thinkpad T410");
    }

    public String getGraphicCard(){
        return graphicCard;
    }

    public void setGraphicCard(String graphicCard){
        this.graphicCard = graphicCard;
    }

    @Override
    public String toString() {
        return "型號:\t" + this.getType() + "\nCPU\t" + this.getCpu()
                + "\n內存\t" + this.getRam() + "\n硬盤\t" + this.getHardDisk()
                + "\n顯卡\t" + this.getGraphicCard() + "\n顯示器\t" + this.getMonitor()
                + "\n操作系統\t" + this.getOs();
    }
}
X201.java
package cn.codekong.start;

public class X201 extends Computer{
    public X201() {
        this.setType("Thinkpad X201");
    }
    @Override
    public String toString() {
        return "型號:\t" + this.getType() + "\nCPU\t" + this.getCpu()
                + "\n內存\t" + this.getRam() + "\n硬盤\t" + this.getHardDisk()
                + "\n顯示器\t" + this.getMonitor() + "\n操作系統\t" + this.getOs();
    }
}

上面的(1)(2)步只是相當于我們有了我們需要的產品類已經聲明好了,下面開始寫我們的抽象建造類

3 . 抽象計算機建造類 ComputerBuilder 類

我們創建了一個接口,在其中聲明了我們要建造產品過程中需要用到的方法,其實就是產品的各個建造步驟

package cn.codekong.start;
/**
 * 抽象的計算機建造者
 * 聲明建造的公共方法
 */
public interface ComputerBuilder {
    //建造CPU
    void buildCpu();
    //建造內存
    void buildRam();
    //建造硬盤
    void buildHardDisk();
    //建造顯卡
    void buildGraphicCard();
    //建造顯示器
    void buildMonitor();
    //建造操作系統
    void buildOs();

    //得到建造好的計算機
    Computer getResult();
}

4 . 創建具體的建造類 T410Builder 類和 X201Builder 類

這兩個類要實現上一步定義的接口,然后實現里面的各個方法,其實就是實現各個具體的組裝方法中的邏輯,但此時只是把每一個組裝的步驟的邏輯具體化了,還沒有開始正式組裝。

T410Builder.java
package cn.codekong.start;

/**
 * T410的具體建造者實現抽象的計算機建造者
 */
public class T410Builder implements ComputerBuilder{
    private T410 computer = new T410();
    @Override
    public void buildCpu() {
        computer.setCpu("i5-450");
    }

    @Override
    public void buildRam() {
        computer.setRam("4G 1333MHz");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("500G 7200轉");
    }

    @Override
    public void buildGraphicCard() {
        computer.setGraphicCard("Nvidia");
    }

    @Override
    public void buildMonitor() {
        computer.setMonitor("14英寸 1280*800");
    }

    @Override
    public void buildOs() {
        computer.setOs("Windows7 旗艦版");
    }

    @Override
    public Computer getResult() {
        return computer;
    }
}
X201Builder.java
package cn.codekong.start;

/**
 * X201計算機的具體建造類實現抽象的計算機建造類
 */
public class X201Builder implements ComputerBuilder{
    private X201 computer = new X201();
    @Override
    public void buildCpu() {
        computer.setCpu("i3-350");
    }

    @Override
    public void buildRam() {
        computer.setRam("2G 1333M");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("250G 5400 轉");
    }

    @Override
    public void buildGraphicCard() {
        //無獨立顯卡
    }

    @Override
    public void buildMonitor() {
        computer.setMonitor("12英寸 1280*800");
    }

    @Override
    public void buildOs() {
        computer.setOs("Windows7 Home版");
    }

    @Override
    public Computer getResult() {
        return computer;
    }
}

5 . 導演類知道具體的建造者類建造產品 ComputerDirector 類

該類就比較簡單了,在該類內部實現兩個構造方法,分別對應實現兩種計算機的過程,這時候才是正式的建造過程。

package cn.codekong.start;

/**
 * 計算機導演類,知道具體建造者建造計算機
 */
public class ComputerDirector {
    ComputerBuilder builder;

    //建造T410計算機
    public T410 constructT410(){
        builder = new T410Builder();
        builder.buildCpu();
        builder.buildRam();
        builder.buildHardDisk();
        builder.buildGraphicCard();
        builder.buildMonitor();
        builder.buildOs();
        //建造結束將產品返回供外部使用
        return (T410)builder.getResult();
    }

    //建造X201計算機
    public X201 constructX201(){
        builder = new X201Builder();
        builder.buildCpu();
        builder.buildRam();
        builder.buildHardDisk();
        //由于X201沒有獨立顯卡,則不調用buildGraphicCard()函數
        //builder.buildGraphicCard();

        builder.buildMonitor();
        builder.buildOs();
        //建造結束將產品返回供外部使用
        return (X201)builder.getResult();
    }
}

6 . 最后讓我們測試一下建造的產品是否是好的,新建測試類 ComputerTest 類

package cn.codekong.start;

/**
 * 計算機建造測試類
 */
public class ComputerTest {
    public static void main(String[] args) {
        ComputerDirector computerDirector = new ComputerDirector();
        //建造T410計算機
        Computer t410 = computerDirector.constructT410();
        //輸出T410計算機的配置參數
        System.out.println(t410);

        System.out.println("------------我是分割線----------------");

        //建造X201計算機
        Computer x201 = computerDirector.constructX201();
        //輸出X201的計算機配置
        System.out.println(x201);
    }
}

輸出結果如下

型號:    Thinkpad T410
CPU    i5-450
內存    4G 1333MHz
硬盤    500G 7200轉
顯卡    Nvidia
顯示器    14英寸 1280*800
操作系統    Windows7 旗艦版
------------我是分割線----------------
型號:    Thinkpad X201
CPU    i3-350
內存    2G 1333M
硬盤    250G 5400 轉
顯示器    12英寸 1280*800
操作系統    Windows7 Home版

好了,經過上面的步驟,我們的產品就建造好咯。

這時候有人會說,你這個例子還是不接地氣啊,我怎么沒見過什么程序這么寫呢,好,那咋就來一個接地氣的例子,看下面的代碼:

AlertDialog alertDialog = new AlertDialog.Builder(this)
                .setTitle("我是標題")
                .setIcon(R.drawable.icon)
                .show();

上面是一個Android里面警告框的例子,我們可以通過鏈式調用的方法將標題和圖標傳入,然后調用show()方法就構建了一個警告框。這個例子是不是很常見,那我們就用一個類使用建造者模式實現一下吧:

package com.codekong.my;

import javax.naming.Context;

public class MyDialog {
    //警告框標題
    private String title;
    //警告框圖標資源Id
    private int iconId;
    //上下文環境
    private Context context;
    public String getTitle() {
        return title;
    }

    public int getIconId() {
        return iconId;
    }

    public Context getContext() {
        return context;
    }

    public static class Builder{
        //設置默認值
        private String title = "Title";
        private int iconId = 0;
        private Context context;
        public Builder(Context context){
            this.context = context;
        }

        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }

        public Builder setIconId(int iconId) {
            this.iconId = iconId;
            return this;
        }

        //應用我們的設置
        private void applyConfig(MyDialog myDialog){
            myDialog.title = title;
            myDialog.iconId = iconId;
            myDialog.context = context;
        }

        public MyDialog show(){
            MyDialog myDialog = new MyDialog();
            applyConfig(myDialog);
            return myDialog;
        }

    }
}

上面的類主要涉及到以下幾步:

1 . 創建一個類,先聲明他的成員變量以及成員變量的get方法(其實這一步就是建造者模式里面的 產品角色 ,get方法是為了我們使用時可以隨時查看我們自定義的產品屬性)

2 . 定義一個靜態內部類 Builder ,然后把我們產品定義的屬性在靜態內部類中復制一份,同時生成它的set方法(這一步呢其實就是我們的 抽象建造者角色 ,要注意的一點是為了實現鏈式調用,我們要讓我們的set方法返回值為Builder, 同時在set方法中返回this,也就是返回本對象)

3 . 接著定義 applyConfig() 方法,把我們通過set方法設置的值全部賦值給我們的外部類對應的成員變量(這一步就是我們的 具體的建造者 角色)

4 . 最后對外提供一個 show() 方法,在其中先 new 出一個我們的MyDialog對象,然后把它傳入調用applyConfig()方法,調用過后我們的myDialog對象就已經被設置屬性了,我們此時就可以將這個設置過的對象傳到外部供其他類使用(這一步就是我們的 導演角色 )

當我們使用的時候就可以通過下面代碼使用:

MyDialog myDialog = new MyDialog.Builder(this)
        .setTitle("我是標題")
        .setIconId(R.drawable.icon)
        .show();

 

 

來自:http://www.jianshu.com/p/4d074224117d

 

 本文由用戶 lijulin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!