统一热干面的制作流程---模板方法

cover

前情提要

上集讲到, 小光引入了日报制度, 用来从各个分店店长那儿收集信息. 如此一来, 小光每天就通过日报系统了解到各个分店的销售情况, 问题所在, 也好根据收集到的用户反馈来改善系统, 改善经营了.

不多久, 就有一个来自用户的反馈, 通过店长的日报到了小光这边:

有用户反馈不同分店的热干面味道不太一样

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

统一热干面的制作流程

小光是想将品牌做大, 做统一的. 当然是需要在任何分店给用户都是统一的体验啊. 所以, 小光很重视这个问题, 马上投入了这个问题的解决中.

上次在弄分店的标准时并没有统一规划下热干面的制作方法流程, 现在小光想借这个机会正好弄一套, 于是小光做了一个热干面的制作规范:

public class HotDryNoodlesMaker {

    public void make() {
        // 1, 烫面
        tangmian();

        // 2, 装碗
        zhuangwan();

        // 3, 加调料(盐,鸡精,胡椒粉之类)
        jiatiaoliao();

        // 4, 加芝麻酱
        jiazhimajiang();
    }

    // 原谅我, 这些个英文真是不知道怎么说了, 以下方法名用拼音吧...
    // 非我所愿

    private void tangmian() {
        System.out.println("热干面入沸水锅焯烫十几秒");
    }

    private void zhuangwan() {
        System.out.println("热干面捞出, 装入大碗中");
    }

    private void jiatiaoliao() {
        System.out.println("加调料");
    }

    private void jiazhimajiang() {
        System.out.println("加芝麻酱");
    }
}

各分店都用这套流程来制作热干面:

public class XiaoGuang {

    public static void main(String[] args) {

        HotDryNoodlesMaker maker = new HotDryNoodlesMaker();
        maker.make();
    }
}
热干面入沸水锅焯烫十几秒
热干面捞出, 装入大碗中
加调料
加芝麻酱

看起来没有问题. 小光是个谨慎的人, 在全面启用这套流程之前, 小光决定现在光谷店试用下.

试用出了问题

果然, 只有经过用户的检验的产品才是好产品, 刚试用第一天, 小光的热干面制作流程就经受考验, 出了问题.

吃客们, 有的是在店吃的, 有的是打包带走的.
当初小光为了品牌性, 在店吃都是用的带有小光热干面专属标志的消毒碗的, 外带是定制的一次性碗具

现在在"装碗"这个环节出了问题~~

解决问题

解决问题的第一步是描述问题.

小光深入思考, 探索问题的本质, 发现现在的问题是: 热干面制作必须是这个流程, 而中间的某些步骤又必须可以根据实际场景改变. 也就是说, 不能改变热干面的制作流程, 但是又可以改变(重新定义)这个流程中的某一步(装碗).

如此, 小光想到了一个办法:

用一个抽象的类来定义操作步骤(当然并不会抽象所有的步骤), 由具体的子类来实现中间需要定制的步骤.

抽象的制作流程:

public abstract class Maker {

    public void make() {
        // 1, 烫面
        tangmian();

        // 2, 装碗
        zhuangwan();

        // 3, 加调料(盐,鸡精,胡椒粉之类)
        jiatiaoliao();

        // 4, 加芝麻酱
        jiazhimajiang();
    }

    private void tangmian() {
        System.out.println("热干面入沸水锅焯烫十几秒");
    }

    private void jiatiaoliao() {
        System.out.println("加调料");
    }

    private void jiazhimajiang() {
        System.out.println("加芝麻酱");
    }

    // 将装碗这一步抽象出来, 由具体的子类实现
    abstract void zhuangwan();
}

然后分别弄一个对应的打包的maker和一个堂食的maker:

// 打包
public class PackingMaker extends Maker {

    @Override
    void zhuangwan() {
        System.out.println("热干面捞出, 装入一次性碗");
    }
}

// 堂食
public class EatInMaker extends Maker {

    @Override
    void zhuangwan() {
        System.out.println("热干面捞出, 装入店内消毒碗");
    }
}

如此这般, 制作的流程有maker控制, 但是也给了不同实现(打包, 堂食)一些自主化空间:

Maker packingMaker = new PackingMaker();
packingMaker.make();

