工廠模式
工廠模式是開發(fā)中常用的一種設(shè)計模式,每一種設(shè)計模式都會極大的解決程序設(shè)計方面的問題,工廠模式也是一樣,本文將會用通俗的語言來解釋什么是工廠模式?工廠模式的種類、代碼示例、每種工廠模式的優(yōu)缺點和工廠模式適用的場景。
為什么要是使用工廠模式?
首先我們用一個生動故事來描述下什么是工廠模式,這會讓你更快的理解工廠模式,為后面理解的工廠模式的幾種實現(xiàn)方式打下基礎(chǔ)。
假如,你需要讓公司開一個收入證明為自己貸款買房提供收入證明,一般開收入證明的過程是:
- 打印收入證明;
- 在收入證明上蓋上公司的章;
貫穿整個過程,可以知道,你需要創(chuàng)建一個收入證明,并使用收入證明為貸款買房提供資料。這個過程中,有兩個關(guān)鍵的行為:創(chuàng)建收入證明,使用收入證明。創(chuàng)建收入證明的過程中又分為兩步:打印、蓋章。不熟悉這個流程的同事在創(chuàng)建收入證明的時候,往往會遇到很多麻煩導(dǎo)致開收入證明頻頻受阻,于是公司決定把員工開收入證明這件事做個優(yōu)化,以前需要兩步做的事,現(xiàn)在只需要發(fā)一份郵件給財務(wù)部門,財務(wù)部門就會在下班之前就會把蓋好章的收入證明送到工位,這就是生活中遇到的工廠模式。
說了這么多,工廠模式究竟解決了生活中的哪些問題呢?
在這個生活案例中,它讓員工創(chuàng)建收入證明這件事情變得更加簡單,員工不需要知道收入證明是怎么創(chuàng)建的,只需要發(fā)一份郵件給財務(wù)部門,財務(wù)部門就會幫助員工打印并蓋章。而且在后續(xù)的公司發(fā)展中,如果需要在收入證明中蓋上更多的章,員工也不需要自己取熟悉整個流程,拿著收入證明跑斷腿的到各個部門去蓋章。
那么,在程序的世界中,如何使用代碼演示員工為貸款買房,去公司開收入證明這個行為呢?又是如何用代碼展示工廠模式的呢?
類比上面的例子,假如有個收入證明類 創(chuàng)建只需要new一下就行,入?yún)⑹?code>打印證明和給證明蓋章,如果后續(xù)收入證明的蓋章變了,需要修改入?yún)ⅰ_@就需要改大量調(diào)用創(chuàng)建收入證明類的new代碼。
于是這時工廠模式就可以使用了,工廠模式將類的創(chuàng)建和類的使用分離出來,當(dāng) Class A 想調(diào)用 Class B ,那么A只是調(diào)用B的方法,而至于B的實例化,就交給工廠類。
那又有人說,也可以把這些創(chuàng)建過程的代碼放到類的構(gòu)造函數(shù)里,同樣可以降低重復(fù)率,而且構(gòu)造函數(shù)本身的作用也是初始化對象。針對這個觀點,我們可以對比下工廠模式相較于構(gòu)造函數(shù)的優(yōu)點:
優(yōu)點:
靜態(tài)工廠方法有名字而構(gòu)造函數(shù)沒有,因為工廠方法可以有多個,通過名字來區(qū)別各個方法,但構(gòu)造函數(shù)名字只能有一個,只能通過參數(shù)來區(qū)別,所以使用靜態(tài)工廠方法更明了。
靜態(tài)工廠方法支持條件性實例化,就是說你創(chuàng)建對象時,有時需要添加一些條件判斷是否應(yīng)該創(chuàng)建,如果滿足條件則返回一個實例,不滿足則返回NULL,比如單例模式。構(gòu)造函數(shù)時做不到這樣的,構(gòu)造函數(shù)的功能需要保持單一,只為了構(gòu)造而存在,而靜態(tài)工廠方法可以很簡單的做到。
方法能夠返回返回類型的子類型
這就是工廠模式在創(chuàng)建對象上的一些優(yōu)點,而工廠模式最核心的知識點是:將對象的創(chuàng)建和使用做分離。請默念三遍。
抓住了核心點,再去了解工廠模式的各種實現(xiàn)就簡單的多了,工廠模式一般可以分為三種:
- 簡單/靜態(tài)工廠模式
- 工廠方法模式
- 抽象工廠模式
我們學(xué)習(xí)步驟按照:工廠方法模式 到 簡單靜態(tài)工廠模式 到 抽象工廠模式,示例都非常簡單,一看就懂,文中的代碼我放在github上感興趣的同學(xué)可以下載:
github地址: java23種設(shè)計模式代碼示例--工廠模式
我知道大家都是愛心人士,白嫖當(dāng)然不是你們的習(xí)慣,歡迎大家來一波素質(zhì)三連:關(guān)注、點贊、點star
工廠方法模式
工廠方法相對比較和簡單\靜態(tài)工廠相對簡單比較容易理解。
如果我們需要在工廠里造一臺手機(jī),那么先定義一個phone抽象類,手機(jī)必須要有打電話功能,加一個call抽象方法
package com.shuai.design.factory.normals;
public abstract class Phone {
// 所有的手機(jī)必須要有打電話的功能
public abstract void call();
}
創(chuàng)建一個手機(jī)的工廠接口,里面有個createPhone方法,其他類型手機(jī)都要繼承這個接口,創(chuàng)建手機(jī)必須實現(xiàn)這個方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public interface PhoneFactory {
Phone createPhone();
}
創(chuàng)建小米手機(jī)類。繼承phone,實現(xiàn)call方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
創(chuàng)建小米手機(jī)工廠
package com.shuai.design.factory.normals;
public class MiPhoneFactory implements PhoneFactory {
@Override
public MiPhone createPhone() {
return new MiPhone();
}
}
類似小米手機(jī),實現(xiàn)華為手機(jī)類,繼承phoen抽象類,實現(xiàn)call方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
實現(xiàn)華為手機(jī)工廠
package com.shuai.design.factory.normals;
public class HuaWeiPhoneFactory implements PhoneFactory{
@Override
public HuaWeiPhone createPhone() {
return new HuaWeiPhone();
}
}
簡單靜態(tài)工廠模式
靜態(tài)工廠相對于工廠方法模式簡單的多,首先創(chuàng)建phone的抽象類
package com.shuai.design.factory.simples;
public abstract class Phone {
// 所有的手機(jī)必須要有打電話的功能
public abstract void call();
}
再創(chuàng)建phone的構(gòu)建工廠類,根據(jù)傳入的類型創(chuàng)建不同類型的手機(jī)
package com.shuai.design.factory.simples;
public class PhoneFactory {
public static MiPhone createMiPhone() {
return new MiPhone();
}
public static HuaWeiPhone createHuaWeiPhone() {
return new HuaWeiPhone();
}
public static Phone createPhone(String type) {
if ("Mi".equals(type)) {
return new MiPhone();
} else {
return new HuaWeiPhone();
}
}
}
實現(xiàn)小米手機(jī)類
package com.shuai.design.factory.simples;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
實現(xiàn)華為手機(jī)類
package com.shuai.design.factory.simples;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
抽象工廠模式
定義:為創(chuàng)建一組相關(guān)或相互依賴的對象提供一個接口,而且無須指定它們的具體類
圖為:

