設計模式之建造者模式

建造者模式(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();

六、后記

以上就是建造者模式的全部內容,希望可以幫助到有需要的人.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 模式定義 建造者模式:將一個復雜產品的創建與表示分離,使得同樣的創建過程可以創建不同的表示客戶端不用去關心產品對象...
    C_zx閱讀 520評論 1 5
  • 1、初識建造者模式 建造者模式屬于創建型模式。比如說:樓房是千差萬別的,樓房的外形,層數,內部房間的數量,房間的裝...
    嘮嗑008閱讀 494評論 0 2
  • 建造者模式 定義 將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。 首先這是一個復雜的對...
    晨鳴code閱讀 862評論 0 1
  • 建造者模式 想象一下,我們想要創建一個由多個部分構成的對象,而且它的構成需要一步接一步地完成。只有當各個部分都創建...
    英武閱讀 2,217評論 1 50
  • 陰雨綿綿的天氣,依然遮擋不住春光滿園
    林雨霖閱讀 287評論 0 0