為了防止被“殺”了祭天,學點設計模式,并總結下還是有必要的。
一:模式理解
- 工廠模式的作用是新建對象。
- 工廠模式的目的是讓新建對象的過程更加優雅,減少對業務代碼的混淆。
- 包含簡單工廠,工廠方法,抽象工廠。
- 以下幾點可以在例子之后再看。
單獨建立用于生成對象的工程類,即為簡單工廠,類似于工具類。在原有代碼上,抽象出一個方法,用于生成對象,即為工廠方法。新建一個完全抽象的工廠接口/類,具體生成的對象由該工廠的實現類/子類決定,即為抽象工廠。
二: 例子
你是一個富二代,擁有有一家汽車公司,公司的主要工作是把汽車賣給客戶,即sellCar。
主要流程包括原料采購,組裝汽車,汽車展示。
為方便模擬,三個步驟分別為:
- 原料采購直接sout原料采購。
- 組裝汽車(assembleCar)生成對應的汽車對象。
- 汽車展示調用生成汽車對象的display方法,確保生產正確。
有一個汽車類,包含屬性為牌子,顏色,還有一個display方法。
// 汽車類
@Data
public class Car {
private String brand;
private String color;
public void display() {
System.out.println("This is a car");
}
}
目前你司主營奔馳和寶馬車,均繼承自父類Car,對應的類分別為:
// 寶馬車實體類
public class BMWCar extends Car{
@Override
public void display(){
System.out.println("This is a BMW car");
}
}
// 奔馳車實體類
public class BenzCar extends Car {
@Override
public void display(){
System.out.println("This is a Benz car");
}
}
兩個類沒什么區別,只是分別重寫了Car類的display方法。
你司的日常就是賣汽車(sellCar),根據不同的訂單,輸入不同的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
可以看出,你司的業務還是挺簡單粗暴,只需判斷輸入生產不同的車即可。
作為富二代的你并不打算對其做出什么改變。
然而,有一天,你發現奧迪車賣的也不錯,準備開始在自己公司賣奧迪車。
此時在業務代碼sellCar中增加一個else判斷條件。
你的公司越做越大,每天都會需要決定加入或者刪除某些車是否生產。
某一次,一個程序員在修改if else的過程中,不小心搞出個bug。由于習慣性復制粘貼,在應該生產QQ車的時候,生產了一輛賓利車。
為此,你“殺”了這位程序員祭天。
有沒有辦法可以在不修改業務代碼的情況下,搞定第二步驟,即組裝汽車邏輯的修改呢?
1. 簡單工廠
將新建對象的邏輯從業務代碼中提取出來。
新建一個簡單汽車工廠類來承載新建汽車對象的功能。
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;
}
}
修改之后,汽車工廠的代碼變為:
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);
}
}
}
輸入和輸出都與之前一樣。
- 嚴格意義上,簡單工廠并不是一種設計模式,更像是一種重構。
- 重構之后,CarCompany的sellCar代碼變得非常清晰,特別是引入方法assembleCar后,一看就可以知道該步驟是在組裝汽車。
- 對于剛看代碼的新手而言,就算不理解assembleCar具體操作,也可以根據該方法名知道該方法的作用。
- 經過程序員的一頓改,你特別高興,自己家又多了一個工廠,又多了個廠長的身份,還高興地freestyle了一番。
2. 方法工廠
工廠的目的是生成一個對象,簡單工廠為此新建了一個類。
此外,也可以在CarCompany類下增加一個新建汽車對象的私有方法。
該方法的作用和簡單工廠一致,不同的是以方法的形式出現,將其稱為方法工廠。
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);
}
}
}
當然,如果只是將方法的實現移動一個位置,那也算不上是設計模式。
好不容易多了個廠長的身份,程序員竟然這么改。你頓時不高興了,回頭就準備掏出自己40米的大刀。
此時,程序員急忙開始解釋。
如果將CarCompany中的assembleCar方法申明為抽象方法,具體實現由子類來決定。
public abstract class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采購");
Car car = assembleCar(carType);
car.display();
}
protected abstract Car assembleCar(String carType);
}
此時,你完全可以開多家公司用于生產不同的車,它們都將繼承自CarCompany。
CarCompany類規定了sellCar方法的步驟,留下組裝汽車的方法讓不同的公司自己去實現。
新建兩個公司,一個賣寶馬,一個賣奔馳,分開之后,業務得到了擴展,兩個工廠都可以生產轎車和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
兩個公司可以完全獨立運行,在需要新建一個公司的時候,只需要增加新的公司類即可,如AudiCarCompany。
聽了程序員這番解釋,你雖然沒聽懂,但覺得可以一下子多搞幾個分公司,內心還是有點小激動的,隨手收回了自己那把40米的大刀。
3. 抽象工廠
由于成本變動,寶馬和奔馳子公司都需要更換組裝汽車的過程,第一時間想到是直接修改兩個公司的assembleCar的代碼。
然而現在寶馬公司又需要生產紀念版的寶馬車,和普通寶馬不同的是,特別版的寶馬使用了特殊的紅色油漆。
面對這樣的需求,雖貴為富二代的你,還是準備討好下程序員,以一起去徹夜鼓掌作為誘惑,希望程序員盡快完成。
在之前的步驟中,已經將組裝汽車的過程抽象成工廠,那么更改/修改汽車組裝的過程,可以抽象成換了一家代工廠。
但無論怎么換代工廠,都要求這些工廠有assembleCar的功能,所以需要一個抽象類或者接口來約束。其中的組裝方法是抽象的,具體實現在子類中定義。
這就是抽象工廠這個名稱的含義。
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方法中,只需直接返回對應工廠組裝的汽車即可,不用關注具體的工藝。
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");
}
}
當某個工廠需要更換代工廠時候,只需新建一個繼承自AbstractCarFactory抽象工廠的具體工廠即可,如AudiCarFactory。
并將此工廠作為carCompany的屬性置入,如果只希望增加代碼而不是修改代碼,可以新建AudiCarCompany即可。
以下例子為寶馬公司更換特別版生產工廠之后,生產寶馬車的過程,在display中同時展示寶馬車的顏色。
可以看到,在更換特別版工廠之后,生產出來的寶馬車加上了特別的紅色。
而作為富二代的你,開上這輛特別版的寶馬,突然發現一下子多了好多一夜成長的機會。
此時,你已經忘了答應給程序員的鼓掌獎勵。/(ㄒ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
三: 再理解
- 簡單工廠只是一個代碼重構的過程,為組裝汽車的過程新建了一個類,一個靜態方法。
- 方法工廠可以分為簡單方法工廠和抽象方法工廠。簡單方法工廠和簡單方法差不多,只是少新建了一個工廠類。抽象方法工廠,將生成對象的方法申明為抽象,在子類中實現,方便建立新的公司,業務拆分。
- 抽象工廠,定義一個什么都不做,只約定做什么的工廠,即將工廠的作用抽象出來。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象,而策略模式的目的主要在于為不同類別的對象執行不同的步驟。