【阿里大神讲设计模式】3.光氏饮品升级了---工厂方法模式

前情提要


上集讲到, 小光的热干面店, 开始搭配提供饮料了. 再加上美女表妹的助阵, 生意是红红火火啊.
然而, 事情也不是尽善尽美的, 慢慢小光就听到了一些的客户的声音: 酸梅汤太酸了, 能调好点吗? 天冷了能来点热饮吗?
客户可是上帝啊, 小光立马就着手改进.

所有示例源码已经上传到Github, 戳这里

表妹的抱怨


带着客户的声音, 小光找表妹聊了下, 想让表妹修改下当前的酸梅汤泡制比例, 另外再增加一些热饮的泡制.
没想到, 表妹一听到着就很反对: “我现在已经记得太多了, 你这么不定时的修改, 增加, 我更容易记混, 到时候更出问题了”. (职责太重)
小光一想, 是啊, 我把自己从做热干面添加各种配料的烦恼中释放出来了(通过Builder模式). 不能让表妹也陷入这样的烦恼啊.

解决之路


可是怎么才能更好的解决这个问题呢?
要是我有很多个表妹就好了, 每个表妹负责一种饮料的泡制, 小光想着. 嗯~, 很多个表妹?! 小光微微一笑, 计上心头.
小光买来很多个迷你饮水机, 一个装一种饮料, 并且贴上相应的标签, 如此这般:


每个迷你饮水机作为一个饮料机, 用来生产不同的饮料. 表妹只有根据用户的需求选择不同的饮料机打出饮料即可.
这样, 表妹也无需关注饮料的生产过程了, 不用记那么多的饮料配置方式了. 如果想要新增饮品, 再弄一台饮料机就行了.

来看看对应关系


这是没有标签的饮水机(饮料机):

public interface IBeverageMachine {

    Drink makeDrink();
}

这是贴了不同饮料类型的饮料机:

public class OrangeJuiceMachine implements IBeverageMachine {
    @Override
    public Drink makeDrink() {
        return new OrangeJuice().make();
    }
}
public class CokeMachine implements IBeverageMachine {
    @Override
    public Drink makeDrink() {
        return new Coke().make();
    }
}
public class PlumJuiceMachine implements IBeverageMachine {
    @Override
    public Drink makeDrink() {
        return new PlumJuice().make();
    }
}

这是那些饮料(还是有一个抽象继承关系):

public abstract class Drink {

    private String name;
    private String instantPackage;

    public Drink make() {
        this.name = getName();
        this.instantPackage = getInstantPackage();
        return this;
    }

    abstract String getInstantPackage();
    abstract String getName();

    @Override
    public String toString() {
        return "This is a cup of:" + this.name;
    }
}

public class Coke extends Drink {
    @Override
    String getInstantPackage() {
        return "速溶可乐粉";
    }

    @Override
    String getName() {
        return "可乐";
    }
}

public class OrangeJuice extends Drink {
    @Override
    String getInstantPackage() {
        return "速溶橙汁粉";
    }

    @Override
    String getName() {
        return "橙汁";
    }
}

public class PlumJuice extends Drink {
    @Override
    String getInstantPackage() {
        return "速溶酸梅粉";
    }

    @Override
    String getName() {
        return "酸梅汤";
    }
}

相比上一篇简单工厂中的饮料, 我们将打包这个操作从饮料这个对象中移除了, 目前的饮料对象中只是一些简单的属性.
为什么要这么做呢? 大家可以用自己面向对象编程思想的脑洞自由发挥下, 下期说说我的想法.

现在表妹的工作就变得简单了:

public class Cousins {

    private IBeverageMachine mBeverageMachine;

    private void setBeverageMachine(IBeverageMachine machine) {
        this.mBeverageMachine = machine;
    }

    private Drink takeDrink() {
        if (mBeverageMachine == null) throw new NullPointerException("Should set Beverage Machine firstly.");

        return mBeverageMachine.makeDrink();
    }

    public static void main(String[] args) {

        Cousins cousins = new Cousins();

        // for A
        cousins.setBeverageMachine(new OrangeJuiceMachine());
        Drink drink = cousins.takeDrink();
        System.out.println(drink);

        // for B
        cousins.setBeverageMachine(new CokeMachine());
        System.out.println(cousins.takeDrink());
    }
}

当A要橙汁时, 表妹去OrangeJuiceMachine装一杯, 当B需要可乐时, 表妹再去CokeMachine拿:

This is a cup of:橙汁
This is a cup of:可乐

表妹的工作变得更简单了, 工作效率也更高了, 也更能微笑面对顾客了, 哈哈…

想要奶茶的C顾客


这天, 来了一位顾客D, 问: “老板, 有没有热奶茶啊?”. 考验小光的程序的时候到了. 很快, 小光copy出了一个奶茶机(扩展开放), 同时也无需修改原来的那些个饮料机程序(修改关闭), 不会因此而耽误生意.
新增奶茶饮料:

public class MilkTea extends Drink {
    @Override
    String getInstantPackage() {
        return "速溶奶茶粉";
    }
    @Override
    String getName() {
        return "奶茶";
    }
}

奶茶机:

public class MilkTeaMachine implements IBeverageMachine {
    @Override
    public Drink makeDrink() {
        return new MilkTea().make();
    }
}

现在的台面:

表妹的工作也很简单, 去奶茶机那儿接奶茶就是了:

// for D
cousins.setBeverageMachine(new MilkTeaMachine());
System.out.println(cousins.takeDrink());

D拿到的:

This is a cup of:奶茶

故事之后

照例, 我们先来画出现在的类对应关系:
实际上这个就是工厂方法模式.

几个点:
1, 为何叫工厂方法, 是因为每个工厂有一个方法来创建产品.
2, 每个产品对应一个工厂实例来生产这个产品实例.
3, 因为产品和其对应的工厂都与其他产品分离, 我们可以很轻易的去增加新的产品和其对应的工厂, 而不改变原来的结构. (开闭原则, 实际上还蕴含了职责单一)

扩展阅读


工厂方法模式如同Buidler模式, 是一些开源库中非常常见的用来创建实例的设计模式.
例如OkHttp中ModelLoader相关的实现:


看对应关系就跟清晰了, 就不一一类比了.
小光看着这一排饮料机, 成就感悠然而生, 仿佛又回到了那个彻夜编码的岁月.


原文地址

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

推荐阅读更多精彩内容