工廠模式顧名思義,工廠就是用來統(tǒng)一創(chuàng)建產(chǎn)品的。我們的工廠根據(jù)產(chǎn)品是具體產(chǎn)品還是工廠可以分為簡單工廠模式和工廠方法模式,根據(jù)抽象程度可以分為工廠方法模式和抽象工廠模式。工廠模式的核心本質(zhì)是:
- 實(shí)例化對(duì)象不使用new,用工廠方法替代
- 將選擇實(shí)現(xiàn)類,創(chuàng)建對(duì)象統(tǒng)一管理和控制。從而將調(diào)用者跟我們的實(shí)現(xiàn)類解耦。
工廠模式在我們的場景中用到的還是比較多的:
- JDK中Calendar的getInstance方法
- JDBC中的Connection對(duì)象的獲取
- Spring中IOC容器創(chuàng)建管理bean對(duì)象
- 反射中Class對(duì)象的newInstance方法
簡單工廠模式(靜態(tài)工廠模式)
用來生產(chǎn)同一等級(jí)結(jié)構(gòu)中的任意產(chǎn)品,只需要對(duì)工廠傳遞需要?jiǎng)?chuàng)建對(duì)象的類型即可。這里我們以計(jì)算器為來講解該模式:

首先是我們的運(yùn)算統(tǒng)一接口(Operation)
package simplefactory;
//運(yùn)算統(tǒng)一接口
public interface Operation {
void operate();
}
加法操作:
package simplefactory;
public class AddOperation implements Operation{
@Override
public void operate() {
System.out.println("進(jìn)行了加法操作!");
}
}
減法操作:
package simplefactory;
public class SubOperation implements Operation{
@Override
public void operate() {
System.out.println("進(jìn)行了減法操作!");
}
}
我們定義一個(gè)簡單工廠來代替我們創(chuàng)建對(duì)象:
package simplefactory;
public class OpetationFactory {
public static Operation createOperate(String operate){
Operation oper = null;
switch (operate){
case "加法":
oper = new AddOperation();
break;
case "減法":
oper = new SubOperation();
break;
}
return oper;
}
}
我們?cè)诳蛻舳诉M(jìn)行實(shí)踐一下:
package simplefactory;
public class Client {
public static void main(String[] args) {
//第一個(gè)操作執(zhí)行加法操作
Operation operation1 = OpetationFactory.createOperate("加法");
operation1.operate();
//第二個(gè)操作執(zhí)行減法操作
Operation operation2 = OpetationFactory.createOperate("減法");
operation2.operate();
}
}
//演示結(jié)果
//進(jìn)行了加法操作!
//進(jìn)行了減法操作!
這樣我們就不需要?jiǎng)?chuàng)建對(duì)象這個(gè)過程,只需要告訴工廠我們需要什么對(duì)象就行,省略了很多的麻煩。但是如果我們這個(gè)時(shí)候要增加一個(gè)要求怎么辦呢?我們先需要實(shí)現(xiàn)操作統(tǒng)一接口,然后再在工廠里面增加我們的新操作的分支,比如我們?cè)黾右粋€(gè)乘法的操作的話。
實(shí)現(xiàn)接口:
package simplefactory;
public class MulOperation implements Operation{
@Override
public void operate() {
System.out.println("進(jìn)行了乘法操作!");
}
}
在工廠里面添加分支:
package simplefactory;
public class OpetationFactory {
public static Operation createOperate(String operate){
Operation oper = null;
switch (operate){
case "加法":
oper = new AddOperation();
break;
case "減法":
oper = new SubOperation();
break;
case "乘法": //新加的乘法操作分支
oper = new MulOperation();
break;
}
return oper;
}
}
很顯然,這樣的操作如果在工程量特別大的去修改代碼是特別麻煩的,而且主要的是,這不符合我們的開閉原則(對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放)。
所以我們是有辦法來關(guān)閉修改操作的——反射,下面代碼我們對(duì)創(chuàng)建工廠進(jìn)行重構(gòu)之后(T表示只要實(shí)現(xiàn)了Operaton都可以當(dāng)作接口,是JDK1.5之后的新特性——泛型,減少了對(duì)象之間的轉(zhuǎn)換)
package simplefactory;
public class OpetationFactory {
public static <T extends Operation> T createOperate(Class<T> c){
Operation oper = null;
try{
oper = (T) Class.forName(c.getName()).newInstance();
}catch (Exception e){
System.out.println("沒有該操作!");
}
return (T) oper;
}
}
這個(gè)時(shí)候如果需要添加新的操作的時(shí)候,我們只需要?jiǎng)?chuàng)建實(shí)現(xiàn)類即可,然后在客戶端就可以使用了,關(guān)閉了對(duì)創(chuàng)建工廠的修改。符合了我們的開閉原則。
package simplefactory;
public class Client {
public static void main(String[] args) {
//第一個(gè)操作執(zhí)行加法操作
Operation operation1 = OpetationFactory.createOperate(AddOperation.class);
operation1.operate();
//第二個(gè)操作執(zhí)行減法操作
Operation operation2 = OpetationFactory.createOperate(SubOperation.class);
operation2.operate();
//第三個(gè)操作為我們的乘法操作
Operation operation3 = OpetationFactory.createOperate(MulOperation.class);
operation3.operate();
}
}
控制臺(tái)結(jié)果:
進(jìn)行了加法操作!
進(jìn)行了減法操作!
進(jìn)行了乘法操作!
Process finished with exit code 0
對(duì)簡單工廠模式的優(yōu)化還有其他方式,可以看看這篇博客Java簡單工廠模式以及來自lambda的優(yōu)化
工廠方法模式
工廠方法是對(duì)簡單工廠模式進(jìn)一步的修改,是在不修改已有類的前提下,通過增加新的工廠類實(shí)現(xiàn)擴(kuò)展。可以說是對(duì)沒有優(yōu)化前的簡單工廠模式的一種優(yōu)化,貼合了開閉原則。
我們通過UML圖可以清晰認(rèn)識(shí):

