工廠方法模式

工廠模式的定義

工廠模式使用的頻率非常高撼玄,我們在開發(fā)中總能見到它們的身影。其定義為:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

即定義一個用于創(chuàng)建對象的接口撞叽,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類摆马。

工廠方法模式的通用類圖如下所示:

工廠方法模式通用類圖

如圖所示停蕉,Product抽象類負責定義產(chǎn)品的共性,實現(xiàn)對事物最抽象的定義雷滚,Creator為抽象工廠類需曾,具體如何創(chuàng)建產(chǎn)品類由具體的實現(xiàn)工廠ConcreteCreator來完成。我們來看一下通用的模板代碼:

public abstract class Product {
    public void method() { //產(chǎn)品類的公共方法祈远,已經(jīng)實現(xiàn)
        //實現(xiàn)了公共的邏輯
    }
    public abstract void method2(); //非公共方法呆万,需要子類具體實現(xiàn)
}

具體產(chǎn)品類可以有多個,都繼承與抽象類Product车份,如下:

public class ConcreateProduct1 extends Product {
    @Override
    public void method2() {
        //product1的業(yè)務(wù)邏輯
    }
}
public class ConcreateProduct2 extends Product {
    @Override
    public void method2() {
        //product2的業(yè)務(wù)邏輯
    }
}

抽象工廠類負責定義產(chǎn)品對象的產(chǎn)生谋减,代碼如下:

public abstract class Creator {
    //創(chuàng)建一個產(chǎn)品對象,其輸入?yún)?shù)類型可以自行設(shè)置
    public abstract <T extends Product> T createProduct(Class<T> clazz);
}

這里用的是泛型扫沼,傳入的對象必須是Product抽象類的實現(xiàn)類出爹。具體如何產(chǎn)生一個產(chǎn)品的對象,是由具體工廠類實現(xiàn)的缎除,具體工廠類繼承這個抽象工廠類:

public class ConcreteCreator extends Creator {
 
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) { //異常處理
            e.printStackTrace();
        }
        return (T) product;
    }
}

通過這樣的設(shè)計严就,我們就可以在測試類中隨意生產(chǎn)產(chǎn)品了,看下面的測試類:

public class FactoryTest {
 
    public static void main(String[] args) {
        Creator factory = new ConcreteCreator();
        Product product1 = factory.createProduct(ConcreteProduct1.class); //通過不同的類創(chuàng)建不同的產(chǎn)品
        Product product2 = factory.createProduct(ConcreteProduct2.class);
         /*
          * 下面繼續(xù)其他業(yè)務(wù)處理
          */
     }
}

下面舉個女媧造人的例子闡述一下工廠模式的實際應(yīng)用器罐。

用女蝸造人闡述工廠模式

現(xiàn)在女媧要造人梢为,她要造三種人:白種人、黃種人和黑種人。怎么造呢抖誉?她得有個能產(chǎn)生人類的工廠吧(類似于八卦爐的東西),這個工廠得讓她生產(chǎn)出不同的人種衰倦。每個人都有兩個屬性:皮膚顏色和說話袒炉。那現(xiàn)在我們開始設(shè)計女蝸造人的程序,首先我們看一下造人的類圖:

女媧造人類圖

抽象接口Human是人類樊零,里面有兩個方法我磁,getColor獲得皮膚顏色,talk交談驻襟。下面三個具體Human的實現(xiàn)類夺艰,分別實現(xiàn)三個人種。根據(jù)工廠模式沉衣,應(yīng)該有個抽象工廠郁副,AbstractHumanFactory就擔當了這個責任,里面有個抽象方法createHuman豌习,那HumanFactory就是實現(xiàn)類了存谎,實現(xiàn)抽象工廠里的方法。下面我們看看具體實現(xiàn):

public interface Human {    
    public void getColor();//人有不同的顏色    
    public void talk(); //人會說話
}

接下來對Human接口的不同實現(xiàn):

public class BlackHuman implements Human {// 黑種人
 
