簡單工廠模式(Simple Factory Pattern)又稱靜態方法工廠模式(Static Methods Factory Pattern),屬於建構式模式(Creational Patterns),目的是用來建構物件。
工廠模式又分為簡單工廠模式(Simple Factory Pattern),工廠方法模式(Factory Method Pattern)及抽象工廠模式(Abstract Factory Pattern)。
所謂的工廠(Factory)是指用來建立物件或產生實例的類別;換句話說,一個專門用來建立物件的類別就叫做工廠類別(Factory Class)。
工廠模式的主要概念為封裝物件建構的邏輯,也就是不要讓客戶端直接以new
的方式建立物件,而是把建立物件的邏輯抽取出來放在工廠類別。
工廠類別提供用來建立物件的靜態方法,該方法依傳入參數產生不同的物件。
例如下面Loader
介面有二個實作類別XMLLoader
及JSONLoader
負責載入不同格式的檔案。
Loader
package com.abc.demo.loader;
public interface Loader {
void load();
}
XMLLoader
用來載入xml檔。
XMLLoader
package com.abc.demo.loader;
public class XMLLoader implements Loader {
XMLLoader() {
// 複雜的建構邏輯
}
@Override
public void load() {
System.out.println("Load from XML");
}
}
JSONLoader
用來載入json檔。
JSONLoader
package com.abc.demo.loader;
public class JSONLoader implements Loader {
JSONLoader() {
// 複雜的建構邏輯
}
@Override
public void load() {
System.out.println("Load from JSON");
}
}
原本要載入檔案時是直接在程式中用new
建立所需的Loader,例如:
package com.abc.demo;
import com.abc.demo.loader.Loader;
import com.abc.demo.loader.XMLLoader;
public class Main {
public static void main(String[] args) {
// 建構物件所需參數的複雜邏輯
Loader loader = new XMLLoader(); // 直接用new建立物件,把所需參數傳入
loader.load();
}
}
然而可能碰到以下問題:
- 若類別的名稱改了,例如
XMLLoader
改為DOMLoader
,則程式中所有new XMLLoader()
的地方都要改為new DOMLoader()
(不過IDE如Eclipse的Refactor -> Rename
可快速修改名稱)。 - 原本建構式不需參數,但若將來要傳入參數,或是建構物件的邏輯改了,則程式中有所有呼叫建構式的地方都要修改。
- 直接用
new
建構物件會把建構邏輯細節混雜在客戶端的業務邏輯中。客戶端不需知道建立物件的細節,因為只是要取得實例來用而已。 - 同性質類別(例如繼承同介面,擁有相同的方法)建構物件的邏輯可集中在同一處,不應該讓客戶端隨意建構物件。
- Java的建構式名稱必須與類別名稱相同,但有時建構式的名稱無法充分反映建構式的行為。
而Simple Factory Pattern則是用來解決以上問題。我們將建構Loader
具體類new XMLLoader()
實例的動作封裝在一個工廠類別中,當需要實例時不直接用new
,而是則透過工廠類別的靜態方法來建構物件,客戶端不需知道建構時例的細節。
JDK慣例用來建構有共同介面的類別實例的工廠類是不可實例化(noninstantiable)類別,且工廠類的名稱為介面的名稱加s。典型的例子為java.util.Collections
,其提供各種建構繼承Collection
介面的類別如List
、Set
的實例。因此Loader工廠類別命名為Loaders
,靜態方法getLoader()
依傳入參數決定要建構的實例。
Loaders (Loader Factory)
package com.abc.demo.loader;
public class Loaders {
private Loaders() { // private建構式無法被實例化
throw new AssertionError("Factory class cannot be instantiated")
}
public static Loader getLoader(LoaderType loaderType) {
switch (loaderType) {
case XML: {
// 建構物件所需參數的複雜邏輯
return new XMLLoader(...);
}
case JSON: {
// 建構物件所需參數的複雜邏輯
return new JSONLoader(...);
}
default: {
return null;
}
}
}
public enum LoaderType {
XML, JSON
}
}
在客戶端建立Loader
的方式改為透過Loaders.getLoader()
產生。
package com.abc.demo;
import com.abc.demo.loader.Loader;
import com.abc.demo.loader.factory.LoaderFactory;
public class Main {
public static void main(String[] args) {
Loader loader = Loaders.getLoader(LoaderFactory.LoaderType.JSON);
loader.load();
}
}
透過Loaders
工廠建立物件,則未來如果需要修改類別名稱、修改建構式參數、更換類別或邏輯等就只要修改getLoader()
中的程式就好。
但未來如果要擴充其他的Loader,例如YAMLLoader
,則必須修改LoaderFactory.getLoader()
的switch case
,這樣違反了SOLID的開放封閉原則(Open/closed principle),添加新功能時不應該修改原有的程式碼。所以進一步把工廠建構不同類別的程序抽取出來成為工廠方法模式(Factory Method Pattern)。
沒有留言:
張貼留言