builder模式-對象創建型模式

    前言:這個模式看了很長時間,反反覆覆,也沒理解多少,當然主要還是現實代碼開發中未遇到過類似的場景是需要使用到builder模式的,可能做的項目都不夠複雜吧,所以還是轉載一篇我個人認爲解釋不錯的文章來的比較實際,待自己在現實項目中遇到過後,再來寫具體的感受,網上對於builder模式的例子都是汽車類例子,所以除此之外我會繼續補充一個迷宮的例子加深大家影響,主要我個人認爲這兩個場景是使用到builder模式的典型場景。下面是我轉載的原文內容:來自:http://dev.csdn.net/htmls/14/14780.html

 

---------------------------------------------------------------轉載開始--------------------------------------------------------------------------

   當初我學這個設計模式的時候,怎麼都搞不懂它和工廠模式到底有什麼區別,而且看了很多別人實現的源代碼,似乎都是在模仿工廠模式的實現。並沒有突出Builder與Factory的本質差別。實際上,就我的理解,既然Builder和Factory同屬創建型模式,那麼他們的最大共同點就在於都可以創建類對象,在這點上,不光這兩個模式一樣,其它創建型模式也一樣。但正如我在《深入探索Factory模式與Prototype模式的異同(續)》一文中所說,這些模式,功能上的相似,只是形似而非神似。既然這樣,那好,下面就讓我們能看看Builder和Factory在功能的相似上,存在哪些神韻方面的差別。

   首先,也是最重要的一點,就是雖然Builder和Factory都可創建產品,但兩者所創建的產品類型完全不一樣。Factory創建只能是單一的產品(單一在這指它非複合產品),而Builder所創建的產品是複合產品,即產品本身就是由其它部件產品組成的。舉個例子來說,現在要生產一輛車,假設它就只由這三個部分組成:玻璃、輪子、發動機。對於工廠模式來說,他創建後返回的,只能是玻璃,或者輪子,抑或是發動機。不管怎麼樣,他不能向客戶返回一輛完整的汽車,要得到一輛完整的汽車,客戶必須自己動手去把這些零部件組裝成一輛汽車。從這個意義上來講,工廠模式中的工廠,只是充當了零件廠的角色。那Builder又是如何創建產品的呢?在Builder模式中,一般不需要、也不充許向客戶返回單個部件,他向客戶返回的,僅僅就是一部已經完全組裝好的汽車成品。對於汽車部件的生產細節,客戶不需要、也不應該讓他們知道。寫到這,我突然想到了組裝電腦與品牌電腦的差別,組裝電腦雖然價格便宜,且易於改動,但性能沒有保證,另外你自己還必須瞭解很多有關電腦方面的知識;對於品牌電腦,價格貴這點先暫時不說,關鍵在於他不靈活,但是它的性能可以得到很好保證(由廠家),這易像我們在Builder的系統端保證部件的質量一樣。另外,對於品牌電腦,客戶根本不需要了解多少電腦組裝方面的知識,就可以把一臺電腦抱回家,開機使用了。那麼,在實際運用中,你是喜歡做DIY一族呢,還是喜歡穩定有保證的質量呢?好像在我們編著程的這個過程中,我們比較趨向於使用品牌電腦。這也就爲我們正確使用這兩種設計模式提供了一個方向:如果你要生產的產品是由不同部件組成的,你最好使用Builder模式,而非Factory模式。

    另外,Builder和Factory的差別,就在於他們所生產部件產品所在產品樹的問題。這樣說,可能有點拗口。具體來說吧,在工廠模式中,我們知道,一個工廠可以創建多個產品,但一個工廠模式中所創建的某個產品,都會和另一個工廠中所創建的產品在同一棵繼承樹上。如果大家看過我最早寫的《用Java實現的設計模式系列(1)Factory 》那篇文章,就會記得,我在CFactoryMac中創建了一種產品叫MacRam,而在CFactoryWin中創建了另一種產品叫WinRam,很顯然,這兩種產品是在同一棵繼承樹上的。對於它們之所以會出現在同一棵繼承樹上,是完全由Factory模式本身所決定的。大家如果看過Factory的UMl圖,就應該記得,要實現Factory模式,一定要有一個Abstract Product類,具體產品再由它派生出來。好了,說完了Factory,再讓我們來看看Builder中是否必這麼做!實際上,在Builder模式中,我們只是在Abstract Builder中封裝了創建部件的接口,而具體創建了什麼部件,不同的實際Builder可能會生產出完全不一樣的部件,這樣不會存在任何問題,因爲,我上面說過,Builder只是向客戶返回成品,而不向客戶返回具體部件,這樣,當然就充許產品的部件按要求隨意變化了。再舉個例子吧,假如你現在要創建兩種風馬不相及的東西,例如一種是人,它就只由這幾部分組成:腦、身、四肢;另一種是樹,也由三個部分組成:根、葉、莖。好了,你現在要用Builder模式創建出這兩種產品,能不能做到呢?看看下面的代碼就明白了:

