設計模式詳解——工廠模式(二)

weixin_34050427發表於2017-02-28

本篇文章介紹一種設計模式——工廠模式。工廠模式是用來封裝物件的建立,減少應用程式和具體類之間的依賴,促進鬆耦合。根據工廠模式的應用特性,一共分為三種子模式:簡單工廠模式,工廠方法模式和抽象工廠模式。本篇文章主要介紹工廠方法模式。本篇文章內容參考工廠方法模式【原】從頭學習設計模式(三)——工廠方法模式

一、工廠方法模式簡介

1.定義

工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多型工廠(Polymorphic Factory)模式,它屬於類建立型模式。

在工廠方法模式中,工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件,這樣做的目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。

2.使用動機

現在對該系統(上篇文章提到)進行修改,不再設計一個按鈕工廠類來統一負責所有產品的建立,而是將具體按鈕的建立過程交給專門的工廠子類去完成。

我們先定義一個抽象的按鈕工廠類,再定義具體的工廠類來生成圓形按鈕、矩形按鈕、菱形按鈕等,它們實現在抽象按鈕工廠類中定義的方法。這種抽象化的結果使這種結構可以在不修改具體工廠類的情況下引進新的產品,如果出現新的按鈕型別,只需要為這種新型別的按鈕建立一個具體的工廠類就可以獲得該新按鈕的例項,這一特點無疑使得工廠方法模式具有超越簡單工廠模式的優越性,更加符合“開閉原則”。

二、工廠方法模式結構

1.模式結構
3985563-8ce4534a7a872a09.png

工廠方法模式包含如下角色:

●Product:抽象產品,工廠方法模式所建立的物件的超類,也就是所有產品類的共同父類或共同擁有的介面。在實際的系統中,這個角色也常常使用抽象類實現。

●ConcreteProduct:具體產品,這個角色實現了抽象產品(Product)所宣告的介面,工廠方法模式所建立的每一個物件都是某個具體產品的例項。

●Factory:抽象工廠,擔任這個角色的是工廠方法模式的核心,任何在模式中建立物件的工廠類必須實現這個介面。在實際的系統中,這個角色也常常使用抽象類實現。

●ConcreteFactory:具體工廠,擔任這個角色的是實現了抽象工廠介面的具體Java類。具體工廠角色含有與業務密切相關的邏輯,並且受到使用者的呼叫以建立具體產品物件。

2.時序圖
3985563-bb6ceace257078ed.png

①先呼叫具體工廠物件中的方法createProduct()

②根據傳入產品型別引數(也可以無參),獲得具體的產品物件

③返回產品物件並使用

三、工廠方法模式的使用例項

3985563-c5a708a3fbf93c26.png

上面的類圖中,在燈這個品類下,有燈泡燈管兩種產品,並且都實現了燈的通用方法:關燈和開燈。在工廠類下,有各種生產具體產品的子工廠負責生產相應的兩種燈具。

如果還不是太明白,那我們來假設一個情景。小明(客戶端)想要買一個燈泡,他不認識工廠,只能去供銷店(工廠類)買,於是和老闆說“我要一個燈泡”,老闆說 “沒問題!您稍等”。轉身到了後院,對生產燈泡的小弟(燈泡工廠子類)吆喝一聲,給我造個燈泡!不一會燈泡造好了,老闆拿給小明,“嘿嘿,燈泡給您作了一個,您試試?”,小明把燈泡擰在燈口上,開關了兩下(燈的通用方法)“嘿!挺好,沒問題!”,付了錢高高興興走了。
** 抽象的產品介面ILight**

 public interface ILight
    {
        void TurnOn();
        void TurnOff();
    }

具體的產品類:BulbLight

 public class BulbLight implements ILight
    {
        public void TurnOn()
        {
            Console.WriteLine("BulbLight turns on.");
        }
        public void TurnOff()
        {
            Console.WriteLine("BulbLight turns off.");
        }
    }

**具體的產品類:TubeLight **

 public class TubeLight implements ILight
    {
        public void TurnOn()
        {
            Console.WriteLine("TubeLight turns on.");
        }

        public void TurnOff()
        {
            Console.WriteLine("TubeLight turns off.");
        }

    }

** 抽象的工廠類**

public interface ICreator
    {
        ILight CreateLight();
    }

具體的工廠類:BulbCreator

 public class BulbCreator implements ICreator
    {
        public ILight CreateLight()
        {
            return new BulbLight();
        }

    }

具體的工廠類:TubeCreator

 public class TubeCreator implements ICreator
    {
        public ILight CreateLight()
        {
            return new TubeLight();
        }
    }

** 客戶端呼叫**

static void Main(string[] args)
        {
            //先給我來個燈泡
            ICreator creator = new BulbCreator();
            ILight light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();

            //再來個燈管看看
            creator = new TubeCreator();
            light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();

        }

通過一個引用變數ICreator來建立產品物件,建立何種產品物件由指向的具體工廠類決定。通過工廠方法模式,將具體的應用邏輯和產品的建立分離開,促進鬆耦合。

本例中每個具體工廠類只負責生產一種型別的產品,當然每個具體工廠類也內部可以維護少數幾種產品例項物件,類似於簡單工廠模式。

四、工廠方法模式的優缺點

優點

①在工廠方法模式中,工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節,使用者只需要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名。

②基於工廠角色和產品角色的多型性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多型工廠模式,是因為所有的具體工廠類都具有同一抽象父類。

③使用工廠方法模式的另一個優點是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的介面,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要新增一個具體工廠和具體產品就可以了。這樣,系統的可擴充套件性也就變得非常好,完全符合“開閉原則”,這點比簡單工廠模式更優秀。

缺點

①在新增新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和執行,會給系統帶來一些額外的開銷。

②由於考慮到系統的可擴充套件性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

適用場景

在以下情況下可以使用工廠方法模式:

①一個類不知道它所需要的物件的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品物件由具體工廠類建立;客戶端需要知道建立具體產品的工廠類。

②一個類通過其子類來指定建立哪個物件:在工廠方法模式中,對於抽象工廠類只需要提供一個建立產品的介面,而由其子類來確定具體要建立的物件,利用物件導向的多型性和里氏代換原則,在程式執行時,子類物件將覆蓋父類物件,從而使得系統更容易擴充套件。

③將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定,可將具體工廠類的類名儲存在配置檔案或資料庫中。

五、工廠方法模式在Java中應用

JDBC中的工廠方法:

Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");

相關文章