由浅到深了解工厂模式

作者已经搬迁去隔壁网站,也欢迎大家关注我们的写作团队:天星技术团队

唠个嗑

先给各位观众老爷道个歉,在上一篇文章的末尾本来说了这次要给大家分享代理模式,但是臣妾,做不到啊! 最近公司给我了一个新项目,于是比较忙一点,再加上代理模式那边的东西有点多,我有点懵逼的,静态、动态、远程、虚拟,还有个RMI,小机灵鬼儿的脑袋一时间处理不过来啊!



最近在搭建新项目的时候,参考了前辈的一些代码。这一次看别人代码的时候,更容易知道别人写着类的目的是干嘛,为啥要这样写了,这就是学习设计模式之后的好处之一吧,我仍然会继续加油。尝到了一些甜头,现在更有动力了。你们的留言,讨论,点赞更是我巨大的动力。

虽然是中途改道来写工厂模式,但绝对不会让各位观众老爷失望的!本次要讲的是三种工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式),以及相关模式源码上的一些理解、扩展。

什么是工厂模式

new! 准确的说,是代替new实例化具体类的一种模式。
接下来我将以“音乐厂牌创造音乐”为例子,由浅到深深入工厂模式。
至于为什么要用工厂模式我会边讲例子边说。

简单工厂模式

制作一首歌曲,确定歌曲风格之后,就先要写词谱曲,然后依次就是录歌,剪辑,混音,就可以发型了。当然也可以“不混,直接发”!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;
    }
}

在这儿有另外一种方法:利用静态方法定义一个简单工厂(静态工厂)
这样就不需要使用创建对象的方法来实例化对象。但这样也有一个缺点:不能通过继承来改变创建方法的行为。

修改之后重写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等等通常都会用到简单工厂模式。
优点:简单,解耦。
缺点:静态工厂无法继承,违反开闭原则。

工厂方法模式

定义

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

类图

工厂方法模式有四个核心类:

  1. Product(产品类):所有产品必须实现这个共同的接口,这样使用这些产品的类就可以引用这个接口,而不是具体类。
  2. ConcreteProduct(具体产品类)
  3. Creator(创建者类):实现了所有操纵产品的方法,但不实现工厂方法。
  4. ConcreteCreator(具体创建者类):实现了factoryMethod(),负责创建一个或多个具体产品,只有ConcreteCreator类知道如何创建这些产品。

来撸代码

刚刚我们已经创建了两个类,MusicLabel和SongFactory,MusicLabel在工厂方法中可以作为一个Creator。SongFactory不在四大核心之内,先不管。
我们先来创造一下产品类和创建者类(他们是两个平行类层级)。

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 "混音";
    }
}

接下来创建各自的子类。厂牌方面,各位最熟知的可能就是“摩登天空”了,另外,听国摇的小伙伴对谢天笑这个名字应该不会陌生,谢天笑是在“十三月”音乐厂牌。这里我们就以这两个厂牌为例,来写各自的子类。

摩登天空音乐厂牌

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()中,并不知道真正创建的是哪一个厂牌的音乐。创建具体对象的工作,都在子类中。
接下来的工作就是把刚刚写过的MDSkyFolkSong等具体子类继承Song。这里只写一个。

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

在这里或许许多小伙伴要说这样写会有很多子类,很麻烦。但这样已经是最优的选择了。耦合度低,遵守了开闭原则。

感受

工厂方法模式有点像简单工厂的合集,特别是当只有一个具体工厂类存在时。
简单工厂可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。
优点:在简单工厂的优点上加上“可以变更正在创建的产品”。
缺点:子类相当多,不便于管理。

抽象工厂模式

刚刚我们再写具体厂牌的时候,有提到,可以在具体厂牌类中使用简单工厂模式。也就是说,我们可以创建MDSkySongFactory和ThirteenMonthSongFactory两个工厂类。并且这两个工厂做的事都是一样的,只是具体东西不一样而已。
那……
我们是不是可以写一个工厂超类,把要做的事情写成抽象方法,再让子工厂类各自实现呢?
可以的!这就是传说中的抽象工厂模式。

定义

为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

类图

这图画的好辛苦
  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;
    }
}

完美!到处都充斥着依赖倒置的清香。

体会

这个模式虽然需要些的核心类比较多,但是当需求满足“为相互依赖的对象提供一个接口”,具体对象又比较多,又易修改的时候,这个模式的优点你就能体会到了。
优点:闭合开闭原则,耦合低。
缺点:不适用于对象数量少的情况。

BitmapFactory


BitmapFactory是android中比较常见的工厂模式的使用。我们肯定都写过这样一句代码

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

看出来什么没??
这特么就是个简单工厂模式啊!还是个静态工厂!
为何这么说,因为它是通过类名调用方法,并且返回了一个对象。这不就是简单工厂吗?


BitmapFactory.class

看里面,全是红彤彤的静态方法。这个工厂做的事就是通过不同的参数,返回Bitmap对象。这也就是简单工厂模式的作用。
今天的源码解读就到此为止了,要问我为啥没写扩展。
你要是看懂了工厂模式,就不会问这个问题。

为什么要用工厂模式

(此处应有弹幕:“收尾呼应,满分作文!”)
我写的优点里面那么多,还不能让你使用工厂模式吗?
就冲解耦合这一点,你就该用它!

总结

以下是我“设计模式系列”文章,欢迎大家关注留言投币丢香蕉。
也可以进群跟大神们讨论。qq群:557247785

设计模式入门
Java与Kotlin的单例模式
Kotlin的装饰者模式与源码扩展
由浅到深了解工厂模式
为了学习Rxjava,年轻小伙竟作出这种事!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,748评论 0 14
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,948评论 1 15
  • 该文章属于刘小壮原创,转载请注明:刘小壮[//www.greatytc.com/u/2de707c93d...
    刘小壮阅读 12,763评论 29 59
  • 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。通常我们所说的工厂模式是指工厂方法模...
    zfylin阅读 1,316评论 0 7
  • 透过镜子看到的是自己 透过爱情看到的是你。
    来自星星的寒阅读 140评论 0 5