這里還是用創(chuàng)建手機(jī)的方式來實現(xiàn)抽象工廠,先創(chuàng)建一個phone的抽象類,里面不僅有call方法,還有手機(jī)的屏幕類型
package com.shuai.design.factory.abstracts;
public abstract class Phone {
// 所有的手機(jī)必須要有打電話的功能
public abstract void call();
// 手機(jī)的屏幕類型
public abstract void screenType();
}
再創(chuàng)建一個手機(jī)構(gòu)建工廠的父接口
package com.shuai.design.factory.abstracts;
public interface PhoneFactory {
Phone createMiPhone();
Phone createHuaWeiPhone();
}
首先所有小米手機(jī)和華為手機(jī)都必須實現(xiàn)通話功能,重寫call方法
package com.shuai.design.factory.abstracts;
public abstract class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
package com.shuai.design.factory.abstracts;
public abstract class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
在根據(jù)手機(jī)的屏幕類型分為小米折疊屏手機(jī):
package com.shuai.design.factory.abstracts;
public class FoldingScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米折疊屏手機(jī)");
}
}
在根據(jù)手機(jī)的屏幕類型分為華為折疊屏手機(jī):
package com.shuai.design.factory.abstracts;
public class FoldingScreenHuaWeiPhone extends HuaWeiPhone {
@Override
public void screenType() {
System.out.println("this is 華為折疊屏手機(jī)");
}
}
在根據(jù)手機(jī)的屏幕類型分為小米曲面屏屏手機(jī):
package com.shuai.design.factory.abstracts;
public class CurvedScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米曲面屏手機(jī)");
}
}
在根據(jù)手機(jī)的屏幕類型分為華為曲面屏手機(jī):
package com.shuai.design.factory.abstracts;
public class CurvedScreenHuaWeiPhone extends HuaWeiPhone{
@Override
public void screenType(){
System.out.println("this is 華為曲面屏手機(jī)");
}
}
針對不同的屏幕類型手機(jī),抽象出根據(jù)屏幕類型的構(gòu)建工廠,有曲面屏手機(jī)工廠和折疊屏手機(jī)工廠:
package com.shuai.design.factory.abstracts;
//曲面屏手機(jī)工廠
public class CurvedScreenPhoneFactory implements PhoneFactory {
@Override
public CurvedScreenMiPhone createMiPhone() {
return new CurvedScreenMiPhone();
}
@Override
public CurvedScreenHuaWeiPhone createHuaWeiPhone() {
return new CurvedScreenHuaWeiPhone();
}
}
折疊屏手機(jī)工廠
package com.shuai.design.factory.abstracts;
//折疊屏手機(jī)工廠
public class FoldingScreenPhoneFactory implements PhoneFactory{
@Override
public FoldingScreenMiPhone createMiPhone() {
return new FoldingScreenMiPhone();
}
@Override
public FoldingScreenHuaWeiPhone createHuaWeiPhone() {
return new FoldingScreenHuaWeiPhone();
}
}
再寫一個測試類測試一下結(jié)果:
package com.shuai.design.factory.abstracts;
public class Test {
public static void main(String[] args) {
// 創(chuàng)建一個華為曲面屏手機(jī)
CurvedScreenHuaWeiPhone curvedScreenHuaWeiPhone = new CurvedScreenPhoneFactory().createHuaWeiPhone();
System.out.println("curvedScreenHuaWeiPhone 的手機(jī)類型為:");
curvedScreenHuaWeiPhone.screenType();
// 創(chuàng)建一個小米曲面屏手機(jī)
CurvedScreenMiPhone curvedScreenMiPhone = new CurvedScreenPhoneFactory().createMiPhone();
System.out.println("curvedScreenMiPhone 的手機(jī)類型為:");
curvedScreenMiPhone.screenType();
// 創(chuàng)建一個華為折疊屏手機(jī)
FoldingScreenHuaWeiPhone foldingScreenHuaWeiPhone = new FoldingScreenPhoneFactory().createHuaWeiPhone();
System.out.println("foldingScreenHuaWeiPhone 的手機(jī)類型為:");
foldingScreenHuaWeiPhone.screenType();
// 創(chuàng)建一個小米折疊屏手機(jī)
FoldingScreenMiPhone foldingScreenMiPhone = new FoldingScreenPhoneFactory().createMiPhone();
System.out.println("foldingScreenMiPhone 的手機(jī)類型為:");
foldingScreenMiPhone.screenType();
}
}
抽象工廠模式的使用場景:一個對象族(或是一組沒有任何關(guān)系的對象)都有相同的約束,則可以使用抽象工廠模式。通過工廠類,只要知道工廠類是誰,我就能創(chuàng)建出一個需要的對象

缺點 :
擴(kuò)展產(chǎn)品族困難。比如在phone中類型增加一個帶手寫筆類型的手機(jī),那么每個已經(jīng)實現(xiàn)的手機(jī)類就都需要實現(xiàn)這個方法。這嚴(yán)重違反了開閉原則。
優(yōu)點:
增加等級簡單。如果在折疊屏手機(jī)下增加一個雙折疊屏和三折疊屏的手機(jī)這就比較簡單,只需要在折疊屏手機(jī)構(gòu)建工廠下面修改就行。
總結(jié)
實際上,一般開發(fā)過程中,我們使用簡單工廠模式比較多,抽象工廠模式的話需要業(yè)務(wù)比較大的情況下才會用到。如果你有更好的觀點,歡迎在評論區(qū)提出,互相學(xué)習(xí)。
參考資料:
- 《工廠模式理解了沒有?(https://segmentfault.com/a/1190000014949595)》
- 《設(shè)計模式系列-Builder模式,工廠方法模式和抽象工廠模式》
- 《產(chǎn)品等級結(jié)構(gòu)與產(chǎn)品族》
歡迎關(guān)注公眾號:java之旅