1. 程式人生 > >在Java中應用工廠(Factory)模式

在Java中應用工廠(Factory)模式

基本概念
Factory Method是一種建立性模式,它定義了一個建立物件的介面,但是卻讓子類來決定具體例項化哪一個類.當一個類無法預料要建立哪種類的物件或是一個類需要 由子類來指定建立的物件時我們就需要用到Factory Method 模式了.簡單說來,Factory Method可以根據不同的條件產生不同的例項,當然這些不同的例項通常是屬於相同的型別,具有共同的父類.Factory Method把建立這些例項的具體過程封裝起來了,簡化了客戶端的應用,也改善了程式的擴充套件性,使得將來可以做最小的改動就可以加入新的待建立的類. 通常我們將Factory Method作為一種標準的建立物件的方法,當發現需要更多的靈活性的時候,就開始考慮向其它建立型模式轉化

簡單分析
圖1是Factory Method 模式的結構圖,這裡提供了一些術語,讓我們可以進行更方便的描述:

  1. Product: 需要建立的產品的抽象類.
  2. ConcreteProduct: Product的子類,一系列具體的產品.
  3. Creator: 抽象建立器介面,宣告返回Product型別物件的Factory Method.
  4. ConcreteCreator: 具體的建立器,重寫Creator中的Factory Method,返回ConcreteProduct型別的例項.

圖1: Factory Method 模式結構
圖1: Factory Method 模式結構

由此可以清楚的看出這樣的平行對應關係: Product <====> Creator ; ConreteProduct <====> ConreteCreator

抽象產品對應抽象建立器,具體產品對應具體建立器.這樣做的好處是什麼呢?為什麼我們不直接用具體的產品和具體的建立器完成需求呢?實際上我們也可 以這樣做.但通過Factory Method模式來完成,客戶(client)只需引用抽象的Product和Creater,對具體的ConcreteProduct和 ConcreteCreator可以毫不關心,這樣做我們可以獲得額外的好處:

  • 首先客戶端可以統一從抽象建立器獲取產生的例項,Creator的作用將client和產品建立過程分離開來,客戶不用操心返回的是那一個具體的 產品,也不用關心這些產品是如何建立的.同時,ConcreteProduct也被隱藏在Product後面,ConreteProduct繼承了 Product的所有屬性,並實現了Product中定義的抽象方法,按照Java中的物件造型(cast)原則,通過ConcreteCreator產 生的ConcreteProduct可以自動的上溯造型成Product.這樣一來,實質內容不同的ConcreteProduct就可以在形式上統一為 Product,通過Creator提供給client來訪問.
  • 其次,當我們新增一個新的ConcreteCreator時,由於Creator所提供的介面不變,客戶端程式不會有絲毫的改動,不 會帶來動一發而牽全身的災難, 這就是良好封裝性的體現.但如果直接用ConcreteProduct和ConcreteCreator兩個類是無論如何也做不到這點的. 優良的面向物件設計鼓勵使用封裝(encapsulation)和委託(delegation),而Factory Method模式就是使用了封裝和委託的典型例子,這裡封裝是通過抽象建立器Creator來體現的,而委託則是通過抽象建立器把建立物件的責任完全交給 具體建立器ConcreteCreator來體現的.

現在,請再回頭看看基本概念中的那段話,開始也許覺得生澀難懂,現在是不是已經明朗化了很多.

下面讓我們看看在 Java 中如何實現Factory Method模式,進一步加深對它的認識.