我們操作的接口和實(shí)現(xiàn)類不變,只不過我們把原來在一個(gè)工廠生產(chǎn)對(duì)象的的工廠變成了每一個(gè)實(shí)現(xiàn)類都有自己的生產(chǎn)工廠了。這樣我們?nèi)绻枰偬砑有碌牟僮鞯脑?,我們只需要添加一個(gè)實(shí)現(xiàn)類和一個(gè)工廠就行了,非常符合我們的開閉原則。
定義我們工廠統(tǒng)一接口的抽象類:
package factorymethod;
//工廠統(tǒng)一接口
public interface OperationFactory {
Operation doingOperate();
}
然后是我們加法的工廠:
package factorymethod;
public class AddOperationFactory implements OperationFactory{
@Override
public Operation doingOperate() {
return new AddOperation();
}
}
減法的工廠:
package factorymethod;
public class SubOperationFactory implements OperationFactory {
@Override
public Operation doingOperate() {
return new SubOperation();
}
}
我們的客戶端:
package factorymethod;
public class Client {
public static void main(String[] args) {
OperationFactory factoryAdd = new AddOperationFactory();
OperationFactory factorySub = new SubOperationFactory();
// 第一個(gè)操作為加法操作
Operation operation1 = factoryAdd.doingOperate();
operation1.operate(); //輸出:進(jìn)行了加法操作
// 第二個(gè)操作為減法操作
Operation operation2 = factorySub.doingOperate();
operation2.operate(); //輸出:進(jìn)行了減法操作
}
}
抽象工廠模式
抽象工廠總的來說,提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無需指定它們具體的類。就是圍繞一個(gè)超級(jí)工廠來創(chuàng)建其他工廠,該超級(jí)工廠又稱為其他工廠的工廠。
這里我再以計(jì)算器的操作來舉例有點(diǎn)不合適,網(wǎng)上最多的就是手機(jī)和電腦品牌的舉例。因?yàn)榭梢员容^清晰理解抽象工廠,我們不是增加一個(gè)產(chǎn)品,而是增加一個(gè)產(chǎn)品族。如果我們只是單一的一個(gè)手機(jī)可以使用上面的工廠方法來創(chuàng)建,但是如果多了一個(gè)電腦呢?可以復(fù)制一下手機(jī)工廠方法的代碼,修改一下名字就可以使用,但是這很明顯的就是重復(fù)代碼了吧。我們是無論如何也不能忍受的,所以就有了抽象工廠模式。
首先我們先來定義一個(gè)手機(jī)的統(tǒng)一接口
package abstractfactory;
public interface Phone {
void make();
}
實(shí)現(xiàn)華為和小米的Phone實(shí)現(xiàn)類:
package abstractfactory;
public class HuaWeiPhone implements Phone {
@Override
public void makePhone() {
System.out.println("生產(chǎn)的是華為手機(jī)!");
}
}
package abstractfactory;
public class XiaoMiPhone implements Phone{
@Override
public void makePhone() {
System.out.println("生產(chǎn)的是小米手機(jī)!");
}
}
定義一個(gè)電腦的統(tǒng)一接口
package abstractfactory;
public interface Computer {
void makeComputer();
}
實(shí)現(xiàn)華為和小米的Computer實(shí)現(xiàn)類:
package abstractfactory;
public class HuaWeiComputer implements Computer {
@Override
public void makeComputer() {
System.out.println("生產(chǎn)的是華為電腦!");
}
}
package abstractfactory;
public class XiaoMiComputer implements Computer{
@Override
public void makeComputer() {
System.out.println("生產(chǎn)的是小米電腦!");
}
}
然后就是我們的抽象工廠了,因?yàn)槲覀儾还苁切∶坠S還是華為工廠都需要生產(chǎn)手機(jī)和電腦,所以我們可以說這就是一個(gè)產(chǎn)品族。
package abstractfactory;
public interface AbstractFactory {
Phone makePhone();
Computer makeComputer();
}
然后我們實(shí)現(xiàn)抽象工廠的接口,有小米工廠和華為工廠
package abstractfactory;
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new XiaoMiPhone();
}
@Override
public Computer makeComputer() {
return new XiaoMiComputer();
}
}
package abstractfactory;
public class HuaWeiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new HuaWeiPhone();
}
@Override
public Computer makeComputer() {
return new HuaWeiComputer();
}
}
在我們的客戶端使用:
package abstractfactory;
public class Client {
public static void main(String[] args) {
AbstractFactory xiaoMiFactory = new XiaoMiFactory();
Phone xiaoMiPhone = xiaoMiFactory.makePhone();
xiaoMiPhone.makePhone(); //輸出:生產(chǎn)的是小米手機(jī)!
Computer xiaoMiComputer = xiaoMiFactory.makeComputer();
xiaoMiComputer.makeComputer(); //輸出:生產(chǎn)的是小米電腦!
AbstractFactory huaWeiFactory = new HuaWeiFactory();
Phone huaWeiPhone = huaWeiFactory.makePhone();
huaWeiPhone.makePhone(); // 輸出:生產(chǎn)的是華為手機(jī)!
Computer huaWeiComputer = huaWeiFactory.makeComputer();
huaWeiComputer.makeComputer(); //輸出:生產(chǎn)的是華為電腦!
}
}
關(guān)于我們的抽象工廠的適用場景:
- 客戶端不依賴產(chǎn)品類實(shí)例如何被創(chuàng)建,實(shí)現(xiàn)等細(xì)節(jié)
- 強(qiáng)調(diào)一系列相關(guān)的產(chǎn)品對(duì)象(屬于同一產(chǎn)品族)一起使用創(chuàng)建對(duì)象需要大量的重復(fù)代碼
- 提供一個(gè)產(chǎn)品類的庫,所有的產(chǎn)品以同樣的接口出現(xiàn),從而使得客戶端不依賴于具體的實(shí)現(xiàn)
優(yōu)點(diǎn):
- 具體產(chǎn)品在應(yīng)用層的代碼隔離,無需關(guān)心創(chuàng)建的細(xì)節(jié)
- 將一個(gè)系列的產(chǎn)品統(tǒng)一到一起創(chuàng)建
缺點(diǎn):
- 規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合,產(chǎn)品簇中擴(kuò)展新的產(chǎn)品困難(注意是產(chǎn)品擴(kuò)展族而不是產(chǎn)品等級(jí)。比如我們這個(gè)時(shí)候要添加一個(gè)一加品牌,只要擁有相同的產(chǎn)品簇,我們只要實(shí)現(xiàn)產(chǎn)品接口有實(shí)現(xiàn)類然后添加一個(gè)工廠生產(chǎn)實(shí)例就可以實(shí)現(xiàn)??梢哉f是橫向擴(kuò)展,符合開閉原則。但是如果這時(shí)候要添加一個(gè)充電寶的產(chǎn)品,就需要修改里面的大量代碼,極其復(fù)雜,所以需要在剛開始就要設(shè)計(jì)好。)
- 增加了系統(tǒng)的抽象性和理解難度
總結(jié)
工廠模式的三種模式有各自的應(yīng)用場景,無論哪種模式,只要能解決問題就是好用的。
參考資料
《大話設(shè)計(jì)模式》
《設(shè)計(jì)模式之禪(第二版)》