1. 程式人生 > >Java設計模式-建造者(Builder)模式

Java設計模式-建造者(Builder)模式

最近在看Mybatis的原始碼, 在閱讀解析 XML 配置檔案的過程中, 發現使用到了建造者(Builder)模式。 因此, 打算重溫一下該設計模式。

由來

假設我們需要畫一個小人, 我們可能會有以下的建構函式定義:

public Person(HeadType headType, HairType hairType, HairColor hairColor, FaceType faceType, BodyType bodyType, ArmType amrType, LegType legTyype) {
}

看到這麼一個建構函式, 估計我們自己以後回來看的時候都懵了, 這麼多引數, 導致我們後續的維護也很麻煩。

而構造模式就可以解決此類的問題。

使用

目標是畫一個小人

1. 定義抽象 Builder

先定義抽象的PersonBuilder。 該類定義了畫小人需要的步驟, 這樣每個通過PersonBuilder產生的物件本質上就都是一樣的了, 只不過個性上可以不一樣。

abstract class PersonBuilder {
    protected Graphics graphics;

    public PersonBuilder(Graphics graphics) {
        this.graphics = graphics;
    }

    public abstract void buildHead();
    public abstract void buildBody();
    public abstract void buildArmLeft();
    public abstract void buildArmRight();
    public abstract void buildLegLeft();
    public abstract void buildLegRight();
}

2. 定義具體 Builder

在定義一個具體的實現類PersonFatBuilder。 該類繼承PersonBuilder, 並實現了抽象方法。

public class PersonFatBuilder extends PersonBuilder {
    public PersonFatBuilder(Graphics graphics) {
        super(graphics);
    }
    @Override
    public void buildHead() {
        graphics.drawOval(50, 20, 30, 30);
        graphics.drawArc(50, 30, 10, 5, 45, 135);
        graphics.drawArc(70, 30, 10, 5, 45, 135);
        graphics.drawArc(60, 35, 10, 5, 200, 135);
    }

    @Override
    public void buildBody() {
        graphics.drawRect(55, 50, 20, 50);
    }

    @Override
    public void buildArmLeft() {
        graphics.drawLine(55, 50, 40, 100);
    }

    @Override
    public void buildArmRight() {
        graphics.drawLine(75, 50, 90, 100);
    }

    @Override
    public void buildLegLeft() {
        graphics.drawLine(55, 100, 45, 150);
    }

    @Override
    public void buildLegRight() {
        graphics.drawLine(75, 100, 85, 150);
    }
}

3. 定義具體 Director

該類負責具體的建造過程, 對建成什麼樣不關心。

public class PersonDirector {
    private PersonBuilder personBuilder;

    public PersonDirector(PersonBuilder personBuilder) {
        this.personBuilder = personBuilder;
    }

    public void drawPerson() {
        personBuilder.buildHead();
        personBuilder.buildBody();
        personBuilder.buildArmLeft();
        personBuilder.buildArmRight();
        personBuilder.buildLegLeft();
        personBuilder.buildLegRight();
    }
}

4. 測試

建立一個視窗,將小人畫出來。

 public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            // 建立視窗物件
            JFrame frame = new JFrame();
            frame.setVisible(true);
            frame.setTitle("畫人");
            frame.setSize(250, 300);

            // 設定視窗關閉按鈕的預設操作(點選關閉時退出程序)
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

            // 把視窗位置設定到螢幕的中心
            frame.setLocationRelativeTo(null);
            frame.setContentPane(new JPanel(){
                @Override
                protected void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    PersonThinBuilder thinBuilder = new PersonThinBuilder(g);
                    PersonDirector director = new PersonDirector(thinBuilder);
                    director.drawPerson();


                }
            });
        }
    });
}

結果如下:

瘦小人

定義

文字定義

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

換句話解釋, 允許你建立不同種類的物件, 同時又能避免對建構函式的汙染。當物件有多種型別時, 該模式非常有用。 或者在建立物件時涉及到很多的步驟。

結構圖

引用《大話設計模式》的一個圖

結構圖

抽象類Builder:為建立Product物件而抽象的介面。

繼承類ConcreateBuilder:具體的建造者, 構造和裝配各個部件。

具體產品類Product:我們需要建造的物件。

Director: 用來建立產品的, 其內部有Builder型別的成員變數。

優點

  1. Director 不需要知道 Product 的內部細節, 它只提供需要的資訊給建設者, 由具體的建造者ConcreateBuilder處理從而完成產品的構造。
  2. 建造者模式將複雜的產品建立過程分散到了不同的物件中, 從而實現對產品建立過程更精確的控制, 建立過程更加清晰。
  3. 每個具體的建造者都可以創建出完整的產品物件, 而且是相互獨立的。 因此, 呼叫端可以通過不同的具體建造者就可以得到不同的物件。當有新的產品出現時, 不需要改變原有程式碼, 只需要新增一個建造者即可。

舉例

現在如果我們想建造一個胖小人,有五官的。那我們只需要新增一個PersonFatBuilder類就可以了, 不需要改原有程式碼。

public class PersonFatBuilder extends PersonBuilder {
    public PersonFatBuilder(Graphics graphics) {
        super(graphics);
    }
    @Override
    public void buildHead() {
        graphics.drawOval(50, 20, 30, 30);
        graphics.drawArc(50, 30, 10, 5, 45, 135);
        graphics.drawArc(70, 30, 10, 5, 45, 135);
        graphics.drawArc(60, 35, 10, 5, 200, 135);
    }

    @Override
    public void buildBody() {
        graphics.drawRect(55, 50, 20, 50);
    }

    @Override
    public void buildArmLeft() {
        graphics.drawLine(55, 50, 40, 100);
    }

    @Override
    public void buildArmRight() {
        graphics.drawLine(75, 50, 90, 100);
    }

    @Override
    public void buildLegLeft() {
        graphics.drawLine(55, 100, 45, 150);
    }

    @Override
    public void buildLegRight() {
        graphics.drawLine(75, 100, 85, 150);
    }
}

結果:

胖小人