interface ABuilder{

  public void builderPartA();

  public void builderPartB();

  public void builderPartC();

}

class CBuilderHuman implements ABuilder {

  private Human human;

  public CBuilderHuman() {

      human=new Human();

  }

  public void builderPartA(){human.head=new Head()};

  public void builderPartB(){human.body=new Body()};

  public void builderPartC()(human.limb=new Limb()};

  public Human getProduct(){

    builderPartA();

    builderPartB();

    builderPartC();

    return human;

}

class CBuilderTree的代碼類似,這裏就不寫了。

再來看客戶端代碼:

public stacic void main(String[] args) {

   CBuilderHuman builderhuman=new CBuilderHuman();

   CBuilderTree buildertree=new CBuilderTree();

   Human man;

   Tree tree;

   man=builderman.getProduct();

   tree=buildertree.getProduct();

}

看了上面的代碼,相信大家對於Factory與Builder的產品樹問題已有了清楚的認識。雖然,Builder模式可以創建出風馬不相及的產品,但一般我們不會這麼做。更常見的運用是部件產品在同一棵繼承樹上。這樣做的原因,大家想想面向對象的本質目的也就明白了。

好了,隨手寫寫,想不到就寫了這麼一大篇。還是那句話,代碼說明一切,那接下來,就讓我們來看代碼吧!

 

/**

 * Design Pattern In Java

 * Name:Builder

 * 目的:利用Builder模式創建兩種汽車carA和carB

 * Car=Glass+Wheel+Engine

 * carA=AmericanGlass+JapaneseWheel+ChinaEngine

 * carB=JapaneseGlass+AmericanWheel+FranceEngine

 * A:abstract

 * C:Concret

 * Author:blackphoenix

 * Modify Date:2002-08-19

 */

 

/**

 * 定義部件Glass的抽象類AClass

 * 和兩個具體類AmericanGlass、JapaneseGlass

 */

 

abstract class AGlass{

 

}

class AmericanGlass extends AGlass{

  public String toString(){

    return "/"American Glass/" ";

  }

}

class JapaneseGlass extends AGlass{

  public String toString(){

    return "/"Japanese Glass/" ";

  }

}

/**

 * 定義部件Wheel的抽象類AWheel

 * 和兩個具體類AmericanWheel、JapaneseWheel

 */

 

abstract class AWheel{

 

}

class AmericanWheel extends AWheel{

  public String toString(){

    return "/"American Wheel/" ";

  }

}

class JapaneseWheel extends AWheel{

  public String toString(){

    return "/"Japanese Wheel/" ";

  }

}

/**

 * 定義部件Engine的抽象類AEngine

 * 和兩個具體類ChineseEngine、FranceEngine

 */

abstract class AEngine{

 

}

class ChineseEngine extends AEngine{

  public String toString(){

    return "/"Chinese Engine/" ";

  }

}

class FranceEngine extends AEngine{

  public String toString(){

    return "/"France Engine/" ";

  }

}

 

/**

 * 定義產品類Car

 */

class Car{