具體實施
先說明一點,用Factory Method模式建立物件並不一定會讓我們的程式碼更短,實事上往往更長,我們也使用了更多的類,真正的目的在於這樣可以靈活的,有彈性的建立不確定的對 象.而且,程式碼的可重用性提高了,客戶端的應用簡化了,客戶程式的程式碼會大大減少,變的更具可讀性.

  1. 標準實現: 這裡我採用Bruce Eckel 用來描述OO思想的經典例子 Shape.這樣大家會比較熟悉一些.我完全按照圖1中所定義的結構寫了下面的一段演示程式碼.這段程式碼的作用是建立不同的Shape例項,每個例項完成兩 個操作:draw和erase.具體的建立過程委託給ShapeFactory來完成.

    1.a 首先定義一個抽象類Shape,定義兩個抽象的方法.


    abstract class Shape {
    // 勾畫shape
    public abstract void draw();
    // 擦去 shape
    public abstract void erase();

    public String name;
    public Shape(String aName){
    name = aName;
    }
    }

    1.b 定義 Shape的兩個子類: Circle, Square,實現Shape中定義的抽象方法


    // 圓形子類
    class Circle extends Shape {
    public void draw() {
    System.out.println("It will draw a circle.");
    }
    public void erase() {
    System.out.println("It will erase a circle.");
    }
    // 建構函式
    public Circle(String aName){
    super(aName);
    }
    }

    // 方形子類
    class Square extends Shape {
    public void draw() {
    System.out.println("It will draw a square.");
    }
    public void erase() {
    System.out.println("It will erase a square.");
    }
    // 建構函式
    public Square(String aName){
    super(aName);
    }
    }

    1.c 定義抽象的建立器,anOperation呼叫factoryMethod建立一個物件,並對該物件進行一系列操作.


    abstract class ShapeFactory {
    protected abstract Shape factoryMethod(String aName);
    // 在anOperation中定義Shape的一系列行為
    public void anOperation(String aName){
    Shape s = factoryMethod(aName);
    System.out.println("The current shape is: " + s.name);
    s.draw();
    s.erase();
    }
    }

    1.d 定義與circle和square相對應的兩個具體建立器CircleFactory,SquareFactory,實現父類的methodFactory方法


    // 定義返回 circle 例項的 CircleFactory
    class CircleFactory extends ShapeFactory {
    // 過載factoryMethod方法,返回Circle物件
    protected Shape factoryMethod(String aName) {
    return new Circle(aName + " (created by CircleFactory)");
    }
    }

    // 定義返回 Square 例項的 SquareFactory
    class SquareFactory extends ShapeFactory {
    // 過載factoryMethod方法,返回Square物件
    protected Shape factoryMethod(String aName) {
    return new Square(aName + " (created by SquareFactory)");
    }
    }

    1.e 測試類:請注意這個客戶端程式多麼簡潔,既沒有羅嗦的條件判斷語句,也無需關心ConcreteProduct和ConcreteCreator的細節 (因為這裡我用anOperation封裝了Product裡的兩個方法,所以連Product的影子也沒看見,當然把Product裡方法的具體呼叫放 到客戶程式中也是不錯的).


    class Main {
    public static void main(String[] args){
    ShapeFactory sf1 = new SquareFactory();
    ShapeFactory sf2 = new CircleFactory();
    sf1.anOperation("Shape one");
    sf2.anOperation("Shape two");
    }
    }

    執行結果如下:
    The current shape is: Shape one (created by SquareFactory)
    It will draw a square.
    It will erase a square.
    The current shape is: Shape two (created by CircleFactory)
    It will draw a circle.
    It will erase a circle.

  2. 引數化的Factory Method: 這種方式依靠指定的引數作為標誌來建立對應的例項,這是很常見的一種辦法.比如JFC中的BorderFactory就是個很不錯的例子. 以下的這個例子是用字串作為標記來進行判斷的,如果引數的型別也不一樣,那就可以用到過載函式來解決這個問題,定義一系列引數和方法體不同的同名函式, 這裡java.util.Calendar.getInstance()又是個極好的例子.引數化的建立方式克服了Factory Method模式一個最顯著的缺陷,就是當具體產品比較多時,我們不得不也建立一系列與之對應的具體構造器. 但是在客戶端我們必須指定引數來決定要建立哪一個類.

    2.a 我們在第一種方法的基礎上進行修改,首先自定義一個的異常,這樣當傳入不正確的引數時可以得到更明顯的報錯資訊.


    class NoThisShape extends Exception {
    public NoThisShape(String aName) {
    super(aName);
    }
    }

    2.b去掉了ShapeFactory的兩個子類,改為由ShapeFactory直接負責例項的建立. ShapeFactory自己變成一個具體的建立器,直接用引數化的方法實現factoryMethod返回多種物件.


    abstract class ShapeFactory {
    private static Shape s;
    private ShapeFactory() {}

    static Shape factoryMethod(String aName, String aType) throws NoThisShape{
    if (aType.compareTo("square")==0)
    return new Square(aName);
    else if (aType.compareTo("circle")==0)
    return new Circle(aName);
    else throw new NoThisShape(aType);
    }

    // 在anOperation中定義Shape的一系列行為
    static void anOperation(String aName, String aType) throws NoThisShape{
    s = factoryMethod(aName, aType);
    System.out.println("The current shape is: " + s.name);
    s.draw();
    s.erase();
    }
    }

    2.c 測試類:這裡客戶端必須指定引數來決定具體建立哪個類.這個例子裡的anOperation是靜態函式,可以直接引用.


    class Main {
    public static void main(String[] args) throws NoThisShape{
    ShapeFactory.anOperation("Shape one","circle");
    ShapeFactory.anOperation("Shape two","square");
    ShapeFactory.anOperation("Shape three", "delta");
    }
    }

    執行結果如下:


    The current shape is: Shape one
    It will draw a circle.
    It will erase a circle.
    The current shape is: Shape two
    It will draw a square.
    It will erase a square.
    Exception in thread "main" NoThisShape: delta
    at ShapeFactory.factoryMethod(ShapeFactory.java:10)
    at ShapeFactory.anOperation(ShapeFactory.java:15)
    at Main.main(Main.java:5)
  3. 動態裝載機制:

    有的時候我們會把ConcreteProduct的例項傳給建立器作為引數,這種情況下,如果在建立器裡完成建立過程,就必須判斷引數的具體型別(用instanceof),然後才能產生相應的例項,那麼比較好的做法是利用Java的動態裝載機制來完成這件事.比如:

    我們得到一個Shape的子類s,但不知道具體是那個子類,就可以利用Class類自帶的方法newInstance()得到例項

    return (Shape)s.getClass().newInstance();

    這種方法有興趣得讀者可以自己嘗試,限於篇幅,不寫具體程式碼出來了.

