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

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

一、工廠方法模式簡介

1.定義

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

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

2.使用動機

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

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

二、工廠方法模式結構

1.模式結構


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

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

●ConcreteProduct:具體產品,這個角色實現了抽象產品(Product)所聲明的接口,工廠方法模式所創建的每一個對象都是某個具體產品的實例。

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

●ConcreteFactory:具體工廠,擔任這個角色的是實現了抽象工廠接口的具體Java類。具體工廠角色含有與業務密切相關的邏輯,並且受到使用者的調用以創建具體產品對象。

2.時序圖


①先調用具體工廠對象中的方法createProduct()

②根據傳入產品類型參數(也可以無參),獲得具體的產品對象

③返回產品對象並使用

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


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

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

抽象的產品接口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");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章