  AGlass glass;

  AWheel wheel;

  AEngine engine;

}

 

/**

 * 定義抽象建造器接口ABuilder

 */

interface ABuilder{

  public void buildGlass();

  public void buildWheel();

  public void buildEngine();

}

 

/**

 * 具體建造器類CBuilderCarA

 * carA=AmericanGlass+JapaneseWheel+ChineseEngine

 */

class CBuilderCarA implements ABuilder{

  private Car product=null;

  public CBuilderCarA(){

    product=new Car();

  }

  public void buildGlass(){

    product.glass=new AmericanGlass();

  }

  public void buildWheel(){

    product.wheel=new JapaneseWheel();

  }

  public void buildEngine(){

    product.engine=new ChineseEngine();

  }

  /**

   * 將建造部件的工作封裝在getProduct()操作中,主要是爲了向客戶隱藏實現細節

   * 這樣,具體建造類同時又起到了一個Director的作用

   */

  public Car getProduct(){

    buildGlass();

    buildWheel();

    buildEngine();

    return product;

  }

}

 

/**

 * 具體建造器類CBuilderCarB

 * carB=JapaneseGlass+AmericanWheel+FranceEngine

 */

class CBuilderCarB implements ABuilder{

  private Car product;

  public CBuilderCarB(){

    product=new Car();

  }

  public void buildGlass(){

    product.glass=new JapaneseGlass();

  }

  public void buildWheel(){

    product.wheel=new AmericanWheel();

  }

  public void buildEngine(){

    product.engine=new FranceEngine();

  }

  /**

   * 將建造部件的工作封裝在getProduct()操作中,主要是爲了向客戶隱藏實現細節

   * 這樣,具體建造類同時又起到了一個Director的作用

   */

  public Car getProduct(){

    buildGlass();

    buildWheel();

    buildEngine();

    return product;

  }

}

 

/**

 * 客戶端代碼,使用Builder創建兩種不同型別的carA和carB

 */

public class Builder{

  public static void main(String[] args){

    Builder client=new Builder();

    Car carA,carB;

    CBuilderCarA builderA;

    CBuilderCarB builderB;

    builderA=new CBuilderCarA();

    builderB=new CBuilderCarB();

    carA=builderA.getProduct();

    carB=builderB.getProduct();

    System.out.println("Car A is made by:"+carA.glass+carA.wheel+carA.engine);

    System.out.println("Car B is made by:"+carB.glass+carB.wheel+carB.engine);

  }

}

 

---------------------------------------------------------------轉載結束--------------------------------------------------------------------------

 

後續:

    builder模式的參與對象有:

    Builder----爲創建一個Product對象的各個部件指定抽象接口

    ConcreteBuilder----實現Builder的接口以構造和裝備該產品的各個部件。

                             ----定義並明確它所創建的表示。

                             ----提供一個檢索產品的接口

    Director----構造一個使用Builder接口的對象。

    Product----表示被構造的複雜對象。ConcreteBuilder創建該產品的內部表示並定義它的裝備過程。

                ----包含定義組成部件的類,包括將這些部件裝備成最終產品的接口。

 

    Abstract factory模式參與對象有:

    AbstractFactory----聲明一個創建抽象產品對象的操作接口

    ConcreteFactory----實現創建具體產品對象的操作。

    AbstractProduct----爲一類產品對象聲明一個接口.

    ConcreteProduct----定義一個將被相應的具體工廠創建的產品對象。

      ----實現AbstractProduct接口

    Client                ----僅使用由AbstractFactory和AbstractProduct類聲明的接口.

 

    綜看builder模式和Abstract factory模式的參與對象,可以發現abstract factory參與對象的product必須繼承AbstractProduct對象,也就是說Abstract factory模式中的產品其實是一系列相關的產品。而在builder模式中,對於product沒有這個規定,因此個人認爲這是兩個模式中最大的區別.builder模式中的ConcreteBuilder和Abstract factory模式中的ConcreteFactory其實功能很相似,都是創建具體的產品了,只是創建的細節不同,前者是通過組合的方式創建對象。

