設計模式:工廠方法模式(Factory Method)和抽象工廠模式(Abstact Factory)


 在面向對象編程中, 最通常的方法是一個new操作符產生一個對象實例,new操作符就是用來構造對象實例的。但是在一些情況下, new操作符直接生成對象會帶來一些問題。舉例來說, 許多類型對象的創造需要一系列的步驟: 你可能需要計算或取得對象的初始設置; 選擇生成哪個子對象實例; 或在生成你需要的對象之前必須先生成一些輔助功能的對象。 在這些情況,新對象的建立就是一個 “過程”,不僅是一個操作,像一部大機器中的一個齒輪傳動。


分類

 工廠模式主要是為創建對象提供過渡接口,以便將創建對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。
 工廠模式可以分為三類:

  1. 簡單工廠模式(Simple Factory)
  2. 工廠方法模式(Factory Method)
  3. 抽象工廠模式(Abstract Factory)

 這三種模式從上到下逐步抽象,並且更具一般性。
 GOF在《設計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。


簡單工廠模式

 簡單工模式時類的創建模式,又叫做靜態工廠方法(static Factory Method)。簡單工廠模式是一個工廠對象決定創建出哪一種產品類的實例。它存在的目的很簡單:定義一個創建對象的接口。
 組成:

  1. 工廠類角色:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由一個具體類實現。
  2. 抽象產品角色:它一般是具體產品繼承的父類或者實現的接口。在java中由接口或者抽象類來實現。
  3. 具體產品角色:工廠類所創建的對象就是此角色的實例。在java中由一個具體類實現。

 舉個簡單例子:
1 抽象產品角色

public interface IProduct
{
public void method();
}

2 具體產品角色(可以把方法定義為static的)

public class ProductA implements IProduct
{

@Override
public void method()
{
System.out.println("I'm ProductA!");
}
}

public class ProductB implements IProduct
{

@Override
public void method()
{
System.out.println("I'm ProductB!");
}
}

3 工廠類角色

public class SimpleFactory
{
public IProduct produce(String type)
{
if("A".equals(type))
{
return new ProductA();
}
else if("B".equals(type))
{
return new ProductB();
}
else
{
System.out.println("請輸入正確的類型");
return null;
}
}
}

4 測試類

public class MainTest
{
public static void main(String args[])
{
SimpleFactory factory = new SimpleFactory();
IProduct product = factory.produce("A");
product.method();
}
}

輸出:I’m ProductA!

 在Java中java.text.DateFormat就是簡單工廠模式的典型案例。
優點:專門定義一個工廠類負責創建其他類的實例,最大的優點在於工廠類中包含了必要的邏輯,根據客戶需要的條件動態實例化相關的類。
缺點:當需要增加一種產品時,比如ProductC就需要修改簡單工廠類SimpleFactory(增加if-else塊),這違背了開閉原則。

TIPS
 其實如果采用反射機制實現簡單工廠並沒有違背開閉原則。
 利用反射機制,將簡單工廠類改成:

public class SimpleFactory
{
public IProduct produce(Class<? extends IProduct> c) throws Exception
{

return (IProduct)Class.forName(c.getName()).newInstance();
// return (IProduct)c.newInstance(); //或者采用這種方法
}
}

 測試類:

public class MainTest
{
public static void main(String args[]) throws Exception
{
SimpleFactory factory = new SimpleFactory();
IProduct product = factory.produce(ProductA.class);
product.method();
}
}

 這樣當有新的產品時,其實並不需要修改工廠類。《Effective Java(Second Edition)》中明確指出:通常,普通應用程序在運行時不應該以反射方式訪問對象。所以本篇文章建立在不采用反射機制的情況下,在下面介紹的工廠方法模式其實也可以改用反射機制實現,博主就不展示了。至於《Effective Java(Second Edition)》為什么不推薦反射機制,可以參考此書的“接口優先於反射機制”這一主題,這里不贅述。


工廠方法模式

 工廠方法模式是簡單工廠模式的進一步抽象化和推廣,工廠方法模式里不再只由一個工廠類決定那一個產品類應當被實例化,這個決定被交給抽象工廠的子類去做。
這里寫圖片描述
 來看下它的組成:
1. 抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現的接口或者必須繼承的父類。在java中它由抽象類或者接口來實現。
2. 具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以創建對應的具體產品的對象。
3. 抽象產品角色:它是具體產品繼承的父類或者是實現的接口。在java中一般有抽象類或者接口來實現。
4. 具體產品角色:具體工廠角色所創建的對象就是此角色的實例。在java中由具體的類來實現。
 舉個簡單例子:
1 抽象工廠角色

public interface IFactory
{
public IProduct produce();
}

2 具體工廠角色

public class ConcreteFactoryA implements IFactory
{

@Override
public IProduct produce()
{
return new ProductA();
}
}

public class ConcreteFactoryB implements IFactory
{

@Override
public IProduct produce()
{
return new ProductB();
}
}

3 抽象產品角色(和簡單工廠的一樣)

public interface IProduct
{
public void method();
}

4 具體產品角色(和簡單工廠的一樣)

public class ProductA implements IProduct
{

@Override
public void method()
{
System.out.println("I'm ProductA!");
}
}

public class ProductB implements IProduct
{

@Override
public void method()
{
System.out.println("I'm ProductB!");
}
}