後話:
看完這篇文章後,相信讀者對Factory Method模式有一個比較清楚的瞭解了.我想說的是,我們不僅應該關心一個具體的模式有什麼作用,如何去實現這個模式,更應該透過現象看本質,不但知其 然,還要知其所以然.要通過對模式的學習加深對面向物件思想的理解,讓自己的認識得到昇華.Factory Method模式看似簡單,實則深刻.抽象,封裝,繼承,委託,多型,針對介面程式設計等面向物件中的概念都在這裡得到了一一的體現.只有抓住了它的本質,我 們才能夠不拘於形式的靈活運用,而不是為了使用模式而使用模式.

參考資料

相關推薦

Java應用工廠(Factory)模式

基本概念 Factory Method是一種建立性模式,它定義了一個建立物件的介面,但是卻讓子類來決定具體例項化哪一個類.當一個類無法預料要建立哪種類的物件或是一個類需要 由子類來指定建立的物件時我們就需要用到Factory Method 模式了.簡單說來,Factory M

Java工廠設計模式

  Java中的工廠設計模式   歡迎到工廠設計模式在Java教程。Factory Pattern是Creational Design模式之一,它在JDK以及Spring和Struts等框架中得到廣泛應用。 目錄[ 隱藏 ] 1工廠設

淺談javadao工廠設計模式

<?xml version="1.0"?> <config>     <daos>        <!-- 組織機構服務介面實現類 -->        <dao id="organizationService"            type="

java工廠方法模式(先把模板方法模式看透)