    另外補充的迷宮的例子。注意,爲了比較builder模式與Abstract factory模式的區別,所以這裏的例子是通過Abstractory factory的例子改造成builder模式,便於理解兩者的區別,雖然有點改的不倫不類,見諒。

 

------------------Builder類-------------------

package com.gof.chapter3.builder.maze;

/**

 * 定義了builder產生子產品的方法

 * @author ben

 */

public abstract class MazeBuilder {

public abstract void BuilderMaze();

public abstract void BuilderRoom(String roomNo);

public abstract void BuilderDoor(String roomFromNo,String roomToNo);

public abstract Maze GetMaze();

public Direction commonWall(Room roomFrom,Room roomTo){

//這個程序主要用與返回門在房間的方向,這裏偷懶返回固定值

return Direction.East;

}

protected Maze maze;

}

 

---------------Director類------------------

 

package com.gof.chapter3.builder.maze;

/**

 *使用MazeBuilder來構建最終產品
**/

public class MazeGame {

public Maze createMaze(MazeBuilder builder){

builder.BuilderMaze();

builder.BuilderRoom("1");

builder.BuilderRoom("2");

builder.BuilderDoor("1", "2");

return builder.GetMaze();

}

public Maze createComplexMaze(MzeBuilder builder){

     builder.BuilderMaze();

                builder.BuilderRoom("1");

                //.......

                //.......

                 return builder.GetMaze();

}

}

 

 

-----------Maze類,我們要創建的產品--------------------

 

package com.gof.chapter3.builder.maze;

import com.gof.chapter3.builder.maze.Room;

public class Maze {

private java.util.List<Room> rooms = null;

public Maze(){

rooms = new java.util.ArrayList<Room>();

System.out.println("產生了一個迷宮對象..........");

}

public void setRoom(Room room){

rooms.add(room);

}

public Room getRoom(String roomNo){

for(Room room : rooms){

if(roomNo.equals(room.getRoomNo()))return room;

}

return null;

}

public boolean haveRoom(String roomNo){

for(Room room : rooms){

if(roomNo.equals(room.getRoomNo()))return true;

}

return false;

}

}

 

---------下面是產品的虛類,基本爲了abstract factory兼容而存在的,如果純粹是builder模式就不需要這些類了---------

 

package com.gof.chapter3.builder.maze;

 

/**

 *房間,門,牆的接口

**/

public interface MapSite {

public abstract Object enter();

}

 

 

 

 

 

package com.gof.chapter3.builder.maze;

/**

 *方向

**/

public enum Direction {

North,East,South,West

}

 

 

 

 

package com.gof.chapter3.builder.maze;

/**

 * 房間虛類

 */

public abstract class Room implements MapSite{

public abstract Object enter() ;

public abstract void setSide(Direction direction,MapSite site);

public abstract MapSite getSide(Direction direction);

public abstract String getRoomNo();

protected String roomNo;

}

 

 

 

 

package com.gof.chapter3.builder.maze;

/**

 * 門虛類

 */

public abstract class Door implements MapSite{

public abstract Object enter();

}

 

 

 

 

package com.gof.chapter3.builder.maze;

/**

 * 牆虛類

 */

public abstract class Wall implements MapSite{

public abstract Object enter();

}

 

 

---------------------------------------------這裏是具體產品類,就算是純粹的builder類也是需要的哦------------------------------------

 

package com.gof.chapter3.builder.maze.common;

import java.util.HashMap;

import java.util.Map;

import com.gof.chapter3.builder.maze.Direction;

import com.gof.chapter3.builder.maze.MapSite;

import com.gof.chapter3.builder.maze.Room;

/**

 * 產品,房間

 */

public class CommonRoom extends Room {

private Map sides = null;

public CommonRoom(String _roomNo) {

this.roomNo = _roomNo;

System.out.println("建立了一個普通的房間..........");

sides = new HashMap();

}

 

public Object enter() {

return null;

}

 

public void setSide(Direction direction, MapSite site) {

// just 設置

sides.put(direction, site);

}

 

public MapSite getSide(Direction direction) {

if (sides.get(direction) != null) {

return (MapSite) sides.get(direction);

} else {

return null;

}

}

 

@Override

public String getRoomNo() {

// TODO Auto-generated method stub

return this.roomNo;

}

}

 

 

package com.gof.chapter3.builder.maze.common;

import com.gof.chapter3.builder.maze.Door;

import com.gof.chapter3.builder.maze.Room;

/**

 * 產品,門

 */

public class CommonDoor extends Door{

private Room roomFrom;

private Room roomTo;

public CommonDoor(Room _roomFrom,Room _roomTo){

this.roomFrom = _roomFrom;

this.roomTo = _roomTo;

System.out.println("建立了一個普通的門.........");

}

@Override

public Object enter() {

// TODO Auto-generated method stub

return null;

}

}

 

 

package com.gof.chapter3.builder.maze.common;

import com.gof.chapter3.builder.maze.Wall;

/**

 * 產品,牆

 */

public class CommonWall extends Wall{

public CommonWall(){

System.out.println("建立了一個普通的牆.............");

}

@Override

public Object enter() {

// TODO Auto-generated method stub

return null;

}

}

 

 

----------------------ConcreteBuilder類,生成各產品部件的具體實現類-----------------

 

package com.gof.chapter3.builder.maze.common;

import com.gof.chapter3.builder.maze.Direction;

import com.gof.chapter3.builder.maze.Door;

import com.gof.chapter3.builder.maze.Maze;

import com.gof.chapter3.builder.maze.MazeBuilder;

import com.gof.chapter3.builder.maze.Room;

public class CommonMazeBuilder extends MazeBuilder{

public CommonMazeBuilder(){

//~~

}

 

@Override

public void BuilderDoor(String roomFromNo, String roomToNo) {

Room roomFrom = this.maze.getRoom(roomFromNo);

Room roomTo = this.maze.getRoom(roomToNo);

Door door = new CommonDoor(roomFrom,roomTo);

roomFrom.setSide(this.commonWall(roomFrom, roomTo), door);

roomTo.setSide(this.commonWall(roomTo, roomFrom), door);

}

 

@Override

public void BuilderMaze() {

// TODO Auto-generated method stub

this.maze = new Maze();

}

 

@Override

public void BuilderRoom(String roomNo) {

if(!this.maze.haveRoom(roomNo)){

Room room = new CommonRoom(roomNo);

room.setSide(Direction.East, new CommonWall());

room.setSide(Direction.North, new CommonWall());

room.setSide(Direction.South, new CommonWall());

room.setSide(Direction.West, new CommonWall());

this.maze.setRoom(room);

}

}

 

@Override

public Maze GetMaze() {

// TODO Auto-generated method stub

return this.maze;

}

}

 

 

----------------------------最後是client測試類------------------
package com.gof.chapter3.builder.maze;
import com.gof.chapter3.builder.maze.common.CommonMazeBuilder;
public class Client {
public static void main(String[] ben){
MazeBuilder builder = new CommonMazeBuilder();
MazeGame game = new MazeGame();
Maze maze = game.createMaze(builder);
System.out.println(maze.getRoom("1").getRoomNo());
}
}
----------運行結果--------
產生了一個迷宮對象..........
建立了一個普通的房間..........
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的房間..........
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的牆.............
建立了一個普通的門.........
1

PS:如果你要比對abstract factory與builder代碼上的構造主要區別,觀察如下幾個類,就知道了:

  • abstrac factory模式中的:CommonMazeFactory是具體創建對象的工廠類,這個類不但能生成部件,而且還將最終產品返回
  • builder模式中的:CommonMazeBuilder主要功能是創建產品對象,比如門,房間和迷宮,並且建立這些部件創建的聯繫規則,但它沒有生成一個完整的對象,需要MazeGame的createMaze將部件組合。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章