5 測試代碼:

public class MainTest
{
public static void main(String[] args)
{
IFactory factoryA = new ConcreteFactoryA();
IProduct product1 = factoryA.produce();
product1.method();
}
}

輸出:I’m ProductA!

工廠方法模式和簡單工廠模式在定義上的不同是很明顯的。工廠方法模式的核心是一個抽象工廠類,而不像簡單工廠模式, 把核心放在一個實類上。工廠方法模式可以允許很多實的工廠類從抽象工廠類繼承下來, 從而可以在實際上成為多個簡單工廠模式的綜合,從而推廣了簡單工廠模式。
工廠方法相比於簡單工廠模式的優點是增加一個產品,只需要增加一個具體工廠類和具體產品類,沒有修改原先的工廠類,符合開閉原則。缺點是客戶端的代碼會需要修改(簡單工廠模式的客戶端不需要修改),隨着產品的繼續增加,所要實現的類的個數也會隨之增大。


抽象工廠模式

 在抽象工廠模式中,抽象產品 (AbstractProduct) 可能是一個或多個,從而構成一個或多個產品族(Product Family)。 在只有一個產品族的情況下,抽象工廠模式實際上退化到工廠方法模式。
這里寫圖片描述

工廠方法模式 VS 抽象工廠模式
工廠方法模式:一個抽象產品類,可以派生出多個具體產品類。每個具體工廠類只能創建一個具體產品類的實例。
抽象工廠模式:多個抽象產品類,每個抽象產品類可以派生出多個具體產品類。一個抽象工廠類可以派生出多個具體工廠類。每個具體工廠類可以創建多個具體產品的實例。
區別:工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。工廠方法模式的具體工廠類只能創建一個具體產品類的實例,而抽象工廠模式可以創建多個。

舉個簡單例子:
1 抽象產品角色

public interface AbstractProductA
{
public void produceA();
}
public interface AbstractProductB
{
public void produceB();
}

2 抽象工廠角色

public interface AbstractFactory
{
public AbstractProductA CreateProductA();
public AbstractProductB CreateProductB();
}

3 具體產品角色

public class ProductA1 implements AbstractProductA
{

@Override
public void produceA()
{
System.out.println("Im ProductA1!");
}
}
public class ProductA2 implements AbstractProductA
{

@Override
public void produceA()
{
System.out.println("Im ProductA2!");
}
}
public class ProductB1 implements AbstractProductB
{

@Override
public void produceB()
{
System.out.println("Im ProductB1!");
}
}
public class ProductB2 implements AbstractProductB
{

@Override
public void produceB()
{
System.out.println("Im ProductB2!");
}
}

4 具體工廠角色

public class ConcreteFactory1 implements AbstractFactory
{

@Override
public AbstractProductA CreateProductA()
{
return new ProductA1();
}

@Override
public AbstractProductB CreateProductB()
{
return new ProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory
{

@Override
public AbstractProductA CreateProductA()
{
return new ProductA2();
}

@Override
public AbstractProductB CreateProductB()
{
return new ProductB2();
}
}

5 測試代碼

public class MainTest
{
public static void main(String[] args)
{
AbstractFactory factory = new ConcreteFactory1();
AbstractProductA product1 = factory.CreateProductA();
AbstractProductB product2 = factory.CreateProductB();
product1.produceA();
product2.produceB();
}
}

輸出結果:

Im ProductA1!
Im ProductB1!

 抽象工廠的優點:抽象工廠模式除了具有工廠方法模式的優點外,最主要的優點就是可以在類的內部對產品族進行約束。所謂的產品族,一般或多或少的都存在一定的關聯,抽象工廠模式就可以在類內部對產品族的關聯關系進行定義和描述,而不必專門引入一個新的類來進行管理。
 抽象工廠的缺點:產品族的擴展將是一件十分費力的事情,假如產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改。所以使用抽象工廠模式時,對產品等級結構的划分是非常重要的。

Jdk中的工廠方法
java.lang.Object#toString();
java.lang.Class#newInstance();
java.lang.Class#forName();
java.lang.Boolean#valueOf();
java.lang.Proxy#newProxyInstance();
java.lang.reflect.Array#newInstance();
java.lang.reflect.Constructor#newInstance();
java.util.concurrent.Executors#newCachedThreadPool()等


總結

 無論是簡單工廠模式,工廠方法模式,還是抽象工廠模式,他們都屬於工廠模式,在形式和特點上也是極為相似的,他們的最終目的都是為了解耦。在使用時,我們不必去在意這個模式到底工廠方法模式還是抽象工廠模式,因為他們之間的演變常常是令人琢磨不透的。經常你會發現,明明使用的工廠方法模式,當新需求來臨,稍加修改,加入了一個新方法后,由於類中的產品構成了不同等級結構中的產品族,它就變成抽象工廠模式了;而對於抽象工廠模式,當減少一個方法使的提供的產品不再構成產品族之后,它就演變成了工廠方法模式。所以,在使用工廠模式時,只需要關心降低耦合度的目的是否達到了。


參考資料:
1. 《設計模式(一)工廠模式Factory(創建型)
2. 《學習:java設計模式—工廠模式
3. 《23種設計模式(3):抽象工廠模式
4. 《Effective Java(Second Edition)》Joshua Bloch.
5. 《細數JDK里的設計模式


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com