一、基本介紹
建造者模式(Build Pattern)又稱生成器模式,該模式封裝一個產品的構造過程,一步一步地創建出一個複雜的對象,它允許用戶通過指定複雜對象的類型和具體內容來構建對象,不需要知道內部的構建細節。
下面是建造者的原理UML類圖:
建造者模式的四個角色說明:
- Product(產品對象):一個具體的產品對象
- Builder(抽象建造者):用於創建一個產品對象的各個部件指定的接口/抽象類
- ConcreteBuilder(具體建造者):實現接口,構建和裝配產品的各個部件。
- Director(指揮者):用於創建一個複雜的對象,它主要有兩個作用:隔離客戶與對象的生產過程和負責控制產品對象的生產過程。指揮者針對抽象建造者編程,客戶端只需要知道具體建造者的類型,即可通過指揮者類調用建造者的相關方法,返回一個完整的產品對象。
二、使用
下面應用建造者模式來解決一個實際案例
1.場景
假設你在廣州做一個導遊,現在你需要爲遊客制定多個“羊城一日遊”的行程安排的計劃,其中每個計劃去的景點可能不太一樣,另外如果是外地的遊客則需要提供旅館住宿。
所以需要一個彈性的數據結構,代表客人的規劃以及所有的變化。要創建這樣的計劃,就需要使用建造者模式來實現創建複雜的結構的同時,又不會和創建它的步驟混在一起。
2.UML類圖設計
其中PlanA不包含住宿,PlanB包含住宿
3.代碼實現
Plan類,擔當產品對象的角色
public class Plan {
public String morningPlan;
public String afternoonPlan;
public String nightPlan;
public String sleepPlan;
//省略getter和setter方法
//省略toString方法
Director類,擔當指揮者的角色
public class Director {
AbstractBuilder planBuilder;
public Director(AbstractBuilder planBuilder) {
// TODO Auto-generated constructor stub
this.planBuilder = planBuilder;
}
public void setPlanBuilder(AbstractBuilder planBuilder) {
this.planBuilder = planBuilder;
}
public Plan constructPlan() {
System.out.println("計劃制定:");
planBuilder.arrangeMorning();
planBuilder.arrangeAfternoon();
planBuilder.arrangeNight();
planBuilder.arrangeSleep();
return planBuilder.getPlan();
}
}
AbstractPlanBuilder類,擔當抽象建造者的角色
public abstract class AbstractBuilder {
Plan plan = new Plan();
public abstract void arrangeMorning();
public abstract void arrangeAfternoon();
public abstract void arrangeNight();
public abstract void arrangeSleep();
public Plan getPlan() {
return plan;
}
}
PlanABuilder,PlanBBuilder都擔當具體建造者的角色
public class PlanABuilder extends AbstractBuilder {
@Override
public void arrangeMorning() {
// TODO Auto-generated method stub
plan.setMorningPlan("出發時間:8:00,目的地:白雲山");
System.out.println("已制定好早上的計劃");
}
@Override
public void arrangeAfternoon() {
// TODO Auto-generated method stub
plan.setAfternoonPlan("出發時間:12:00,目的地:長隆歡樂世界");
System.out.println("已制定好下午的計劃");
}
@Override
public void arrangeNight() {
// TODO Auto-generated method stub
plan.setNightPlan("出發時間: 20:00 ,目的地:廣州塔");
System.out.println("已制定好晚上的計劃");
}
@Override
public void arrangeSleep() {
System.out.println("該計劃不安排住宿");
}
}
public class PlanBBuilder extends AbstractBuilder {
@Override
public void arrangeMorning() {
// TODO Auto-generated method stub
plan.setMorningPlan("出發時間:8:15,目的地:大夫山");
System.out.println("已制定好早上的計劃");
}
@Override
public void arrangeAfternoon() {
// TODO Auto-generated method stub
plan.setAfternoonPlan("出發時間:11:30,目的地:長隆歡樂世界");
System.out.println("已制定好下午的計劃");
}
@Override
public void arrangeNight() {
// TODO Auto-generated method stub
plan.setNightPlan("出發時間: 20:00 ,目的地:廣州塔");
System.out.println("已制定好晚上的計劃");
}
@Override
public void arrangeSleep() {
System.out.println("晚上住宿在白天鵝賓館");
}
}
Client類作爲客戶類,只需調用Director的consructPlan即可完成計劃對象的創建
public class Client {
public static void main(String[] args) {
Director director = new Director(new PlanABuilder());
Plan p1 = director.constructPlan();
System.out.println("計劃詳情:" + p1);
director.setPlanBuilder(new PlanBBuilder());
Plan p2 = director.constructPlan();
System.out.println("計劃詳情:" + p2);
}
}
程序運行結果:
三、典型應用
java.lang.StringBuilder中的建造者模式
StringBuilder類的繼承和實現關係,如圖:
Appendable接口定義了三個append方法,其擔當抽象建造者的角色,定義了抽象方法。
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
AbstractStringBuilder實現了Appendable接口的三個方法,AbstractStringBuilder類擔當了具體建造者的角色,當由於其是抽象類,所以不能實例化。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
//省略
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
//省略
}
StringBuilder即擔當了指揮者角色,同時擔當了具體的建造者的角色,還是產品對象。其建造方法是由其父類AbstractStringBuilder來實現,下面三個重載的append方法都是建造方法,只不過比較簡單,只有一個方法調用。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
//省略
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
@Override
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
@Override
public StringBuilder append(char c) {
super.append(c);
return this;
}
}
//省略
四、總結
- 建造者模式的優點:
- 在建造者模式中,向客戶端隱藏產品內部的表現,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
- 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者, 用戶使用不同的具體建造者即可得到不同的產品對象 。
- 可以更加精細地控制產品的創建過程 。將複雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。
- 增加新的具體建造者無須修改原有類庫的代碼,指揮者類針對抽象建造者類編程,系統擴展方便,符合“開閉原則”。
- 建造者模式的缺點:
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。
- 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。
- 建造者模式與抽象工廠模式的比較:
與抽象工廠模式相比, 建造者模式返回一個組裝好的完整產品 ,而抽象工廠模式返回一系列相關的產品,這些產品位於不同的產品等級結構,構成了一個產品族。
在抽象工廠模式中,客戶端實例化工廠類,然後調用工廠方法獲取所需產品對象,而在建造者模式中,客戶端可以不直接調用建造者的相關方法,而是通過指揮者類來指導如何生成對象,包括對象的組裝過程和建造步驟,它側重於一步步構造一個複雜對象,返回一個完整的對象。
如果將抽象工廠模式看成 汽車配件生產工廠 ,生產一個產品族的產品,那麼建造者模式就是一個 汽車組裝工廠 ,通過對部件的組裝可以返回一輛完整的汽車。
參考:
1.4.建造者模式
2.設計模式 | 建造者模式及典型應用