
為了防止被“殺”了祭天,學(xué)點(diǎn)設(shè)計(jì)模式,并總結(jié)下還是有必要的。
一:模式理解
- 工廠模式的作用是新建對象。
- 工廠模式的目的是讓新建對象的過程更加優(yōu)雅,減少對業(yè)務(wù)代碼的混淆。
- 包含簡單工廠,工廠方法,抽象工廠。
- 以下幾點(diǎn)可以在例子之后再看。
單獨(dú)建立用于生成對象的工程類,即為簡單工廠,類似于工具類。在原有代碼上,抽象出一個(gè)方法,用于生成對象,即為工廠方法。新建一個(gè)完全抽象的工廠接口/類,具體生成的對象由該工廠的實(shí)現(xiàn)類/子類決定,即為抽象工廠。
二: 例子
你是一個(gè)富二代,擁有有一家汽車公司,公司的主要工作是把汽車賣給客戶,即sellCar。
主要流程包括原料采購,組裝汽車,汽車展示。
為方便模擬,三個(gè)步驟分別為:
- 原料采購直接sout原料采購。
- 組裝汽車(assembleCar)生成對應(yīng)的汽車對象。
- 汽車展示調(diào)用生成汽車對象的display方法,確保生產(chǎn)正確。

有一個(gè)汽車類,包含屬性為牌子,顏色,還有一個(gè)display方法。
// 汽車類
@Data
public class Car {
private String brand;
private String color;
public void display() {
System.out.println("This is a car");
}
}
目前你司主營奔馳和寶馬車,均繼承自父類Car,對應(yīng)的類分別為:
// 寶馬車實(shí)體類
public class BMWCar extends Car{
@Override
public void display(){
System.out.println("This is a BMW car");
}
}
// 奔馳車實(shí)體類
public class BenzCar extends Car {
@Override
public void display(){
System.out.println("This is a Benz car");
}
}
兩個(gè)類沒什么區(qū)別,只是分別重寫了Car類的display方法。
你司的日常就是賣汽車(sellCar),根據(jù)不同的訂單,輸入不同的carType,新建不同的汽車對象。
public class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采購");
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
car.display();
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
輸入/輸出:
BMW
原料采購
This is a BMW car
Benz
原料采購
This is a Benz car
car
原料采購
This is a car
可以看出,你司的業(yè)務(wù)還是挺簡單粗暴,只需判斷輸入生產(chǎn)不同的車即可。
作為富二代的你并不打算對其做出什么改變。
然而,有一天,你發(fā)現(xiàn)奧迪車賣的也不錯,準(zhǔn)備開始在自己公司賣奧迪車。
此時(shí)在業(yè)務(wù)代碼sellCar中增加一個(gè)else判斷條件。
你的公司越做越大,每天都會需要決定加入或者刪除某些車是否生產(chǎn)。
某一次,一個(gè)程序員在修改if else的過程中,不小心搞出個(gè)bug。由于習(xí)慣性復(fù)制粘貼,在應(yīng)該生產(chǎn)QQ車的時(shí)候,生產(chǎn)了一輛賓利車。
為此,你“殺”了這位程序員祭天。
有沒有辦法可以在不修改業(yè)務(wù)代碼的情況下,搞定第二步驟,即組裝汽車邏輯的修改呢?
1. 簡單工廠
將新建對象的邏輯從業(yè)務(wù)代碼中提取出來。
新建一個(gè)簡單汽車工廠類來承載新建汽車對象的功能。
public class SimpleCarFactory {
public static Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
return car;
}
}
修改之后,汽車工廠的代碼變?yōu)椋?/p>
public class CarCompany {
public void sellCar(String carType) {
// 原料采購
System.out.println("原料采購");
// 組裝汽車
Car car = SimpleCarFactory.assembleCar(carType);
// 展示汽車
car.display();
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
輸入和輸出都與之前一樣。
- 嚴(yán)格意義上,簡單工廠并不是一種設(shè)計(jì)模式,更像是一種重構(gòu)。
- 重構(gòu)之后,CarCompany的sellCar代碼變得非常清晰,特別是引入方法assembleCar后,一看就可以知道該步驟是在組裝汽車。
- 對于剛看代碼的新手而言,就算不理解assembleCar具體操作,也可以根據(jù)該方法名知道該方法的作用。
- 經(jīng)過程序員的一頓改,你特別高興,自己家又多了一個(gè)工廠,又多了個(gè)廠長的身份,還高興地freestyle了一番。
2. 方法工廠
工廠的目的是生成一個(gè)對象,簡單工廠為此新建了一個(gè)類。
此外,也可以在CarCompany類下增加一個(gè)新建汽車對象的私有方法。
該方法的作用和簡單工廠一致,不同的是以方法的形式出現(xiàn),將其稱為方法工廠。
public class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采購");
Car car = assembleCar(carType);
car.display();
}
private Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
return car;
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
當(dāng)然,如果只是將方法的實(shí)現(xiàn)移動一個(gè)位置,那也算不上是設(shè)計(jì)模式。
好不容易多了個(gè)廠長的身份,程序員竟然這么改。你頓時(shí)不高興了,回頭就準(zhǔn)備掏出自己40米的大刀。
此時(shí),程序員急忙開始解釋。
如果將CarCompany中的assembleCar方法申明為抽象方法,具體實(shí)現(xiàn)由子類來決定。
public abstract class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采購");
Car car = assembleCar(carType);
car.display();
}
protected abstract Car assembleCar(String carType);
}
此時(shí),你完全可以開多家公司用于生產(chǎn)不同的車,它們都將繼承自CarCompany。
CarCompany類規(guī)定了sellCar方法的步驟,留下組裝汽車的方法讓不同的公司自己去實(shí)現(xiàn)。
新建兩個(gè)公司,一個(gè)賣寶馬,一個(gè)賣奔馳,分開之后,業(yè)務(wù)得到了擴(kuò)展,兩個(gè)工廠都可以生產(chǎn)轎車和SUV。
public class BMVCarCompany extends CarCompany {
@Override
protected Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
public class BenzCarCompany extends CarCompany {
@Override
protected Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BenzCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
public class Client {
public static void main(String[] args) {
CarCompany bmwCarCompany = new BMVCarCompany();
CarCompany benzCarCompany = new BenzCarCompany();
bmwCarCompany.sellCar("car");
benzCarCompany.sellCar("car");
bmwCarCompany.sellCar("suv");
benzCarCompany.sellCar("suv");
}
}
輸入/輸出:
原料采購
This is a BMW car
原料采購
This is a Benz car
原料采購
This is a BMW SUV car
原料采購
This is a BMW SUV car
兩個(gè)公司可以完全獨(dú)立運(yùn)行,在需要新建一個(gè)公司的時(shí)候,只需要增加新的公司類即可,如AudiCarCompany。
聽了程序員這番解釋,你雖然沒聽懂,但覺得可以一下子多搞幾個(gè)分公司,內(nèi)心還是有點(diǎn)小激動的,隨手收回了自己那把40米的大刀。
3. 抽象工廠
由于成本變動,寶馬和奔馳子公司都需要更換組裝汽車的過程,第一時(shí)間想到是直接修改兩個(gè)公司的assembleCar的代碼。
然而現(xiàn)在寶馬公司又需要生產(chǎn)紀(jì)念版的寶馬車,和普通寶馬不同的是,特別版的寶馬使用了特殊的紅色油漆。
面對這樣的需求,雖貴為富二代的你,還是準(zhǔn)備討好下程序員,以一起去徹夜鼓掌作為誘惑,希望程序員盡快完成。
在之前的步驟中,已經(jīng)將組裝汽車的過程抽象成工廠,那么更改/修改汽車組裝的過程,可以抽象成換了一家代工廠。
但無論怎么換代工廠,都要求這些工廠有assembleCar的功能,所以需要一個(gè)抽象類或者接口來約束。其中的組裝方法是抽象的,具體實(shí)現(xiàn)在子類中定義。
這就是抽象工廠這個(gè)名稱的含義。
public abstract class AbstractCarFactory {
public abstract Car assembleCar(String carType);
}
分別新建兩家代工廠。
// 寶馬車代工廠
public class BMWCarFactory extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
// 奔馳車代工廠
public class BenzCarFactory extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BenzCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
兩家公司分別持有AbstractCarFactory對象,在assembleCar方法中,只需直接返回對應(yīng)工廠組裝的汽車即可,不用關(guān)注具體的工藝。
public class BMVCarCompany extends CarCompany {
private AbstractCarFactory carFactory;
public BMVCarCompany(AbstractCarFactory carFactory) {
this.carFactory = carFactory;
}
@Override
protected Car assembleCar(String carType) {
return carFactory.assembleCar(carType);
}
}
public class BenzCarCompany extends CarCompany {
private AbstractCarFactory carFactory;
public BenzCarCompany(AbstractCarFactory carFactory) {
this.carFactory = carFactory;
}
@Override
protected Car assembleCar(String carType) {
return carFactory.assembleCar(carType);
}
}
public class Client {
public static void main(String[] args) {
AbstractCarFactory bmwCarFactory = new BMWCarFactory();
CarCompany bmwCarCompany = new BMVCarCompany(bmwCarFactory);
AbstractCarFactory benzCarFactory = new BenzCarFactory();
CarCompany benzCarCompany = new BenzCarCompany(benzCarFactory);
bmwCarCompany.sellCar("car");
benzCarCompany.sellCar("car");
bmwCarCompany.sellCar("suv");
benzCarCompany.sellCar("suv");
}
}
當(dāng)某個(gè)工廠需要更換代工廠時(shí)候,只需新建一個(gè)繼承自AbstractCarFactory抽象工廠的具體工廠即可,如AudiCarFactory。
并將此工廠作為carCompany的屬性置入,如果只希望增加代碼而不是修改代碼,可以新建AudiCarCompany即可。
以下例子為寶馬公司更換特別版生產(chǎn)工廠之后,生產(chǎn)寶馬車的過程,在display中同時(shí)展示寶馬車的顏色。
可以看到,在更換特別版工廠之后,生產(chǎn)出來的寶馬車加上了特別的紅色。
而作為富二代的你,開上這輛特別版的寶馬,突然發(fā)現(xiàn)一下子多了好多一夜成長的機(jī)會。
此時(shí),你已經(jīng)忘了答應(yīng)給程序員的鼓掌獎勵。/(ㄒoㄒ)/~~
public class BMWCarFactoryForSpecialEdition extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
car.setColor("Red for special edition");
return car;
}
}
public class BMWCar extends Car {
@Override
public void display() {
System.out.println("This is a BMW car with " + getColor());
}
}
輸入/輸出:
原料采購
This is a BMW car with Red for special edition
三: 再理解
- 簡單工廠只是一個(gè)代碼重構(gòu)的過程,為組裝汽車的過程新建了一個(gè)類,一個(gè)靜態(tài)方法。
- 方法工廠可以分為簡單方法工廠和抽象方法工廠。簡單方法工廠和簡單方法差不多,只是少新建了一個(gè)工廠類。抽象方法工廠,將生成對象的方法申明為抽象,在子類中實(shí)現(xiàn),方便建立新的公司,業(yè)務(wù)拆分。
- 抽象工廠,定義一個(gè)什么都不做,只約定做什么的工廠,即將工廠的作用抽象出來。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象,而策略模式的目的主要在于為不同類別的對象執(zhí)行不同的步驟。