《Head First 设计模式》读书笔记(8)—— 模板方法模式 template

本文共2500词,阅读需要10分钟,本文相关代码地址被托管在 gitee

背景一:其实咖啡和茶的冲泡方法很相似

两者都是把水煮沸,放进去茶叶或者咖啡然后泡好倒出来以后再加入牛奶等一些配料
你可以分别写出一个做咖啡的类和泡茶的类,你会发现两者非常类似,重复代码很多,所以我们应该将这些共同的部分剥离出来,放进一个基类里。

开始改造

在方法层面找出变与不变,类似策略模式

第一种想法写一个做饮料的超类,然后一些有不同的方法就不管了,子类具体实现,而那些共有的方法则直接在超类中实现,基本就是策略模式,找出变与不变,针对接口编程等等

public class Beverage {
     void prepareRecipe(){
         boidWater();
         pourInCup();
     }

    void boidWater() {
        System.out.println("water water");
    }

    void pourInCup() {
        System.out.println("pour");
    }
}
其实不变的东西还可以挖掘的更深

比如最后放配料,都是去放,不同的配料可以用参数传进去,继续从子类中逐步抽象出更多东西,其实比如浸泡茶叶和冲泡咖啡这两个也是很像的,可以合并,加牛奶还是柠檬都是加调料也是合并
于是就是可以直接一套流程在一个超类里全部实现,暂时无法确定的方法先用抽象方法占个位置,但是好处在于这个实现一套流程的方法你就不用管了

public abstract class Beverage {
    void prepareRecipe(){
        boidWater();
        brew();
        pourInCup();
        addConditions();
    }

    protected abstract void addConditions();

    protected abstract void brew();

    void boidWater() {
        System.out.println("water water");
    }

    void pourInCup() {
        System.out.println("pour");
    }
}

子类实现就把上面的两个抽象方法实现,子类直接使用prepareRecipe()是没有任何问题的
这就是模板方法模式,prepareRecipe()就是我们的模板方法:
首先这是一个方法,然后他又被用来作为一个算法模板完成一个共同目——制作出最后的饮料。
同时在这个方法中,他被认为是一种模板,因为他只是有一组方法组合,有些自己实现,有些子类具体实现。

此时如果实例化,比如Tea myTea = new Tea();
然后调用模板方法,myTea.prepareRecipe()
煮水是直接超类实现,专门泡则是具体茶那个子类实现

定义模板方法模式

模板方法模式是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类不需要改变任何算法结构,只是在完成算法中某些步骤。

个人想法--车有些了,你看着买轮子吧

其实和策略模式在解耦方面其实差不多,都是使用超类来实现共用重复代码,类之间的继承结构也基本相同,但是模板方法模式最特殊的我认为是在模板两个字,模板以为你不用在想具体流程,只是实现就好,模板已经想好了结构是什么,模板已经规定好了1234,你只是根据具体情况实现某个1或者3就好;但是策略关键在于不用的就定个接口,然后组合进来

抽象方法中的挂钩把戏

钩子是一种被声明在抽象类中的方法,但是只有空或者默认的实现。
钩子的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自己决定。

比如加调料这种事需要用户自己确定加不加,于是可以写个返回flag的方法结合条件判断来实现
这个返回flag的方法就是钩子,比如超类关于addCondiments()这个方法统一返回“加”,那具体实现的时候可以覆盖这个钩子方法,具体在判断加不加

真实api中这样的情况应该是很多的,以后我要留意一下

好莱坞原则——别调用我们,我们会调用你

个人感觉这也是区别策略模式的根本,也是能够定义算法结构的原因和保证
当使用模板方法模式时,我们告诉子类,不要来调用我们,我们都弄好了,你把你的干好就行了,用你的时候通知你

工厂方法和观察者模式也采用了好莱坞原则。
好莱坞原则成功避免了就是“依赖腐败”现象,例如超类调用子类的方法,子类也调用超类的方法,然后其他子类和这些又有调用关系,反正就是乱调,开发费劲,修改复杂。
于是定一条规定,我们不要毛遂自荐的,我们要用你的话会跟你说,就像大牌导演和十八线演员的关系。

饮料就是大牌导演,具体的咖啡和茶就是一个小演员,小演员只需要完成自己的工作,这些工作在导演那都已经规划好了。

好莱坞 与 依赖倒置

依赖倒置原则教我们尽量避免使用具体类,而多使用抽象
而好莱坞原则是用在创建框架或组件上的技巧,低层的被当作挂钩放进去,但是高层不会依赖低层
两者都是在于解耦,但是好莱坞原则强调低层之间可以互相操作,而防止其他类太过依赖

使用模板方法的接口或者类:Comparable接口 Comparator接口 Arrays类

钩子方法:compareTo()\compare()
只要实现Comparable接口的compareTo()方法,在使用相关方法时,如果传的参数是comparable接口的子类,就可以实现

Arrays类中的静态方法sort本身就是模板方法,需要参数,这个参数如果不用comparator接口,就是默认升序的规则

然后也可以使用两个参数,像上面,而compartor接口也具有模板方法。

Arrays.sort的目标就是希望能够满足任意数组并且满足任意排序规则,所以利用模板方法的思想,虽然并不想前面的使用方式,但是本质很像

边角料

  • 策略模式很像,两者也不要过度区分,但是其不同主要在于组合的子类是否完全实现整个算法。有的时候就是他自己一套算法,他又另外一套算法,这时候用模板方法就是作茧自缚,使用策略模式的委托机制就很有弹性很灵活
  • java.io的InputStream的read方法,也是使用了模板方法,具体怎么读可以自定义
  • 工厂方法就是模板方法的特殊版本。

发表评论

电子邮件地址不会被公开。