public class DatabaseFactory extends Factory { private Database database; @Override public Database createDatabase(String databasetype) {

Java工廠模式

ride 依賴倒轉原則 spa 接口隔離 負責 展開 具體實現 sta 開放 設計模式遵循原則 開閉原則:對擴展開放,對修改關閉 裏氏代換原則:只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被覆用。而衍生類也能夠在基類的基礎上增加新的行為 依賴倒轉原

Java的單例模式工廠模式、介面回撥、異常

for迴圈:起點為基本資料型別,包括boolean . equals():重寫原因,希望在地址不同但內容相同時也能返回true。 匿名物件:直接new出物件,不需要物件名來接收。 new Person().show(); 內部類:類

設計模式(二)--- java工廠模式

工廠模式 簡單工廠模式是屬於建立型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠物件決定創建出哪一種產品類的例項。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。

Java常見的設計模式---簡單工廠模式工廠方法模式和單例模式

在講設計模式之前,我們首先來說一下面向物件思想的設計原則,在實際的開發中,我們要想更深入的瞭解面向物件思想,就必須熟悉前人總結過的面向物件的思想的設計原則:1.單一職責原則:“高內聚,低耦合”,也就是說,每個類應該只有一個職責,對外只能提供一種功能,而引起類變化的原因應該只有

JAVA工廠模式和單例模式講解

轉載:http://blog.csdn.net/fangleijiang/article/details/19912667 1.單例模式 單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個例項而且該例

java幾種設計模式(單例模式,介面卡模式,簡單工廠模式

1、單例模式:也分餓漢式單例模式(建立物件)與懶漢式單例模式(未建立物件)程式碼實現:餓漢式單例模式:懶漢式單例模式:2、介面卡模式:介面:實現介面的類:實現介面某個方法的類:3、簡單工廠模式:介面:類1:類2:工廠類:測試類:

Java 常用的設計模式?說明工廠模式

Java 中的 23 種 設 計 模 式 :Factory( 工 廠 模 式 ),Builder( 建 造 模 式 ),Factory Method(工廠方法模式),Prototype(原始模型模

java的單例模式

javajava中的單例模式Java中單例模式是一種常見的設計模式,要求保證一個類僅有一個實例,並提供一個訪問他的全局訪問點具體要求:一、構造方法私有化;二、聲明一個本類對象;三、給外部提供一個靜態方法,獲取對象實例兩種實現方式:1.餓漢式故名之意:先創建實例,class sington1{ pr

在 js 應用 訂閱釋出模式(subscrib/public)

什麼是釋出-訂閱者模式 我們在使用釋出-訂閱者模式之前,先了解什麼是釋出-訂閱者模式。簡單來說,釋出訂閱者模式就是一種一對多的依賴關係。多個訂閱者(一般是註冊的函式)同時監聽同一個資料物件,當這個資料物件發生變化的時候會執行一個釋出事件,通過這個釋出事件會通知到所有的訂閱者,使它

設計模式(9)----- 補充spring工廠設計模式(手寫)

package com.DesignPatterns.ad.factory6; public interface BeanFactory { Object getBean(String id); }     package com.DesignPattern

Java23種設計模式(附代碼樣例)

體會 如何解決 熱插拔 原型 原型模式 strac println template sendmai 一、設計模式分類總體來說設計模式分為三大類:創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。結構型模式,共七種:適配器模式、裝飾器模式、代理

二、Java的單例模式

1、簡介 單例模式作為物件的一種建立方式,它的作用是確保某一個類在整個系統中只有一個例項,而且自行例項化並向系統中提供這個例項。 單例模式具有如下特點: 單例類只能有一個例項。 單例類必須自己建立自己的例項。 單例類必須給其他所有的物件提供這一個例項。 餓漢式單例類 /**

python工廠設計模式

簡單工廠模式相當於是一個4s店中有各種汽車,建立在一個類中,客戶無需知道具體汽車型號,只需要知道汽車類所對應的引數即可。但是工廠的職責過重,而且當型別過多時不利於系統的擴充套件維護。 #現代汽車4s店類 class CarStore(object): #示例方法

Java關於單例模式的一些理解

單例模式是常見的設計模式之一,又細分為餓漢模式和懶漢模式,比較重要。 什麼是單例模式: 單例模式是為了避免重複生成同一個例項物件,而產生的一種模式。因為物件在記憶體中佔據空間比較大,相對而言,更希望對於一個已有的物件,不必要每次都新建一個相同的物件。多個變數指向同一個物件

JAVA的觀察者模式例項教程

觀察者模式例子 在此例中,我們將完成一個簡單的主題討論,觀察者能夠註冊此主題。任何在此主題上的內容提交導致的變化都會通知所有在註冊的觀察者。 基於Subject被觀察者的需求,這個是實現一個基本的Subject介面,此介面定了一系列具體的方法需要在隨後實現介面的具體類中被實現。 package com.jo

Java 的單例模式,看完這一篇就夠了

單例模式是最常見的一個模式,在Java中單例模式被大量的使用。這同樣也是我在面試時最喜歡提到的一個面試問題,然後在面試者回答後可以進一步挖掘其細節,這不僅檢查了關於單例模式的相關知識,同時也檢查了面試者的編碼水平、多執行緒方面的知識,這些在實際的工作中非常重要。 在這個