建造者模式(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();
六、后記
以上就是建造者模式的全部內容,希望可以幫助到有需要的人.