    @Override
    public void getColor() {
        System.out.println("Black");
    }
    @Override
    public void talk() {
        System.out.println("Black man");
    }
}
public class Human implements Human {   //黃種人
 
    @Override
    public void getColor() {
        System.out.println("Yellow");
    }
    @Override
    public void talk() {
        System.out.println("Yellow man");
    }
}
public class WhiteHuman implements Human {//白種人
 
    @Override
    public void getColor() {
        System.out.println("White");
    }
    @Override
    public void talk() {
        System.out.println("White man");
    }
}

好了肥隆,人的模子搞好了既荚,現(xiàn)在女媧要開始搭建八卦爐了,于是女媧開始畫八卦爐模型了:

public abstract class AbstractHumanFactory{
    public abstract <T extends Human> T createHuman(Class<T> clazz); //注意這里T必須是Human的實現(xiàn)類才行栋艳,因為要造Human嘛
}

然后女媧開始具體實現(xiàn)這個八卦爐了……

public class HumanFactory extends AbstractHumanFactory {
 
    @Override
    public <T extends Human> T createHuman(Class<T> clazz) {
        Human human = null;
        try {
            human = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) { //異常處理
            System.out.println("人種產(chǎn)生錯誤");
        }
        return (T) human;
    }
}

好恰聘,現(xiàn)在人種也有了,八卦爐也有了吸占,剩下的就是采集黃土晴叨,然后命令八卦爐開始生產(chǎn)了:

public class FactoryTest {
    public static void main(String[] args) {
        AbstractHumanFactory bagualu = new HunmanFactory();
        Human blackMan = bagualu.createHuman(BlackHuman.class); //黑人誕生了
        Human yellowMan = bagualu.createHuman(YelloHuman.class); //黃人誕生了
        Human whiteMan = bagualu.createHuman(WhiteHuman.class); //白人誕生了
      }
}

女媧就是這么把人造出來的……這就是工廠模式!

工廠模式的擴展

靜態(tài)工廠模式

我們還用上面女媧造人的例子說明旬昭,現(xiàn)在女媧在思考一個問題:我現(xiàn)在只需要一個工廠就可以把人生產(chǎn)出來篙螟,我干嘛要具體的工廠對象呢?我只要使用靜態(tài)方法就好了。這樣一想蒸矛,于是女媧就開始把AbstractHumanFactory抽象類去掉了宦赠,只保留了HumanFactory類,同時把createHuman方法設(shè)置成了static類型绪杏,搞定!

public class HumanFactory {
 
    @Override
    public static <T extends Human> T createHuman(Class<T> clazz) {
        Human human = null;
        try {
            human = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) { //異常處理
            System.out.println("人種產(chǎn)生錯誤");
        }
        return (T) human;
    }
}

然后女媧造人的時候就不用new什么八卦爐了纽绍,直接用HumanFactory類調(diào)用就行了:

    Human blackMan = HumanFactory.createHuman(BlackHuman.class);

這就是靜態(tài)工廠模式蕾久,在實際項目中,根據(jù)需求可以設(shè)置成靜態(tài)工廠類拌夏,但是缺點是擴展比較困難僧著,如果就一個工廠履因,不需要擴展,可以這么設(shè)計盹愚,仍然是很常用的栅迄。

多個工廠模式

我們還以女媧造人為例,后來女媧想了想皆怕,毅舆,這人不可能就說話吧,還得有不同的屬性殺的愈腾,如果在一個八卦爐里造憋活,除了new一個人外,還得對不同的人設(shè)置不同的屬性虱黄,這樣的話悦即,八卦爐有點吃不消阿……又有點亂啊……但是她想到了一個法子,每個人種弄個八卦爐礁鲁,不同的八卦爐專門生產(chǎn)不同的人種盐欺,這樣就結(jié)構(gòu)清晰了,她在造人的時候自己選擇與哪個八卦爐相關(guān)聯(lián)就行了仅醇。示意圖如下:

多個工廠模式類圖

這樣的話AbstractHumanFactory抽象類我們就要改寫了:

public abstract class AbstractHumanFactory {
    public abstract Human createHuman();
}

注意抽象方法中已經(jīng)不需要再傳遞相關(guān)類的參數(shù)了冗美,因為每個具體的工廠都已經(jīng)非常明確自己的職責:創(chuàng)建自己負責的產(chǎn)品類對象。所以不同的工廠實現(xiàn)自己的createHuman方法即可:

public class BlackHumanFactory extends AbstractHumanFactory {
    public Human createHuman() {
        return new BlackHuman();
    }
}
public class YellowHumanFactory extends AbstractHumanFactory {
    public Human createHuman() {
        return new YellowHuman();
    }
}
public class WhiteHumanFactory extends AbstractHumanFactory {
    public Human createHuman() {
        return new WhiteHuman();
    }
}

這樣三個不同的工廠就產(chǎn)生了析二,每個工廠對應(yīng)只生產(chǎn)自己對應(yīng)的人種粉洼。所以現(xiàn)在女媧造人就可以不用一個八卦爐了,分工協(xié)作叶摄,各不影響了属韧!

public class FactoryTest {
    public static void main(String[] args) {
        Human blackMan = new BlackHumanFactory().createHuman(); //黑人誕生了
        Human yellowMan = new YellowHumanFactory().createHuman(); //黃人誕生了
        Human whiteMan = new WhiteHumanFactory().createHuman(); //白人誕生了
      }
}

這種工廠模式的好處是職責清晰,結(jié)構(gòu)簡單蛤吓,但是給擴擴展性和可維護性帶來了一定的影響宵喂,因為如果要擴展一個產(chǎn)品類,就需要建立一個相應(yīng)的工廠類会傲,這樣就增加了擴展的難度锅棕。因為工廠類和產(chǎn)品類的數(shù)量是相同的,維護時也需要考慮兩個對象之間的關(guān)系淌山。但是這種模式還是很常用的裸燎。

替代單例模式

上一章介紹了單例模式,并且指出了單例和多例的一些缺點泼疑,但是我們是不是可以采用工廠模式來實現(xiàn)一個單例模式的功能呢德绿?答案是肯定的,單例模式的核心要求就是在內(nèi)存中只有一個對象,通過工廠方法模式也可以只在內(nèi)存中生產(chǎn)一個對象移稳。見下面:

Singleton類定義了一個private的無參構(gòu)造方法蕴纳,目的是不允許通過new的方式創(chuàng)建對象,另外个粱,Singleton類也不自己定義一個Singleton對象了袱蚓,因為它要通過工廠來獲得。

public class Singleton {
    private Singleton() {
        
    }
    public void doSomething() {
        //業(yè)務(wù)處理
    }
}

既然Singleton不能通過正常的渠道建立一個對象几蜻,那SingletonFactory如何建立一個單例對象呢?答案是通過反射方式創(chuàng)建:

public class SingletonFactory {
    private static Singleton instance;
    static {
        try {       
            Class clazz = Class.forName(Singleton.class.getName());
            //獲取無參構(gòu)造方法
            Constructor constructor = clazz.getDeclaredConstructor();
            //設(shè)置無參構(gòu)造方法可訪問
            constructor.setAccessible(true);
            //產(chǎn)生一個實例對象
            instance = (Singleton) constructor.newInstance();
        } catch (Exception e) {
            //異常處理
        }
    }
    public static Singleton getInstance() {
        return instance;
    }
}

以上通過工廠方法模式創(chuàng)建了一個單例對象体斩,該框架可以繼續(xù)擴展梭稚,在一個項目中可以產(chǎn)生一個單例構(gòu)造器,所有需要產(chǎn)生單例的類都遵循一定的規(guī)則(構(gòu)造方法是private)絮吵,然后通過擴展該框架弧烤,只要輸入一個類型就可以獲得唯一的一個實例。

延遲初始化

何為延遲初始化(Lazy initialization)蹬敲?即一個對象被使用完畢后暇昂,并不立刻釋放,工廠類保持其初始狀態(tài)伴嗡,等待再次被使用急波。延遲初始化是工廠模式的一個擴展應(yīng)用,其通用類表示如下:

延遲初始化類圖

ProductFactory負責產(chǎn)品類對象的創(chuàng)建工作瘪校,并且通過prMap變量產(chǎn)生一個緩存澄暮,對需要再次被重用的對象保留:

public class ProductFactory {
    private static final Map<String, Product> prMap = new HashMap();
    public static synchronized Product createProduct(String type) throws Exception {
        Product product = null;
        //如果Map中已經(jīng)有這個對象
        if(prMap.containsKey(type)) {
            product = prMap.get(type);
        } else {
            if(type.equals("Product1")) {
                product = new ConcreteProduct1();
            }
            else {
                product = new ConcreteProduct2();
            }
            prMap.put(type, product);
        }
        return product;
    }
}

代碼比較簡單,通過定義一個Map容器阱扬,容納所有產(chǎn)生的對象泣懊,每次在new一個對象的時候先判斷Map中有沒有,有就不用再new了麻惶,直接取馍刮。另外,每次new過一個對象后窃蹋,也將其放入Map中方便下次調(diào)用卡啰。

延遲加載時很有用的,比如JDBC連接數(shù)據(jù)庫脐彩,會要求設(shè)置一個MaxConnections最大連接數(shù)量碎乃,該數(shù)量就是內(nèi)存中最大實例化的數(shù)量。

工廠模式的應(yīng)用

優(yōu)點:

  1. 工廠模式具有良好的封裝性惠奸,代碼結(jié)構(gòu)清晰梅誓,也有利于擴展。在增加產(chǎn)品類的情況下,只需要適當?shù)匦薷木唧w的工廠類或擴展一個工廠類梗掰,就可以完成“擁抱變化”嵌言。
  2. 工廠模式可以屏蔽產(chǎn)品類。這一點非常重要及穗,產(chǎn)品類的實現(xiàn)如何變化摧茴,調(diào)用者都不用關(guān)系,只需要關(guān)心產(chǎn)品的接口埂陆,只要接口保持不變苛白,系統(tǒng)的上層模塊就不需要發(fā)生變化。
  3. 工廠模式是典型的解耦框架焚虱。高層模塊只需要知道產(chǎn)品的抽象類购裙,其他的實現(xiàn)類都不用關(guān)心。

高層模塊只需要知道產(chǎn)品的抽象類鹃栽,其他產(chǎn)品都不用關(guān)心躏率,符合迪米特法則(最少知識原則),我不需要的就不要去交流民鼓;也符合依賴倒置原則薇芝,只依賴產(chǎn)品類的抽象;當然也符合里式替換原則丰嘉,使用產(chǎn)品子類替代產(chǎn)品父類沒有問題夯到。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市饮亏,隨后出現(xiàn)的幾起案子黄娘,更是在濱河造成了極大的恐慌,老刑警劉巖克滴,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逼争,死亡現(xiàn)場離奇詭異,居然都是意外死亡劝赔,警方通過查閱死者的電腦和手機誓焦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來着帽,“玉大人杂伟,你說我怎么就攤上這事∪院玻” “怎么了赫粥?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長予借。 經(jīng)常有香客問我越平,道長频蛔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任秦叛,我火速辦了婚禮晦溪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挣跋。我一直安慰自己三圆,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布避咆。 她就那樣靜靜地躺著舟肉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪查库。 梳的紋絲不亂的頭發(fā)上度气,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音膨报,去河邊找鬼。 笑死适荣,一個胖子當著我的面吹牛现柠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弛矛,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼够吩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了丈氓?” 一聲冷哼從身側(cè)響起周循,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎万俗,沒想到半個月后湾笛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡闰歪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年嚎研,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片库倘。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡临扮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出教翩,到底是詐尸還是另有隱情杆勇,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布饱亿,位于F島的核電站蚜退,受9級特大地震影響闰靴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜关霸,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一传黄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧队寇,春花似錦膘掰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至零渐,卻和暖如春窒舟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诵盼。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工惠豺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人风宁。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓洁墙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親戒财。 傳聞我的和親對象是個殘疾皇子热监,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容