美文网首页
由浅到深了解工厂模式

由浅到深了解工厂模式

作者: 点先生在这 | 来源:发表于2018-09-25 23:54 被阅读0次

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

    唠个嗑

    先给各位观众老爷道个歉,在上一篇文章的末尾本来说了这次要给大家分享代理模式,但是臣妾,做不到啊! 最近公司给我了一个新项目,于是比较忙一点,再加上代理模式那边的东西有点多,我有点懵逼的,静态、动态、远程、虚拟,还有个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,年轻小伙竟作出这种事!

    相关文章

      网友评论

          本文标题:由浅到深了解工厂模式

          本文链接:https://www.haomeiwen.com/subject/izjonftx.html