Luogu3845 [TJOI2007]球賽(Dilworth定理)題解
建造者模式
案例
生活中有一個常見的例子,沒有人買車會只買一個輪胎或者方向盤,大家買的都是一輛包含輪胎和方向盤等多個部件的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用者呢,下面我們用程式來模擬一下這一過程。
1.首先是汽車、輪胎和方向盤三個實體類:
汽車類
/** * 汽車 */ public class Car { // 方向盤 private SteeringWheel steeringWheel; // 輪胎 private Doughnut doughnut; @Override public String toString() { return "汽車{" + "方向盤=" + steeringWheel + ", 輪胎=" + doughnut + '}'; } // 省略getter/setter }
輪胎類:
/** * 輪胎 */ public class Doughnut { // 大小 private String size; // 型別 private String type; public Doughnut(String size, String type) { this.size = size; this.type = type; } @Override public String toString() { return " 輪胎{" + "大小='" + size + '\'' + ", 型別='" + type + '\'' + '}'; } }
方向盤類:
/** * 方向盤 */ public class SteeringWheel { // 大小 private String size; // 型別 private String type; public SteeringWheel(String size, String type) { this.size = size; this.type = type; } @Override public String toString() { return " 方向盤{" + "大小='" + size + '\'' + ", 型別='" + type + '\'' + '}'; } }
2.然後我們直接生成汽車:
public class Main {
public static void main(String[] args) {
// 生成一個轎車
// 製造汽車方向盤
SteeringWheel steeringWheel1 = new SteeringWheel("中號", "三幅");
// 製造輪胎
Doughnut doughnut1 = new Doughnut("中號", "轎車輪胎");
// 製造汽車
Car car1 = new Car();
car1.setSteeringWheel(steeringWheel1);
car1.setDoughnut(doughnut1);
System.out.println(car1);
// 生成一個貨車
SteeringWheel steeringWheel2 = new SteeringWheel("大號", "三幅");
// 製造輪胎
Doughnut doughnut2 = new Doughnut("大號", "貨車輪胎");
// 製造汽車
Car car2 = new Car();
car2.setSteeringWheel(steeringWheel2);
car2.setDoughnut(doughnut2);
System.out.println(car2);
}
}
可以看到生成汽車的過程直接由我們客戶端來完成了,這樣就使得客戶端和各個類都有耦合。同時我們,可以看出生產汽車的過程其實是一個比較流程化的事。而建造輪胎、方向盤等元件有可能是不盡相同的。並且,實際上建造輪胎和方向盤是較為麻煩的。基於此,接下來介紹建造者模式用於解決這一類問題。
模式介紹
建造者模式是設計模式的一種(建立型),將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
角色構成:
- Builder(抽象建造者):它為建立一個產品Product物件的各個部件指定抽象介面,在該介面中一般宣告兩類方法,一類方法是buildPartX(),它們用於建立複雜物件的各個部件;另一類方法是getResult(),它們用於返回複雜物件。Builder既可以是抽象類,也可以是介面。
- ConcreteBuilder(具體建造者):它實現了Builder介面,實現各個部件的具體構造和裝配方法,定義並明確它所建立的複雜物件,也可以提供一個方法返回建立好的複雜產品物件。
- Product(產品角色):它是被構建的複雜物件,包含多個組成部件,具體建造者建立該產品的內部表示並定義它的裝配過程。
- Director(指揮者):指揮者又稱為導演類,它負責安排複雜物件的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其construct()建造方法中呼叫建造者物件的部件構造與裝配方法,完成複雜物件的建造。客戶端一般只需要與指揮者進行互動,在客戶端確定具體建造者的型別,並例項化具體建造者物件(也可以通過配置檔案和反射機制),然後通過指揮者類的建構函式或者Setter方法將該物件傳入指揮者類中。
UML類圖:
特點:
建造者模式是較為複雜的建立型模式,它將客戶端與包含多個組成部分(或部件)的複雜物件的建立過程分離,客戶端無須知道複雜物件的內部組成部分與裝配方式,只需要知道所需建造者的型別即可。它關注如何一步一步建立一個的複雜物件,不同的具體建造者定義了不同的建立過程,且具體建造者相互獨立,增加新的建造者非常方便,無須修改已有程式碼,系統具有較好的擴充套件性。
程式碼改造
1.首先是三個實體類:因為和上面的實體類相同,就不貼程式碼了
2.抽象構造者和兩個具體實現類:
/**
* 抽象構造者
*/
public abstract class CarBuilder {
//建立產品物件
protected Car car = new Car();
public abstract void buildSteeringWheel();
public abstract void buildDoughnut();
//返回產品物件
public Car getCar() {
return car;
}
}
小轎車類:
/**
* 具體構造者:構造小轎車
*/
public class SedanCarBuilder extends CarBuilder {
public void buildSteeringWheel() {
car.setSteeringWheel(new SteeringWheel("中號", "三幅"));
}
public void buildDoughnut() {
car.setDoughnut(new Doughnut("中號", "轎車輪胎"));
}
}
貨車類:
/**
* 具體構造者:構造貨車
*/
public class FreightCarBuilder extends CarBuilder {
public void buildSteeringWheel() {
car.setSteeringWheel(new SteeringWheel("大號", "三幅"));
}
public void buildDoughnut() {
car.setDoughnut(new Doughnut("大號", "貨車輪胎"));
}
}
3.汽車生產指揮者:
/**
* 指揮者負責汽車的安裝過程
*/
public class Director {
private CarBuilder builder;
public Director(CarBuilder builder) {
this.builder = builder;
}
public Car create() {
builder.buildSteeringWheel();
builder.buildDoughnut();
return builder.getCar();
}
}
3.客戶端使用:
/**
* 客戶端通過指揮者來獲取汽車
*/
public class Main {
public static void main(String[] args) {
// 製造汽車
Car car1 = new Director(new SedanCarBuilder()).create();
System.out.println(car1);
// 製造貨車
Car car2 = new Director(new FreightCarBuilder()).create();
System.out.println(car2);
}
}
經過改造後,客戶端只需例項化指揮者類,指揮者類針對抽象建造者程式設計,客戶端根據需要傳入具體的建造者型別,指揮者將指導具體建造者一步一步構造一個完整的產品(逐步呼叫具體建造者的buildX()
方法),相同的構造過程可以建立完全不同的產品。
模式應用
當我們看到Builder
這個單詞的時候,很容易就能聯想到在我們 JDK 中的 StringBuilder
也是 Builder
結尾,其實它的append()
方法就蘊含著建造者模式的思想,下面我們分析一下它的組成結構。
平時我們使用的最多的就是append()
方法了:
public class Main {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("hello");
System.out.println(builder);
}
}
下面是它的 UML 類圖結構:
通過上圖我們可以分析出:
Appendable
介面定義了多個append()
方法(抽象方法),即Appendable
為抽象建造者,定義了抽象方法AbstractStringBuilder
實現了Appendable
介面方法,這裡的AbstractStringBuilder
已經是就建造者,只是不能例項化StringBuilder
即充當了指揮者角色,同時也充當了具體的建造者,建造方法的實現是由AbstractStringBuilder
完成。
總結
1.主要優點:
- 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
- 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件。由於指揮者類針對抽象建造者程式設計,增加新的具體建造者無須修改原有類庫的程式碼,系統擴充套件方便,符合“開閉原則”
- 可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
2.主要缺點:
- 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
- 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和執行成本。
3.適用場景:
- 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員屬性。
- 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。
- 物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
- 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。
參考資料
- 大話設計模式
- 設計模式Java版本-劉偉
- 建造者模式及在原始碼中的實踐
本篇文章github程式碼地址:https://github.com/Phoegel/design-pattern/tree/main/builder
轉載請說明出處,本篇部落格地址:https://www.cnblogs.com/phoegel/p/13922150.html