由浅到深了解工厂模式

做者 点先生 日期 2018.9.26android

唠个嗑

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

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

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

什么是工厂模式

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

简单工厂模式

制做一首歌曲,肯定歌曲风格以后,就先要写词谱曲,而后依次就是录歌,剪辑,混音,就能够发型了。固然也能够“不混,直接发”!Skr~。composer

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对象的部分提出来,单独造成一个类(工厂)。ide

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;
    }
}
复制代码

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

修改以后重写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;
    }
}
复制代码

这样一来就将面向具体编程,变成了面向接口编程。ui

在设计模式中,“实现一个接口”泛指“实现某个超类型(类/接口)的某个方法”。this

心得

给个人感受,简单工厂模式更像是一种编程的习惯。最简单的解耦,使得工厂类可以被各类厂牌反复使用。
在我还没认识简单工厂以前,其实我就写过不少简单工厂的例子了。各类基类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对象。这也就是简单工厂模式的做用。
今天的源码解读就到此为止了,要问我为啥没写扩展。
你要是看懂了工厂模式,就不会问这个问题。

为何要用工厂模式

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

总结

如下是我“设计模式系列”文章,欢迎你们关注留言投币丢香蕉。

设计模式入门
Java与Kotlin的单例模式
Kotlin的装饰者模式与源码扩展
由浅到深了解工厂模式

相关文章
相关标签/搜索