我們在第一次學(xué)習(xí)對象這個概念的時候,新建對象用的是這樣的代碼:
Product product = new Product();
但是真正到了工程上這樣的代碼是“生硬”的,當(dāng)遇到 Product 改變,或者 Product 初始化流程更改這樣的新需求時,要改變的代碼量相當(dāng)?shù)拇蟆N覀冃枰谡页鏊姓{(diào)用 Product 構(gòu)造器的地方,并為之添加上新的代碼,這樣的代碼設(shè)計顯然是不符合設(shè)計原則的。
為了改善上面的情況,工廠模式應(yīng)運(yùn)而生。
工廠模式是一種創(chuàng)建模式。創(chuàng)建模式對類的實例化過程進(jìn)行了抽象,能將類的創(chuàng)建和使用分離。工廠類將產(chǎn)品類的初始化封裝在方法之中,對于外界而言,只要知道產(chǎn)品類的公共接口,就可以正常實現(xiàn)功能,這使得各個模塊開發(fā)時只專注于自己的業(yè)務(wù)邏輯,忽略對象的創(chuàng)建細(xì)節(jié)。
由于場景不同,我們可以看到不同形態(tài)的工廠模式,一般可以分為以下幾種:
- 簡單工廠模式
- 工廠方法模式
- 抽象工廠模式
三種模式間并沒有絕對的優(yōu)劣,因為各個模式各有優(yōu)缺,需要根據(jù)特定的場景來選擇。但是大多數(shù)開發(fā)場景中,我們并沒有這樣龐大的系統(tǒng),因此,通常我們使用的還是前兩種方法。
一、簡單工廠模式
簡單工廠模式是一種類創(chuàng)建模式,通常情況下,簡單工廠的對象會有一個靜態(tài)方法用于創(chuàng)建產(chǎn)品,并且這個方法通常根據(jù)傳入的參數(shù)來生產(chǎn)指定的產(chǎn)品。這種工廠模式即使你不知道有設(shè)計模式這一說法,也會自行使用,因為它構(gòu)造簡單,實現(xiàn)方便,也確實能在項目中實現(xiàn)立竿見影的效果。
1)簡單工廠模型
先來看看最簡單的實現(xiàn)代碼:
public class SimpleFactory {
public static Product createProduct(String arg){
Product product = null;
switch (arg) {
case "A":
product = new ProductA();
// init something
break;
case "B":
product = new ProductB();
// init something
break;
default:
throw new RuntimeException("product not exist for arg: "+arg);
}
return product;
}
}
2)使用場景
對于產(chǎn)品種類少,且產(chǎn)品相對固定,出現(xiàn)新種類產(chǎn)品的可能性小的時候,可以使用簡單工廠模式
我們假定一個簡單的場景,依舊用上一篇建造者模式中提到的電腦。
現(xiàn)在電腦都可以辦公和學(xué)習(xí),因此,我們定義一個公共接口:
public interface IComputer {
void work();
void study();
}
然后,現(xiàn)在一個人有一臺電腦用來工作或者學(xué)習(xí),但是這臺電腦是有配置高低分別的,這時候我們需要兩個類實現(xiàn)上面的接口,并且在構(gòu)造器里,分別配置它的硬件信息:
public class ComputerA implements IComputer {
private static final String TAG = "ComputerA";
public ComputerA(){
//初始化高端硬件配置
Log.i(TAG, "ComputerA: 初始化高端硬件配置");
}
@Override
public void work() {
//高端機(jī)工作
Log.i(TAG, "ComputerA: 高端機(jī)工作");
}
@Override
public void study() {
//高端機(jī)玩學(xué)習(xí)
Log.i(TAG, "ComputerA: 高端機(jī)玩學(xué)習(xí)");
}
}
public class ComputerB implements IComputer {
private static final String TAG = "ComputerB";
public ComputerB(){
//初始化普通硬件配置
Log.i(TAG, "ComputerA: 初始化普通硬件配置");
}
@Override
public void work() {
//普通機(jī)工作
Log.i(TAG, "ComputerA: 普通機(jī)工作");
}
@Override
public void study() {
//普通機(jī)玩學(xué)習(xí)
Log.i(TAG, "ComputerA: 普通機(jī)玩學(xué)習(xí)");
}
}
通過構(gòu)造器,我們已經(jīng)可以獲取到不同硬件配置的電腦,然后我們需要對不同電腦安裝適合的工作學(xué)習(xí)軟件:
public class SimpleFactory {
public static IComputer createProduct(String arg){
IComputer product = null;
switch (arg) {
case "A":
product = new ComputerA();
// 下載學(xué)習(xí)軟件 M
// 下載工作軟件 N
// init something
break;
case "B":
product = new ComputerB();
// 下載學(xué)習(xí)軟件 X
// 下載工作軟件 Y
// init something
break;
default:
throw new RuntimeException("product not exist for arg: "+arg);
}
return product;
}
}
下面在想要工作學(xué)習(xí)的地方,我們只需要傳入所需電腦對應(yīng)的名稱,就可以獲取我們需要的電腦并且實現(xiàn)我們工作學(xué)習(xí)的目的:
IComputer iComputerA = SimpleFactory.createProduct("A");
iComputerA.study();
iComputerA.work();
IComputer iComputerB = SimpleFactory.createProduct("B");
iComputerB.study();
iComputerB.work();
下面是這個過程的日志圖:
對比代碼和日志,我們可以看到,由于簡單工廠的封裝,在需要使用電腦工作學(xué)習(xí)的時候,我們只要知道當(dāng)前需要的電腦名稱:“A” 或 “B”,就可以獲取我們的電腦,并且通過統(tǒng)一的接口方法實現(xiàn)工作學(xué)習(xí)的目的。
這樣做,顯然使得代碼變得更加簡潔,對于業(yè)務(wù)而言邏輯也變得清楚,因為無關(guān)的東西都被隱藏了起來。
3)優(yōu)缺點(diǎn)
**優(yōu)點(diǎn):
構(gòu)造容易、邏輯簡單。只要通過一個工廠方法就可以得到需要的對象,不用關(guān)注這個對象是如何構(gòu)造出來。工廠模式能略過了產(chǎn)品的初始化細(xì)節(jié),并且規(guī)范統(tǒng)一了所有產(chǎn)品功能的調(diào)用方法。
**缺點(diǎn):
由于簡單工廠所有產(chǎn)品的初始化方法都在一個方法中,當(dāng)產(chǎn)品數(shù)量過多且初始化過程復(fù)雜的時候,代碼會顯得過長且難以理解。同時,通過標(biāo)志位分辨生產(chǎn)產(chǎn)品的類型,使得代碼內(nèi)部耦合嚴(yán)重,如果需要新添加一個產(chǎn)品 C ,就必須要修改這個工廠類對象,這顯然不符合前面提到的 開放封閉原則(ASD) ,即對拓展開放,對修改封閉。優(yōu)秀的設(shè)計應(yīng)該能夠在有新產(chǎn)品的時候,盡量保持原代碼不變,只添加新代碼就能實現(xiàn)功能。這一點(diǎn),簡單工廠模式無法達(dá)到。
那么這一點(diǎn),我們需要怎樣達(dá)到呢?于是,工廠方法模式出現(xiàn)了。
二、工廠方法模式
工廠方法模式也是一種類創(chuàng)建模式,與簡單工廠不同的是,它不再通過參數(shù)來區(qū)分當(dāng)前生產(chǎn)的產(chǎn)品對象。工廠方法模式將工廠抽象化,定義了一個公共父類用于規(guī)定創(chuàng)建產(chǎn)品對象的公共接口,并把所有產(chǎn)品實力化的時間延遲到工廠的子類。文字說明有些不好理解,先來看一下圖解。
由圖可以看到,對應(yīng)產(chǎn)品 A 有指定的 FactoryA 用于生產(chǎn),對應(yīng)產(chǎn)品 B 有指定的 FactoryB 來生產(chǎn)。FactoryA 和 FactoryB 都是 IFactory 的子類,如果想要生產(chǎn)新的產(chǎn)品 C ,只需要多加一個 IFactory 子類用于生產(chǎn) C ,而不需要改動原有代碼。因此這種模式,也叫做多態(tài)工廠模式。
還是用上面的電腦例子,現(xiàn)在我們要多加一個生產(chǎn)工廠,生產(chǎn)低端電腦:
工廠方法模式遵循[開放封閉原則(ASD)],但是它也有缺點(diǎn),即完成相同的功能,對于工廠方法而言要新增一個工廠類一個產(chǎn)品類,文件數(shù)量多,使得系統(tǒng)變得復(fù)雜。
而現(xiàn)在問題又來了,高端、中端、低端電腦都有了,但是實際上,對于電腦我們不只有配置高低,還有品牌。現(xiàn)在高端、中端、低端電腦提供商有兩家,一家是聯(lián)想,一家是宏基。在這樣的情況下, 工廠方法模式的設(shè)計并沒有辦法讓我們獲取我們需要的品牌的電腦。于是,我們用到了抽象工廠模式。
三、抽象工廠模式
抽象工廠模式是對工廠模式的抽象。事實上,抽象工廠模式是最具有一般性質(zhì)的工廠模式。為了了解這個最為抽象的模式,首先,我們需要了解幾個概念,我們還是會以熟悉的電腦為例子進(jìn)行說明:
- **產(chǎn)品繼承結(jié)構(gòu):例如高端電腦是一個類別,而聯(lián)想高端電腦和宏基高端電腦,都是這個類別的子類,實現(xiàn)了它的功能接口。
- **產(chǎn)品族:產(chǎn)品族是指由同一個工廠生產(chǎn)的,位于不同產(chǎn)品等級結(jié)構(gòu)中的一組產(chǎn)品。這里是指同個品牌下的一系列產(chǎn)品,例如聯(lián)想有高端、中端、低端的電腦產(chǎn)生流水線,同樣的宏基也有,而這高端、中端、低端的三種電腦,就是他們的產(chǎn)品族
在這里,例子中的高端、中端和低端更換為商務(wù)本、游戲本和二合一的平板筆記本可能更合適些。不過,管他呢。
一個抽象工廠模式一般包含如下角色:
AbstractFactory:抽象工廠
ConcreteFactory:具體工廠
AbstractProduct:抽象產(chǎn)品
Product:具體產(chǎn)品
先看下圖解:
可以看到,不同品牌的生產(chǎn)工廠分別實現(xiàn)了抽象工廠的接口,將自己的生產(chǎn)過程封裝起來,在我們需要電腦工作學(xué)習(xí)的時候,只需要根據(jù)需要的品牌選取合適的工廠類,然后利用工廠類選擇合適的機(jī)型,就可以獲取我們需要的電腦對象:
public interface IComputerFactory {
IComputer createComputerA();
IComputer createComputerB();
}
public class LenovoComputerFactory implements IComputerFactory {
private static final String TAG = "LenovoComputerFactory";
public LenovoComputerFactory(){
Log.i(TAG, "LenovoComputerFactory: 聯(lián)想電腦工廠初始化");
}
@Override
public IComputer createComputerA() {
Log.i(TAG, "createComputerA: 聯(lián)想電腦工廠制造高級配置電腦");
return new ComputerA();
}
@Override
public IComputer createComputerB() {
Log.i(TAG, "createComputerB: 聯(lián)想電腦工廠制造普通配置電腦");
return new ComputerB();
}
}
調(diào)用日志打印:
抽象工廠模式的優(yōu)點(diǎn)在于,新添加產(chǎn)品族是符合[開放封閉原則(ASD)],但是新增產(chǎn)品的繼承結(jié)構(gòu)卻是不符合的,在具體項目中需要權(quán)衡傾向,再作應(yīng)用。
感謝:
以上。