咱們都知道
Java
中共有 23 種設計模式,其中工廠模式分爲三種,即:簡單工廠模式(不在 23 種設計模式之列)、工廠方法模式和抽象工廠模式;咱們平時說的工廠模式,其實大都指工廠方法模式,這種模式是咱們平時編碼中用的頻率最高的一種,在Spring
源碼中就有不少工廠模式的應用,好比BeanFactory
。java
下面依次按照簡單工廠模式、工廠方法模式、抽象工廠模式的順序,依次由淺入深說說這三種模式;文章分別從定義、場景、優缺點也示例進行講解。git
簡單工廠模式(Simple Factory Pattern)是指由一個工廠對象決定建立出哪種產品類的實例,簡單來講就是,定義一個工廠類,根據傳入的參數不一樣返回不一樣的實例,被建立的實例具備共同的父類或接口。github
簡單工廠適用於工廠類負責建立的對象較少的場景,且客戶端只須要傳入工廠類的參數,對於如何建立對象的邏輯不須要關心。總結一下就是:設計模式
實現了對責任的分割,提供了專門的工廠類用於建立對象ide
工廠類的職責相對太重,不易於擴展過於複雜的產品結構,不符合開閉原則(可解決)測試
接下來咱們構造一個場景來看看簡單工廠模式的應用:如今手機更新換代的比較快,手機廠商每一年基本都會在不一樣時間或者在同一時間發佈生產不一樣型號和配置的手機。優化
假設某手機公司最近發佈了型號爲 A、B 的手機,其中生產任務交給代工廠去生產;咱們都知道無論什麼類型的手機都屬於手機,因此咱們先建立一個手機類Phone
,並在其中聲明一個公共的手機型號方法type
:編碼
/**
* @author eamon.zhang
* @date 2019-09-27 上午10:55
*/
public interface Phone {
void type();
}複製代碼
而後定義具體的手機類型:spa
型號 A:設計
/**
* @author eamon.zhang
* @date 2019-09-27 上午11:02
*/
public class PhoneA implements Phone {
@Override
public void type() {
System.out.println("型號爲A的手機!");
}
}複製代碼
型號 B:
/**
* @author eamon.zhang
* @date 2019-09-27 上午11:03
*/
public class PhoneB implements Phone {
@Override
public void type() {
System.out.println("型號爲B的手機!");
}
}複製代碼
建立手機代工廠 PhoneFactory
類:
/**
* @author eamon.zhang
* @date 2019-09-27 上午10:54
*/
public class PhoneFactory {
public Phone product(String type) {
switch (type) {
case "A":
return new PhoneA();
case "B":
return new PhoneB();
default:
return null;
}
}
}複製代碼
測試:
/**
* @author eamon.zhang
* @date 2019-09-27 上午11:09
*/
public class PhoneFactoryTest {
@Test
public void product() {
PhoneFactory phoneFactory = new PhoneFactory();
phoneFactory.product("A").type();
phoneFactory.product("B").type();
}
}複製代碼
輸出:
型號爲A的手機!
型號爲B的手機!複製代碼
固然,爲了方便調用,
PhoneFactory
中的product()
也能夠寫成靜態的。
類圖:
上面的示例中,客戶端調用是簡單了,但若是咱們業務繼續擴展,增長一個型號 C,那麼上面的工廠方法中的product()
方法就得再次修改邏輯。不符合開閉原則;所以咱們客戶考慮對其進行進一步優化,利用反射技術修改product()
方法:
public Phone product(String className) {
try {
if (!(null == className || "".equals(className))) {
return (Phone) Class.forName(className).newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}複製代碼
修改客戶端調用代碼:
public void product() {
PhoneFactory phoneFactory = new PhoneFactory();
phoneFactory.product("com.eamon.javadesignpatterns.factory.PhoneA").type();
phoneFactory.product("com.eamon.javadesignpatterns.factory.PhoneB").type();
}複製代碼
通過優化以後,從此再增長型號,就不用去修改工廠方法了;可是又有一個問題,方法參數是很長的字符串,可控性有待提高,並且還須要強制轉型,不方便閱讀和維護,因此進一步改造:
public Phone product(Class<? extends Phone> clazz) {
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}複製代碼
優化客戶端調用代碼:
@Test
public void product() {
PhoneFactory phoneFactory = new PhoneFactory();
phoneFactory.product(PhoneA.class).type();
phoneFactory.product(PhoneB.class).type();
}複製代碼
再來看一下類圖:
簡單工廠模式在 JDK 源碼中也無處不足,好比經常使用的 Calendar
類中Calendar.getInstance()
方法,跟進源碼到createCalendar(TimeZone zone,Locale aLocale)
就能夠看出。
還有就是 經常使用的logback
,咱們能夠看到 LoggerFactory
中有多個重載的方法 getLogger()
:
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public final Logger getLogger(final Class<?> clazz) {
return getLogger(clazz.getName());
}複製代碼
工廠方法模式(Fatory Method Pattern)是指定義一個建立對象的接口,但讓實現這個 接口的類來決定實例化哪一個類,工廠方法讓類的實例化推遲到子類中進行。
在工廠方法模式中用戶只須要關心所需產品對應的工廠,無須關心建立細節,並且加入新的產品符 合開閉原則。
工廠方法模式主要解決產品擴展的問題,在簡單工廠中,隨着產品鏈的豐富,若是每一個手機的建立邏輯有區別的話,工廠的職責會變得愈來愈多,有點像萬能工廠,並不便於維護。根據單一職責原則咱們將職能繼續拆分,專人幹專事。
工廠方法適用於如下場景:
一、類的個數容易過多,增長複雜度。二、增長了系統的抽象性和理解難度。
A 型號手機由PhoneA
工廠建立,B 型號手機由PhoneB
工廠建立,對工廠自己也作一個抽象。來看代碼,先建立 PhoneFactory
接口:
/**
* @author eamon.zhang
* @date 2019-09-27 下午1:45
*/
public interface PhoneFactory {
Phone product();
}複製代碼
分別建立子工廠 PhoneAFactory
:
/**
* @author eamon.zhang
* @date 2019-09-27 下午1:50
*/
public class PhoneAFactory implements PhoneFactory {
@Override
public Phone product() {
return new PhoneA();
}
}複製代碼
PhoneBFactory
類:
/**
* @author eamon.zhang
* @date 2019-09-27 下午1:50
*/
public class PhoneBFactory implements PhoneFactory {
@Override
public Phone product() {
return new PhoneB();
}
}複製代碼
看測試代碼:
/**
* @author eamon.zhang
* @date 2019-09-27 下午1:54
*/
public class PhoneFactoryTest {
@Test
public void product() {
PhoneFactory factory = new PhoneAFactory();
factory.product().type();
factory = new PhoneBFactory();
factory.product().type();
}
}複製代碼
測試結果:
型號爲A的手機!
型號爲B的手機!複製代碼
再看一下類圖:
再來看看 logback 中工廠方法模式的應用,看看類圖就 OK 了:
抽象工廠模式(Abastract Factory Pattern)是指提供一個建立一系列相關或相互依賴對象的接口,無需指定他們具體的類。
客戶端(應用層)不依賴於產品類實例如何被建立、實現等細節。強調的是一系列相關的產品對象(屬於同一產品族)一塊兒使用建立對象須要大量重複的代碼。須要提供一個產品類的庫,全部的產品以一樣的接口出現,從而使客戶端不依賴於具體實現。
爲了便於你們理解抽象工廠,咱們先了解兩個概念產品等級結構和產品族,看下面的圖:從上圖中看出有正方形,圓形和三角形三種圖形,相同顏色深淺的就表明同一個產品族,相同形狀的表明同一個產品等級結構。一樣能夠從生活中來舉例,好比,美的電器生產多種家用電器。那麼上圖中,顏色最深的正方形就表明美的洗衣機、顏色最深的圓形表明美的空調、顏色最深的三角形表明美的熱水器,顏色最深的一排都屬於美的品牌,都是美的電器這個產品族。再看最右側的三角形,顏色最深的咱們指定了表明美的熱水器,那麼第二排顏色稍微淺一點的三角形,表明海信的熱水器。同理,同一產品結構下還有格力熱水器,格力空調,格力洗衣機。
再看下面這張圖,最左側的箭頭表明具體的工廠,有美的工廠、海信工廠、格力工廠。每一個品牌的工廠都生產洗衣機、熱水器、空調。
經過上面兩張圖的對比理解,相信你們對抽象工廠有了很是形象的理解。
一個對象族(或是一組沒有任何關係的對象)都有相同的約束,則可使用抽象工廠模式。簡單來講:
好比如今有一個應用,假如是某視頻軟件,須要在三個不一樣的平臺(Windows、IOS、Android)上運行,該應用針對每套系統都設計了一套上傳控制器(UploadController
)、播放控制(DisplayController
),下面經過抽象工廠模式來設計該軟件。
視頻軟件裏邊的各個平臺的UploadController
和DisplayController
應該是咱們最終生產的具體產品。因此新建兩個抽象產品接口。
UploadController
接口:
/**
* @author eamon.zhang
* @date 2019-09-27 下午2:59
*/
public interface UploadController {
void upload();
}複製代碼
DisplayController
接口:
/**
* @author eamon.zhang
* @date 2019-09-27 下午2:59
*/
public interface DisplayController {
void display();
}複製代碼
定義抽象工廠VideoPlayerFactory
類,它可以建立UploadController
和DisplayController
:
/**
* 抽象工廠是主入口,在Spring中應用的最普遍的一種設計模式,易於擴展
*
* @author eamon.zhang
* @date 2019-09-27 下午3:04
*/
public interface VideoPlayerFactory {
DisplayController createDisplayController();
UploadController createUploadController();
}複製代碼
而後在各個平臺建立具體的 UploadController
和DisplayController
:
建立適用於Windows的UploadController
和DisplayController
:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:09
*/
public class WindowsUploadController implements UploadController {
@Override
public void upload() {
System.out.println("Windows 上傳控制器!");
}
}
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:09
*/
public class WindowsDisplayController implements DisplayController {
@Override
public void display() {
System.out.println("Windows 上的播放器!");
}
}複製代碼
建立適用於IOS的UploadController
和DisplayController
:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:10
*/
public class IosUploaderController implements UploadController {
@Override
public void upload() {
System.out.println("IOS 上傳控制器!");
}
}
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:09
*/
public class IosDisplayController implements DisplayController {
@Override
public void display() {
System.out.println("IOS 上的播放器!");
}
}複製代碼
建立適用於Android的UploadController
和DisplayController
:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:10
*/
public class AndroidUploaderController implements UploadController {
@Override
public void upload() {
System.out.println("Android 上傳控制器!");
}
}
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:09
*/
public class AndroidDisplayController implements DisplayController {
@Override
public void display() {
System.out.println("Android 上的播放器!");
}
}複製代碼
在各平臺具體的工廠類中完成上傳控制器和播放控制器的建立過程:
建立WindowsFactory
類:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:15
*/
public class WindowsFactory implements VideoPlayerFactory {
@Override
public DisplayController createDisplayController() {
return new WindowsDisplayController();
}
@Override
public UploadController createUploadController() {
return new WindowsUploadController();
}
}複製代碼
建立IosFactory
類:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:17
*/
public class IosFactory implements VideoPlayerFactory {
@Override
public DisplayController createDisplayController() {
return new IosDisplayController();
}
@Override
public UploadController createUploadController() {
return new IosUploaderController();
}
}複製代碼
建立AndroidFactory
類:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:18
*/
public class AndroidFactory implements VideoPlayerFactory {
@Override
public DisplayController createDisplayController() {
return new AndroidDisplayController();
}
@Override
public UploadController createUploadController() {
return new AndroidUploaderController();
}
}複製代碼
來看客戶端調用:
/**
* @author eamon.zhang
* @date 2019-09-27 下午3:20
*/
public class VideoPlayerFactoryTest {
@Test
public void VideoPlayer() {
VideoPlayerFactory factory = new WindowsFactory();
// IOS
// factory = new IosFactory();
// // Android
// factory = new AndroidFactory();
UploadController uploadController = factory.createUploadController();
DisplayController displayController = factory.createDisplayController();
uploadController.upload();
displayController.display();
}
}複製代碼
以調用 Windows 爲例,結果:
Windows 上傳控制器!
Windows 上的播放器!複製代碼
上面就是針對不一樣平臺只經過建立對應的工廠對象就完成了上傳控制器和播放控制器的建立。抽象工廠很是完美清晰地描述這樣一層複雜的關係。可是,不知道你們有沒有發現,若是咱們再繼續擴展功能,將下載器也加入到產品中,那麼咱們的代碼從抽象工廠,到具體工廠要所有調整,很顯然不符合開閉原則。所以就有了上面優缺點中所說的缺點。
在實際應用中,咱們千萬不能犯強迫症甚至有潔癖。在實際需求中產品等級結構升級是很是正常的一件事情。咱們能夠根據實際狀況,只要不是頻繁升級,能夠不遵循開閉原則。代碼每半年升級一次或者每一年升級一次又有何不可呢?
源碼:github.com