設計模式-創建型模式(二)
建造者模式
定義
將一個複雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示。它是將一個複雜的對象分解爲多個簡單對象,然後一步一步的構建而成。即產品的組成是不變的,但每一部分是可以靈活選擇的。
特點
優點
- 各個具體的建造者相互獨立,有利於系統的擴展
- 客戶端不必知道產品的內部組成細節,便於控制細節風險
缺點
- 產品的組成部分必須相同,這限制了其使用範圍
- 如果產品的內部變化複雜,該模式會增加很多的建造者類
結構
建造者模式的主要角色如下:
- 產品(Product):它是一個複雜對象(由多個部件組成),由具體建造者來創建其各個部件。
- 抽象建造者(Builder):定義了創建產品各個子部件的抽象方法的接口,通常還包含一個返回複雜產品的方法build()。
- 具體建造者(Concrete Builder):實現了抽象建造者定義的接口,完成對複雜產品各個部件的具體創建方法。
- 指揮者(Director):它調用建造者對象中的部件構造與裝配方法完成複雜對象的創建,在指揮者中不涉及具體產品的信息。
實現
案列:汽車生產是一個複雜過程,包含發動機、車門、方向盤等,這裏通過建造者模式實現對複雜的汽車部件進行生產組裝。
/**
*1、定義汽車產品,(包含多個部件,由具體建造者來創建各個部件)
*/
public class Car {
//車輪
String tyre;
//發動機
String engine;
//車門
String door;
//方向盤(如果沒有則是自動駕駛汽車)
String wheel;
public void setTyre(String tyre) {this.tyre = tyre;}
public void setEngine(String engine) {this.engine = engine;}
public void setDoor(String door) {this.door = door;}
public void setWheel(String wheel) {this.wheel = wheel;}
public String getTyre() {return tyre;}
public String getEngine() {return engine;}
public String getDoor() {return door;}
public String getWheel() {return wheel;}
public void show() {
String tyreStr = isNull(getTyre()) ? "無車輪" : "有車輪";
String engineStr = isNull(getEngine()) ? "新能源" : "非新能源";
String doorStr = isNull(getDoor()) ? "敞篷" : "非敞篷";
String wheelStr = isNull(getWheel()) ? "自動駕駛" : "非自動駕駛";
System.out.println("建造一輛" + tyreStr + engineStr + doorStr + wheelStr + "汽車");
}
}
/**
*2、定義抽象建造者類或接口(提供創建複雜對象部件的抽象方法,以及返回複雜產品的實例)
*/
public interface ICarBuilder {
//創建輪胎
void setTyre();
//創建車門
void setDoor();
//創建發動機
void setengine();
//創建方向盤
void setWheel();
//返回一個複雜對象
Car build();
}
/**
*3、定義具體建造者,實現抽象建造者定義的抽象方法,完成對複雜對象部件的具體創建。
*/
//公共汽車建造者
public class BusBuilder implements ICarBuilder {
@Override
public void setTyre() {}
@Override
public void setDoor() {}
@Override
public void setengine() {}
@Override
public void setWheel() {}
@Override
public Car build() {
System.out.print("公共汽車具體建造者——>");
Car car = new Car();
car.setTyre("有");
car.setDoor("有");
car.setEngine("有");
car.setWheel("有");
return car;
}
}
//出租車建造者
public class TaxiBuilder implements ICarBuilder {
@Override
public void setTyre() {}
@Override
public void setDoor() {}
@Override
public void setengine() {}
@Override
public void setWheel() {}
@Override
public Car build() {
System.out.print("出租車具體建造者——>");
Car car = new Car();
return car;
}
}
/**
*4、定義指揮者(調用具體建造者中部件構建與裝配方法完成複雜對象創建)
* 指揮者中不涉及具體產品信息
*/
public class Director {
ICarBuilder iCarBuilder;
public Director(ICarBuilder iCarBuilder) {
this.iCarBuilder = iCarBuilder;
}
public Car createCar() {
return iCarBuilder.build();
}
}
//測試
public class Test {
public static void main(String[] args) {
ICarBuilder busBuilder = new BusBuilder();
Director director = new Director(busBuilder);
director.createCar().show();
ICarBuilder taxiBuilder = new TaxiBuilder();
Director director2 = new Director(taxiBuilder);
director2.createCar().show();
}
}
//輸出
公共汽車具體建造者——>建造一輛有車輪非新能源非敞篷非自動駕駛汽車
出租車具體建造者——>建造一輛無車輪新能源敞篷自動駕駛汽車
應用場景
建造者(Builder)模式創建的對象是負責對象,某產品的各個部分經常面臨劇烈變化,但將它們組合在一起的算法相對穩定,所以通常在如下場景使用:
- 創建的對象比較複雜,由多個部件構成,各部件面臨複雜變化,但構建順序是穩定的
- 創建複雜對象短髮獨立於該對象的組成部分以及它們的裝配方式,即產品的構建過車和最終的表示是獨立的。
擴展
建造者模式在應用過程中可以根據需要改變,如果創建的產品種類只有一種,只需要一個具體建造者,這時可以省略掉抽象建造者,甚至可以省略掉指揮者角色
/**
*創建產品(通過靜態內部類實現建造者模式)
*/
public class ExtCar {
//車輪
String tyre;
//發動機
String engine;
//車門
String door;
//方向盤(如果沒有則是自動駕駛汽車)
String wheel;
public void setTyre(String tyre) {this.tyre = tyre;}
public void setEngine(String engine) {this.engine = engine;}
public void setDoor(String door) {this.door = door;}
public void setWheel(String wheel) {this.wheel = wheel;}
public String getTyre() {return tyre;}
public String getEngine() {return engine;}
public String getDoor() {return door;}
public String getWheel() {return wheel;}
public void show() {
String tyreStr = isNull(getTyre()) ? "無車輪" : "有車輪";
String engineStr = isNull(getEngine()) ? "新能源" : "非新能源";
String doorStr = isNull(getDoor()) ? "敞篷" : "非敞篷";
String wheelStr = isNull(getWheel() ) ? "自動駕駛" : "非自動駕駛";
System.out.println("建造一輛" + tyreStr + engineStr + doorStr + wheelStr + "汽車");
}
//靜態內部類實現建造者模式
public static class Builder {
//車輪
String tyre;
//發動機
String engine;
//車門
String door;
//方向盤(如果沒有則是自動駕駛汽車)
String wheel;
public Builder setTyre(String tyre) {
this.tyre = tyre;
return this;
}
public Builder setEngine(String engine) {
this.engine = engine;
return this;
}
public Builder setDoor(String door) {
this.door = door;
return this;
}
public Builder setWheel(String wheel) {
this.wheel = wheel;
return this;
}
public ExtCar build() {
ExtCar extCar = new ExtCar();
extCar.setTyre(tyre);
extCar.setEngine(engine);
extCar.setDoor(door);
extCar.setWheel(wheel);
return extCar;
}
}
}
//測試
public class Test {
public static void main(String[] args) {
//選擇性構建部件
ExtCar car1 = new ExtCar.Builder()
.setTyre("有")
.setEngine("有")
.build();
car1.show();
//選擇性構建部件,與car1構建過程一致,但最終表示不同
ExtCar car2 = new ExtCar.Builder()
.setDoor("有")
.setWheel("有")
.build();
car2.show();
}
}
//輸出
建造一輛有車輪非新能源敞篷自動駕駛汽車
建造一輛無車輪新能源非敞篷非自動駕駛汽車
原型模式
定義
用一個已經創建的實例作爲原型,通過複製該原型來創建一個和原型相同或相似的對象。在這裏,原型實例指定了要創建的對象種類,用這種方式創建對象非常高效,根本無需知道對象的創建細節。
結構
原型模式包含一下角色
- 抽象原型類:規定了具體原型對象必須要實現的接口。
- 具體原型類:實現了抽象原型類的clone()方法,它是可以被複制的對象。
- 訪問類:使用具體原型類中的clone()方法來複制新的對象的類。
實現
原型模式的克隆分爲淺克隆
和深克隆
,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實現 Cloneable 接口就可實現對象的淺克隆,這裏的 Cloneable 接口就是抽象原型類。
用原型模式除了可以生成相同的對象,還可以生成相似(部分屬性不同)對象。
案例:同一三好學生獎狀
除了人名不同,其他都相同,屬於相似對象,可以通過原型模式實現,然後再做簡單修改即可
/**
*1、具體原型類(實現抽象原型類Cloneable接口的clone()方法)-獎狀
*/
public class Citation implements Cloneable {
String name;
String info;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getInfo() {return info;}
public void setInfo(String info) {this.info = info;}
public Citation(String name, String info) {
this.name = name;
this.info = info;
}
public void show() {System.out.println(getName() + getInfo());}
//重寫抽象原型類中的cone()方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//訪問類
public class Test {
public static void main(String[] args) {
Citation zhangsan = new Citation("張三", "同學,在2019年第一學年度被評爲三好學生,"+
"特發此狀以此鼓勵!");
zhangsan.show();
try {
//使用原型模式復創建新對象
Citation lisi = (Citation) zhangsan.clone();
//修改部分屬性
lisi.setName("李四");
lisi.show();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
//輸出
張三同學,在2019年第一學年度被評爲三好學生,特發此狀以此鼓勵!
李四同學,在2019年第一學年度被評爲三好學生,特發此狀以此鼓勵!
應用場景
原型模式適用於以下場景
- 對象之間相同或相似,即只是個別的幾個屬性不同的時候。
- 對象的創建過程比較麻煩,但是複製比較簡單的時候。