@
最近在看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
類型的成員變量。
優點
Director
不需要知道Product
的內部細節, 它只提供需要的信息給建設者, 由具體的建造者ConcreateBuilder
處理從而完成產品的構造。- 建造者模式將復雜的產品創建過程分散到了不同的對象中, 從而實現對產品創建過程更精確的控制, 創建過程更加清晰。
- 每個具體的建造者都可以創建出完整的產品對象, 而且是相互獨立的。 因此, 調用端可以通過不同的具體建造者就可以得到不同的對象。當有新的產品出現時, 不需要改變原有代碼, 只需要添加一個建造者即可。
舉例
現在如果我們想建造一個胖小人,有五官的。那我們只需要添加一個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);
}
}
結果: