由淺到深了解工廠模式

作者已經搬遷去隔壁網站催植,也歡迎大家關注我們的寫作團隊:天星技術團隊

嘮個嗑

先給各位觀眾老爺道個歉刻帚,在上一篇文章的末尾本來說了這次要給大家分享代理模式潦嘶,但是臣妾,做不到俺缰凇掂僵! 最近公司給我了一個新項目航厚,于是比較忙一點,再加上代理模式那邊的東西有點多锰蓬,我有點懵逼的幔睬,靜態(tài)、動態(tài)芹扭、遠程麻顶、虛擬,還有個RMI舱卡,小機靈鬼兒的腦袋一時間處理不過來案ㄉ觥!



最近在搭建新項目的時候轮锥,參考了前輩的一些代碼矫钓。這一次看別人代碼的時候,更容易知道別人寫著類的目的是干嘛交胚,為啥要這樣寫了份汗,這就是學習設計模式之后的好處之一吧,我仍然會繼續(xù)加油蝴簇。嘗到了一些甜頭杯活,現在更有動力了。你們的留言熬词,討論旁钧,點贊更是我巨大的動力。

雖然是中途改道來寫工廠模式互拾,但絕對不會讓各位觀眾老爺失望的歪今!本次要講的是三種工廠模式(簡單工廠模式,工廠方法模式颜矿,抽象工廠模式)寄猩,以及相關模式源碼上的一些理解、擴展骑疆。

什么是工廠模式

new田篇! 準確的說,是代替new實例化具體類的一種模式箍铭。
接下來我將以“音樂廠牌創(chuàng)造音樂”為例子泊柬,由淺到深深入工廠模式。
至于為什么要用工廠模式我會邊講例子邊說诈火。

簡單工廠模式

制作一首歌曲兽赁,確定歌曲風格之后,就先要寫詞譜曲,然后依次就是錄歌刀崖,剪輯惊科,混音,就可以發(fā)型了蒲跨。當然也可以“不混译断,直接發(fā)”!Skr~或悲。

public class MusicLabel {
    Song createSong(String type){
        Song song = null;

        if(type.equals("folk")){ 
            song = new FolkSong();
        }else if(type.equals("rock")){
            song = new RockSong();
        }else if(type.equals("pop")){
            song = new PopSong();
        }

        song.prepare();//作詞作曲演奏
        song.Sing(); //錄歌
        song.Cut(); //剪輯
        song.Mix(); //混音
        return song;
    }
}

這樣寫孙咪,有沒有問題? 沒有巡语! 不出bug能跑就完事兒了翎蹈。科科男公。
然而這樣卻違反了開閉原則:對擴展開放荤堪,對修改關閉
我們可以把易變化的部分跟不變化的部分分開枢赔。也就是將new對象的部分提出來澄阳,單獨形成一個類(工廠)。

public class SongFactory {
    public Song orderSong(String type){
        Song song = null;
        if(type.equals("folk")){
            song = new FolkSong();
        }else if(type.equals("rock")){
            song = new RockSong();
        }else if(type.equals("pop")){
            song = new PopSong();
        }
        return song;
    }
}

在這兒有另外一種方法:利用靜態(tài)方法定義一個簡單工廠(靜態(tài)工廠)踏拜。
這樣就不需要使用創(chuàng)建對象的方法來實例化對象碎赢。但這樣也有一個缺點:不能通過繼承來改變創(chuàng)建方法的行為。

修改之后重寫MusicLabel類

public class MusicLabel {
    SongFactory factory;
    public MusicLabel(SongFactory factory) {
        this.factory = factory;
    }
    Song createSong(String type){
        Song song = null;
        song = factory.orderSong(type);
        song.prepare();
        song.Sing();
        song.Cut();
        song.Mix();
        return song;
    }
}

這樣一來就將面向具體編程速梗,變成了面向接口編程肮塞。

在設計模式中,“實現一個接口”泛指“實現某個超類型(類/接口)的某個方法”姻锁。

心得

給我的感覺枕赵,簡單工廠模式更像是一種編程的習慣。最簡單的解耦位隶,使得工廠類能夠被各種廠牌反復使用拷窜。
在我還沒認識簡單工廠之前,其實我就寫過很多簡單工廠的例子了涧黄。各種基類BaseActivity篮昧、BaseFragment等等通常都會用到簡單工廠模式。
優(yōu)點:簡單弓熏,解耦恋谭。
缺點:靜態(tài)工廠無法繼承糠睡,違反開閉原則挽鞠。

工廠方法模式

定義

工廠方法模式定義了一個創(chuàng)建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類信认。

類圖

工廠方法模式有四個核心類:

  1. Product(產品類):所有產品必須實現這個共同的接口材义,這樣使用這些產品的類就可以引用這個接口,而不是具體類嫁赏。
  2. ConcreteProduct(具體產品類)
  3. Creator(創(chuàng)建者類):實現了所有操縱產品的方法其掂,但不實現工廠方法。
  4. ConcreteCreator(具體創(chuàng)建者類):實現了factoryMethod()潦蝇,負責創(chuàng)建一個或多個具體產品款熬,只有ConcreteCreator類知道如何創(chuàng)建這些產品。

來擼代碼

剛剛我們已經創(chuàng)建了兩個類攘乒,MusicLabel和SongFactory贤牛,MusicLabel在工廠方法中可以作為一個Creator。SongFactory不在四大核心之內则酝,先不管殉簸。
我們先來創(chuàng)造一下產品類和創(chuàng)建者類(他們是兩個平行類層級)。

public abstract class MusicLabel {
    Song createSong(String type){
        Song song = null;
        song = orderSong(type);
        song.prepare();
        song.Sing();
        song.Cut();
        song.Mix();
        return song;
    }
    abstract Song orderSong(String type); //工廠方法
}
public abstract class Song {
    String singer;//演唱者
    String lyricist;//作詞人
    String composer;//作曲人
    String prepare() {
        return "演唱者:"+singer + ",作詞人:"+lyricist + ",作曲人:"+composer;
    }
    String Sing(){
        return "錄歌";
    }
    String Cut(){
        return "剪切";
    }
    String Mix(){
        return "混音";
    }
}

接下來創(chuàng)建各自的子類沽讹。廠牌方面般卑,各位最熟知的可能就是“摩登天空”了,另外爽雄,聽國搖的小伙伴對謝天笑這個名字應該不會陌生蝠检,謝天笑是在“十三月”音樂廠牌。這里我們就以這兩個廠牌為例盲链,來寫各自的子類蝇率。

摩登天空音樂廠牌

public class MDSkyMusicLabel extends MusicLabel {
    @Override
    Song orderSong(String type) { //此處可用簡單工廠模式
        if(type.equals("folk")){
            return new MDSkyFolkSong();
        }else if(type.equals("rock")){
            return new MDSkyRockSong();
        }else if(type.equals("pop")){
            return new MDSkyPopSong();
        }else return  null;
    }
}

十三月音樂廠牌

public class ThirteenMonthMusicLabel extends MusicLabel {
    @Override
    Song orderSong(String type) { //此處可用簡單工廠模式
        if(type.equals("folk")){
            return new ThirteenMonthFolkSong();
        }else if(type.equals("rock")){
            return new ThirteenMonthRockSong();
        }else if(type.equals("pop")){
            return new ThirteenMonthPopSong();
        }else return  null;
    }
}

在MusicLabel類的createSong()中,并不知道真正創(chuàng)建的是哪一個廠牌的音樂刽沾。創(chuàng)建具體對象的工作本慕,都在子類中。
接下來的工作就是把剛剛寫過的MDSkyFolkSong等具體子類繼承Song侧漓。這里只寫一個锅尘。

public class MDSkyFolkSong extends Song {
    public MDSkyFolkSong() {
        singer = "摩登天空的民謠藝人";
        lyricist = "摩登天空的民謠作詞人";
        composer = "摩登天空的民謠作曲人";
    }
}

在這里或許許多小伙伴要說這樣寫會有很多子類,很麻煩布蔗。但這樣已經是最優(yōu)的選擇了藤违。耦合度低,遵守了開閉原則纵揍。

感受

工廠方法模式有點像簡單工廠的合集顿乒,特別是當只有一個具體工廠類存在時。
簡單工廠可以將對象的創(chuàng)建封裝起來泽谨,但是簡單工廠不具備工廠方法的彈性璧榄,因為簡單工廠不能變更正在創(chuàng)建的產品特漩。
優(yōu)點:在簡單工廠的優(yōu)點上加上“可以變更正在創(chuàng)建的產品”。
缺點:子類相當多骨杂,不便于管理涂身。

抽象工廠模式

剛剛我們再寫具體廠牌的時候,有提到搓蚪,可以在具體廠牌類中使用簡單工廠模式蛤售。也就是說,我們可以創(chuàng)建MDSkySongFactory和ThirteenMonthSongFactory兩個工廠類妒潭。并且這兩個工廠做的事都是一樣的悴能,只是具體東西不一樣而已。
那……
我們是不是可以寫一個工廠超類雳灾,把要做的事情寫成抽象方法搜骡,再讓子工廠類各自實現呢?
可以的佑女!這就是傳說中的抽象工廠模式记靡。

定義

為創(chuàng)建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類团驱。

類圖

這圖畫的好辛苦
  1. Cilent(客戶類):代碼中只需涉及抽象工廠摸吠,運行時自動使用實際的工廠。
  2. AbstractFactory(抽象工廠):定義了一個接口嚎花,所有具體工廠必須實現它迷殿。這個接口包含了一組方法來生產產品按咒。
  3. ConcreteFactory(具體工廠):客戶只需要使用其中一個工廠而不需要實例化任何產品對象尼变。
  4. AbstractProduct(抽象產品類):這些抽象產品類就是抽象工廠中所需要的每一個條件姥敛。
  5. ConcreteProduct(具體產品類):繼承抽象產品類。

由入門到放棄

剛剛我們說過兵罢,我們可以整理一個工廠超類献烦,這個工廠超類,就是AbstractFactory卖词!它在我們這個例子中的作用就是返回一個singer巩那,一個lyricist和一個composer。所以我們可以這樣寫此蜈。

public interface SongFactory {
    public String findSinger();
    public String findLyricist();
    public String findComposer();
}

然后給每個廠牌都寫一個具體工廠

public class MDskySongFactory implements SongFactory {
    @Override
    public String findSinger() {
        return new MDskySinger();
    }
    @Override
    public String findLyricist() {
        return new MDskyLyricist();
    }
    @Override
    public String findComposer() {
        return new MDskyComposer();
    }
}
public class ThirteenMonthSongFactory implements SongFactory {
    @Override
    public String findSinger() {
        return new ThirteenMonthSinger();
    }
    @Override
    public String findLyricist() {
        return new ThirteenMonthLyricist();
    }
    @Override
    public String findComposer() {
        return new ThirteenMonthComposer();
    }
}

還需要重寫一下Song類

public abstract class Song {
    String singer;//演唱者
    String lyricist;//作詞人
    String composer;//作曲人
    abstract void prepare();//只改變了這個方法
    String Sing(){
        return "錄歌";
    }
    String Cut(){
        return "剪切";
    }
    String Mix(){
        return "混音";
    }

    @Override
    public String toString() {
        return "Song{" +
                "singer='" + singer + '\'' +
                ", lyricist='" + lyricist + '\'' +
                ", composer='" + composer + '\'' +
                '}';
    }
}

現在就可以根據工廠類來寫歌曲子類了即横。每個廠牌都有FolkSong、RockSong裆赵、PopSong东囚,現在不用寫那么多子類,只需要建立一個相應子類战授,材料(作詞作曲演唱)就交給傳遞進去的工廠類來解決页藻!

public class FolkSong extends Song{
    SongFactory factory;
    public FolkSong(SongFactory factory) {
        this.factory = factory;
    }
    @Override
    void prepare() {
        singer = factory.findSinger();
        lyricist = factory.findLyricist();
        composer = factory.findComposer();
    }
}

現在我們幾乎完成了所有的材料抛蚁,就差調用了。現在先來理一理這些東西惕橙。

  1. 抽象工廠類是SongFactor。
  2. 具體工廠類是MDskySongFactory 和ThirteenMonthSongFactory钉跷。
  3. 抽象產品類是Singer弥鹦、Lyricist、Composer爷辙。
  4. 具體產品類是MDskySinger彬坏、ThirteenMonthSinger等。
  5. Client是MDSkyMusicLabel和ThirteenMonthMusicLabel膝晾。

現在就在Client里面調用看看吧栓始。

public class MDSkyMusicLabel extends MusicLabel {
    @Override
    Song orderSong(String type) {
        Song song = null;
        SongFactory factory = new MDskySongFactory();

       if(type.equals("folk")){
           song = new FolkSong(factory);
       }else if(type.equals("rock")){
           song = new RockSong(factory);
       }else if(type.equals("pop")){
           song = new PopSong(factory);
       }
       return song;
    }
}

