為了防止被“殺”了祭天弦聂,學(xué)點設(shè)計模式鸟辅,并總結(jié)下還是有必要的。
一:模式理解
- 工廠模式的作用是新建對象横浑。
- 工廠模式的目的是讓新建對象的過程更加優(yōu)雅剔桨,減少對業(yè)務(wù)代碼的混淆。
- 包含簡單工廠徙融,工廠方法,抽象工廠瑰谜。
- 以下幾點可以在例子之后再看欺冀。
單獨建立用于生成對象的工程類树绩,即為簡單工廠,類似于工具類隐轩。在原有代碼上饺饭,抽象出一個方法,用于生成對象职车,即為工廠方法瘫俊。新建一個完全抽象的工廠接口/類,具體生成的對象由該工廠的實現(xiàn)類/子類決定悴灵,即為抽象工廠扛芽。
二: 例子
你是一個富二代,擁有有一家汽車公司积瞒,公司的主要工作是把汽車賣給客戶川尖,即sellCar。
主要流程包括原料采購茫孔,組裝汽車叮喳,汽車展示。
為方便模擬缰贝,三個步驟分別為:
- 原料采購直接sout原料采購馍悟。
- 組裝汽車(assembleCar)生成對應(yīng)的汽車對象。
- 汽車展示調(diào)用生成汽車對象的display方法剩晴,確保生產(chǎn)正確锣咒。
有一個汽車類,包含屬性為牌子李破,顏色宠哄,還有一個display方法。
// 汽車類
@Data
public class Car {
private String brand;
private String color;
public void display() {
System.out.println("This is a car");
}
}
目前你司主營奔馳和寶馬車嗤攻,均繼承自父類Car毛嫉,對應(yīng)的類分別為:
// 寶馬車實體類
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");
}
}
兩個類沒什么區(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)備開始在自己公司賣奧迪車。
此時在業(yè)務(wù)代碼sellCar中增加一個else判斷條件尚胞。
你的公司越做越大硬霍,每天都會需要決定加入或者刪除某些車是否生產(chǎn)。
某一次笼裳,一個程序員在修改if else的過程中唯卖,不小心搞出個bug。由于習(xí)慣性復(fù)制粘貼躬柬,在應(yīng)該生產(chǎn)QQ車的時候拜轨,生產(chǎn)了一輛賓利車。
為此楔脯,你“殺”了這位程序員祭天撩轰。
有沒有辦法可以在不修改業(yè)務(wù)代碼的情況下,搞定第二步驟昧廷,即組裝汽車邏輯的修改呢堪嫂?
1. 簡單工廠
將新建對象的邏輯從業(yè)務(wù)代碼中提取出來。
新建一個簡單汽車工廠類來承載新建汽車對象的功能木柬。
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è)計模式恶复,更像是一種重構(gòu)。
- 重構(gòu)之后速挑,CarCompany的sellCar代碼變得非常清晰谤牡,特別是引入方法assembleCar后,一看就可以知道該步驟是在組裝汽車姥宝。
- 對于剛看代碼的新手而言翅萤,就算不理解assembleCar具體操作,也可以根據(jù)該方法名知道該方法的作用腊满。
- 經(jīng)過程序員的一頓改套么,你特別高興,自己家又多了一個工廠碳蛋,又多了個廠長的身份胚泌,還高興地freestyle了一番。
2. 方法工廠
工廠的目的是生成一個對象肃弟,簡單工廠為此新建了一個類玷室。
此外零蓉,也可以在CarCompany類下增加一個新建汽車對象的私有方法。
該方法的作用和簡單工廠一致阵苇,不同的是以方法的形式出現(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)然绅项,如果只是將方法的實現(xiàn)移動一個位置,那也算不上是設(shè)計模式比肄。
好不容易多了個廠長的身份快耿,程序員竟然這么改。你頓時不高興了芳绩,回頭就準(zhǔn)備掏出自己40米的大刀掀亥。
此時,程序員急忙開始解釋妥色。
如果將CarCompany中的assembleCar方法申明為抽象方法搪花,具體實現(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);
}
此時嘹害,你完全可以開多家公司用于生產(chǎn)不同的車撮竿,它們都將繼承自CarCompany。
CarCompany類規(guī)定了sellCar方法的步驟笔呀,留下組裝汽車的方法讓不同的公司自己去實現(xiàn)幢踏。
新建兩個公司,一個賣寶馬许师,一個賣奔馳房蝉,分開之后,業(yè)務(wù)得到了擴展微渠,兩個工廠都可以生產(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
兩個公司可以完全獨立運行,在需要新建一個公司的時候逞盆,只需要增加新的公司類即可檀蹋,如AudiCarCompany。
聽了程序員這番解釋纳击,你雖然沒聽懂续扔,但覺得可以一下子多搞幾個分公司,內(nèi)心還是有點小激動的焕数,隨手收回了自己那把40米的大刀纱昧。
3. 抽象工廠
由于成本變動,寶馬和奔馳子公司都需要更換組裝汽車的過程堡赔,第一時間想到是直接修改兩個公司的assembleCar的代碼识脆。
然而現(xiàn)在寶馬公司又需要生產(chǎn)紀(jì)念版的寶馬車,和普通寶馬不同的是,特別版的寶馬使用了特殊的紅色油漆灼捂。
面對這樣的需求离例,雖貴為富二代的你,還是準(zhǔn)備討好下程序員悉稠,以一起去徹夜鼓掌作為誘惑宫蛆,希望程序員盡快完成。
在之前的步驟中的猛,已經(jīng)將組裝汽車的過程抽象成工廠耀盗,那么更改/修改汽車組裝的過程,可以抽象成換了一家代工廠卦尊。
但無論怎么換代工廠叛拷,都要求這些工廠有assembleCar的功能,所以需要一個抽象類或者接口來約束岂却。其中的組裝方法是抽象的忿薇,具體實現(xiàn)在子類中定義。
這就是抽象工廠這個名稱的含義躏哩。
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)某個工廠需要更換代工廠時候器联,只需新建一個繼承自AbstractCarFactory抽象工廠的具體工廠即可二汛,如AudiCarFactory。
并將此工廠作為carCompany的屬性置入拨拓,如果只希望增加代碼而不是修改代碼肴颊,可以新建AudiCarCompany即可。
以下例子為寶馬公司更換特別版生產(chǎn)工廠之后渣磷,生產(chǎn)寶馬車的過程婿着,在display中同時展示寶馬車的顏色。
可以看到醋界,在更換特別版工廠之后竟宋,生產(chǎn)出來的寶馬車加上了特別的紅色。
而作為富二代的你形纺,開上這輛特別版的寶馬丘侠,突然發(fā)現(xiàn)一下子多了好多一夜成長的機會。
此時逐样,你已經(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òu)的過程打肝,為組裝汽車的過程新建了一個類,一個靜態(tài)方法挪捕。
- 方法工廠可以分為簡單方法工廠和抽象方法工廠粗梭。簡單方法工廠和簡單方法差不多,只是少新建了一個工廠類级零。抽象方法工廠断医,將生成對象的方法申明為抽象,在子類中實現(xiàn)妄讯,方便建立新的公司孩锡,業(yè)務(wù)拆分。
- 抽象工廠亥贸,定義一個什么都不做,只約定做什么的工廠浇垦,即將工廠的作用抽象出來炕置。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象男韧,而策略模式的目的主要在于為不同類別的對象執(zhí)行不同的步驟朴摊。