// output
热干面入沸水锅焯烫十几秒
热干面捞出, 装入一次性碗
加调料
加芝麻酱

Maker eatInMaker = new EatInMaker();
eatInMaker.make();

// output
热干面入沸水锅焯烫十几秒
热干面捞出, 装入店内消毒碗
加调料
加芝麻酱

达成要求.

故事之后

照例我们先缕缕类的关系:


在故事过程中, 我们描述了问题及其解决之道. 实际上这个解决之道就是我们今天要说的模板方法模式.

模板方法模式
定义一个操作中的算法骨架(热干面的制作流程), 而将某些步骤实现延迟到子类中. 使得子类可以根据实际情况不改变算法骨架(热干面的制作流程), 但是可以重新定义或改变该算法中的某些特定步骤(例如装碗).

扩展阅读一

模板方法是代码复用的基本技术, 基本上随处可见. 我们平常编码上, 如果想复用一些代码, 基本上第一个想到的就是提取父类, 抽象可变, 然后子类实现抽象部分的方式来复用. 没错, 这种用法往往你就用到模板方法模式.

所以说, 设计模式并不高深, 它实际上就是一种良好编程习惯的提炼. 我们可能随处再用, 只是有的时候我们并没有意识到.

这种代码复用的方式在一些底层库, SDK中更是被频繁使用. 例如Android中最为复杂之一的View, 就用到木板方法模式.

其draw()方法就是模板方法, onDraw()是抽象的可定制的原语操作:

// 代码节选自View.java

// draw()方法相当于定义了一个View的绘制算法结构, 其中的onDraw的可变的原语操作.
public void draw(Canvas canvas) {
  
   // Step 1, draw the background, if needed
   int saveCount;

   if (!dirtyOpaque) {
       drawBackground(canvas);
   }

   // skip step 2 & 5 if possible (common case)
   final int viewFlags = mViewFlags;
   boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
   boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
   
   if (!verticalEdges && !horizontalEdges) {
       // Step 3, draw the content
       if (!dirtyOpaque) onDraw(canvas);

       // Step 4, draw the children
       dispatchDraw(canvas);

       // Step 6, draw decorations (scrollbars)
       onDrawScrollBars(canvas);

       if (mOverlay != null && !mOverlay.isEmpty()) {
           mOverlay.getOverlayView().dispatchDraw(canvas);
       }

       // we're done...
       return;
   }
}  

/**
* Implement this to do your drawing.
*
* @param canvas the canvas on which the background will be drawn
*/
// 原语操作, 由子类实现, 来定制
protected void onDraw(Canvas canvas) {
} 

扩展阅读二

很多继承关系中, 我们都能找到模板方法的影子, 但是并非所有的继承就是用到了模板方法模式. 有的同学可能会回想起我们之前提到的策略模式. 二者看起来很像啊, 貌似都是跟替换算法有关的.

在这里简单区别下二者:

二者都是通过用继承关系来达到"算法替换"的目的.
但是模板方法是不改变算法结构, 只替换/改变其中的步骤.
而策略模式是改变了整个算法.

打个比方, 我从北京到武汉的过程, 定义一个回家算法 --- 做火车回家, 分成几步:

  1. 上火车站买票
  2. 检票上车
  3. 一路顺风回家

如果我改变了买票的方式, 例如我不想去火车站买票了, 在网上买, 或是电话买. 但是算法结构不变, 还是做火车, 还是这三步, 那么这个就是模板方法模式.

但是如果我不想做火车了, 我开车, 我坐飞机回家, 那么就相当于替换了整个算法, 这个就是策略模式.


总之, 小光又解决了一个问题, 解决用户痛点是产品呢生存之道, 小光知道自己的热干面正在变得越来越好...哈哈.

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,497评论 25 707
  • 初中生们要毕业了 我们看来十万火急 他们看来…… 他们看不来什么东西
    沈沈家阅读 128评论 0 0
  • 你在我心中很大很大 比最大的大还大 我在你心中很小很小 比最小的小还小 但是真的无所谓 因为—— 我爱你
    光速太慢阅读 238评论 0 0
  • Java socket应用—通信 URL 1、URL(Uniform Resource Locator)统一资源定...
    少冰三hun甜阅读 605评论 0 0