完美!到處都充斥著依賴倒置的清香血当。

體會

這個模式雖然需要些的核心類比較多幻赚,但是當需求滿足“為相互依賴的對象提供一個接口”,具體對象又比較多臊旭,又易修改的時候落恼,這個模式的優(yōu)點你就能體會到了。
優(yōu)點:閉合開閉原則离熏,耦合低佳谦。
缺點:不適用于對象數量少的情況。

BitmapFactory


BitmapFactory是android中比較常見的工廠模式的使用滋戳。我們肯定都寫過這樣一句代碼

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher) ;

看出來什么沒钻蔑??
這特么就是個簡單工廠模式凹檠臁咪笑!還是個靜態(tài)工廠!
為何這么說娄涩,因為它是通過類名調用方法蒲肋,并且返回了一個對象。這不就是簡單工廠嗎钝满?


BitmapFactory.class

看里面兜粘,全是紅彤彤的靜態(tài)方法。這個工廠做的事就是通過不同的參數弯蚜,返回Bitmap對象孔轴。這也就是簡單工廠模式的作用。
今天的源碼解讀就到此為止了碎捺,要問我為啥沒寫擴展路鹰。
你要是看懂了工廠模式贷洲,就不會問這個問題。

為什么要用工廠模式

(此處應有彈幕:“收尾呼應晋柱,滿分作文优构!”)
我寫的優(yōu)點里面那么多,還不能讓你使用工廠模式嗎雁竞?
就沖解耦合這一點钦椭,你就該用它!

總結

以下是我“設計模式系列”文章碑诉,歡迎大家關注留言投幣丟香蕉彪腔。
也可以進群跟大神們討論。qq群:557247785

設計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴展
由淺到深了解工廠模式
為了學習Rxjava进栽,年輕小伙竟作出這種事德挣!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市快毛,隨后出現的幾起案子格嗅,更是在濱河造成了極大的恐慌,老刑警劉巖唠帝,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吗浩,死亡現場離奇詭異,居然都是意外死亡没隘,警方通過查閱死者的電腦和手機懂扼,發(fā)現死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右蒲,“玉大人阀湿,你說我怎么就攤上這事」逋” “怎么了陷嘴?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長间坐。 經常有香客問我灾挨,道長,這世上最難降的妖魔是什么竹宋? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任劳澄,我火速辦了婚禮,結果婚禮上蜈七,老公的妹妹穿的比我還像新娘秒拔。我一直安慰自己,他們只是感情好飒硅,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布砂缩。 她就那樣靜靜地躺著作谚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庵芭。 梳的紋絲不亂的頭發(fā)上妹懒,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音双吆,去河邊找鬼眨唬。 笑死,一個胖子當著我的面吹牛伊诵,可吹牛的內容都是我干的。 我是一名探鬼主播回官,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼曹宴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了歉提?” 一聲冷哼從身側響起笛坦,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苔巨,沒想到半個月后版扩,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡侄泽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年礁芦,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼尾。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡柿扣,死狀恐怖,靈堂內的尸體忽然破棺而出闺魏,到底是詐尸還是另有隱情未状,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布析桥,位于F島的核電站司草,受9級特大地震影響,放射性物質發(fā)生泄漏泡仗。R本人自食惡果不足惜埋虹,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娩怎。 院中可真熱鬧吨岭,春花似錦、人聲如沸峦树。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至急灭,卻和暖如春姐浮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背葬馋。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工卖鲤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畴嘶。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓蛋逾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窗悯。 傳聞我的和親對象是個殘疾皇子区匣,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內容

  • 設計模式概述 在學習面向對象七大設計原則時需要注意以下幾點:a) 高內聚、低耦合和單一職能的“沖突”實際上蒋院,這兩者...
    彥幀閱讀 3,734評論 0 14
  • 設計模式匯總 一亏钩、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,905評論 1 15
  • 該文章屬于劉小壯原創(chuàng)欺旧,轉載請注明:劉小壯[http://www.reibang.com/u/2de707c93d...
    劉小壯閱讀 12,732評論 29 59
  • 工廠模式是我們最常用的實例化對象模式了姑丑,是用工廠方法代替new操作的一種模式。通常我們所說的工廠模式是指工廠方法模...
    zfylin閱讀 1,309評論 0 7
  • 透過鏡子看到的是自己 透過愛情看到的是你辞友。
    來自星星的寒閱讀 139評論 0 5