
造房子時需要一個門,你是穿上木匠服開始在你家門口鋸木頭,搞得一團糟?還是從工廠里生產(chǎn)一個?
依賴倒置原則,指導(dǎo)我們避免依賴具體類型,而要盡量依賴抽象。所有的工廠都是用來封裝對象的創(chuàng)建,以便將代碼從具體類解耦。
我們把三個工廠理一遍:
- 簡單工廠,是針對一種“類型”的抽象。
- 工廠方法,是針對一種“類型”,以及一種“創(chuàng)建方法”的抽象。
- 抽象工廠,是針對一組“類型”與“創(chuàng)建方法”的抽象,組內(nèi)每一套類型與創(chuàng)建方法一一對應(yīng)。
用造門這個例子來說:
- 簡單工廠,是封裝了“造門”的操作,輸出的是一種門。
- 工廠方法,是封裝了“多種造門”的操作,并委托“多家工廠”,輸出的是“各種門”。
- 抽象工廠,是封裝了“多種造門”的操作,“提供多種專業(yè)人員”的操作,并委托給“多家工廠”,輸出的是“各種門”,以及“各種專業(yè)人員”,且“門”與“專業(yè)人員”一一對應(yīng)。
附客戶端調(diào)用的代碼:
$door = DoorFactory::makeDoor(100, 200); // 客戶不需要先指定委托對象。傳遞門寬高,輸出一種規(guī)格的門
// 木門
$woodenDoorManager = new WoodenDoorManager();
$door = $woodenDoorManager->makeDoor(100, 200);
// 鐵門
$ironDoorManager = new IronDoorManager();
$door = $ironDoorManager->makeDoor(100, 200); // 客戶需要先指定委托對象。委托給*DoorManager,輸出"各種門"
// 木門
$woodenFactory = new WoodenDoorFactory();
$door = $woodenFactory->makeDoor(); // 輸出門
$expert = $woodenFactory->makeFittingExpert(); // 還輸出專業(yè)人員,且"門"與"專業(yè)人員"一一對應(yīng)。
$door->getDescription();
$expert->getDescription();
// 鐵門
$ironFactory = new IronDoorFactory();
$door = $ironFactory->makeDoor();
$expert = $ironFactory->makeFittingExpert();
$door->getDescription();
$expert->getDescription(); // 多種造門。委托給"多家工廠",輸出"各種門"。且"門"與"專業(yè)人員"一一對應(yīng)。
附分別的UML圖:
定義一個工廠類,它可以根據(jù)參數(shù)的不同返回不同類的實例,被創(chuàng)建的實例通常都具有共同的父類。

定義一個用于創(chuàng)建對象的接口,讓子類決定將哪一個類實例化。工廠方法模式讓一個類的實例化延遲到其子類。

提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無須指定它們具體的類。

工廠方法和簡單工廠有啥區(qū)別?
兩者最大的區(qū)別:抽象的維度。
- 簡單工廠的抽象,是一維的,它抽象的僅僅是所創(chuàng)建"類型"的接口;
- 而工廠方法的抽象,是二維的,它不僅抽象了所創(chuàng)建"類型"的接口,而且抽象了"方法"的接口。
具體到例子里,簡單工廠實例中,客戶就是要一個門, 而不關(guān)心創(chuàng)建過程, 最后實際創(chuàng)造的是一個木門。這個頗為諷刺,如果客戶要的是個鐵門呢?那就事與愿違了。
所以在這個例子里, 也是存在兩維抽象的。一是"門"這個類型的抽象,二是"造門"這個方法的抽象。簡單工廠只做到了前者,而沒有給出后者的解決方案,這才造成了客戶可能吃了啞巴虧。
如果我們按照工廠方法的思路,將門工廠造門這件事進(jìn)行細(xì)分,木門交給木門工廠,鐵門交給鐵門工廠。這就和工廠方法里的例子別無二致了??蛻粜枰戎付ㄎ袑ο螅魂P(guān)心具體怎么造門。
簡單工廠和抽象工廠有啥區(qū)別?
依然可以用維度來理解抽象工廠。 抽象工廠比工廠方法又多了一維。
例子中,抽象工廠提供了兩套“類型 - 創(chuàng)建操作”(分別是"門 - 造門","專業(yè)人員 - 提供專業(yè)人員"),其實這個個數(shù)是無限的。
你可以提供 n 套這樣的對應(yīng)關(guān)系,然后委托給相關(guān)的工廠。這就是“工廠們的工廠”的具體含義。