设计模式之模板方法

一、模板方法模式(封装算法)定义:

        模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

      钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类进行决定。
子类可以覆盖钩子方法,但是也可以什么也不做,这些都是由子类自己进行决定,我们应该保持抽象方法的数目越少越好,否则,在子类中实现这些方法将会很麻烦。在写模板方法的时候,心里一定要随时记得这一点,想要做到这一点,可以让算法内的步骤不要切割的太细,但是如果步骤太少的话,会比较没有弹性,所以要视具体情况而定,此外也要记住这一点,某些步骤是可选的,所以你可以将这些步骤实现成钩子,而不是实现成抽象方法,这样就可以让抽象类的子类的负荷减轻。

二、模板方法类图

问题1:当你创建一个模板方法时,怎么才能知道什么时候该使用抽象方法,什么时候使用钩子?
解答:当你的子类对于某个方法或者步骤的实现,是依赖与自身特性相关的,就必须使用抽象方法。如果方法或者步骤是可选的,那就用钩子。如果是钩子的话,子类可以选择实现这个钩子,但并不强制子类一定要这么做。

三、OO 原则和好莱坞原则

OO原则:
    封装变化;多用组合,少用继承;针对接口编程,不针对实现编程;为交互对象之间的松耦合设计而努力;类应该对扩展开放,对修改关闭;依赖抽象,不要依赖具体;只和朋友交谈;别找我,我会找你

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

    好莱坞原则可以给我们一种防止“依赖腐败”的方法。当高层组件依赖底层组件,而底层组件又依赖高层组件时,而高层组件又依赖边侧组件,而边侧组件又依赖底层组件时,依赖腐败就产生了。在这样的情况下没有人可以轻易地搞清楚系统的设计原理。但是在好莱坞原则下,我们允许底层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些底层组件。简而意之,高层组件对底层组件的方式是“别调用我们,我们会调用你的”

四、问题:
(1)好莱坞原则和依赖倒置原则之间关系?
    依赖倒置原则是让我们尽量避免使用具体类,而多使用抽象。而好莱坞原则是用在创建框架或者组件上的一种技巧,好让底层组件能够被挂钩进行计算中,而且又不会让高层组件依赖底层组件。两者的目标都是在于解耦,但是依赖倒置原则更加注重如何在设计中避免依赖。好莱坞原则则是教会我们一个技巧,创建一个有弹性的设计,允许底层结构能够相互操作,而又防止其他类太过依赖它们。

(2)底层组件不可以调用高层组件中方法吗?
    并非如此,事实上,底层组件在结束时,常常会调用从超类中继承来的方法。我们所要做的是,避免让高层和底层组件之间有明显的环状依赖。

(3)在Java API中,有哪些使用了模板方法?
java.io的InputStream类的read()方法,是由子类实现的,而这个方法又会被read(byte b[],int off,int len)模板方法使用。代码如下:

 /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the stream
     * has been reached, the value <code>-1</code> is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * <p> A subclass must provide an implementation of this method.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public abstract int read() throws IOException;

  

public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

五、模板发放模式和策略模式对比
(1)策略模式定义一个算法簇,并让这些算法可以互换。正因为每个算法都是被封装起来的,所以对外可以轻易的使用不同的算法。
(2)模板方法模式定义一个算法的大纲,而由子类定义其中的具体实现,所以在算法中可以有不同的实现细节,但是整体的算法的结构依然维持不变。
(3)策略模式采用的是组合的方式实现,而模板方法模式采用的是继承的方式实现
(4)模板方法模式,除了极少的部分之外,它的算中每个部分都是相同的,会重复使用到的代码都被放进了超类中,让所有的子类共享。策略模式采用的是组合的方式,所以更加有弹性,可以在运行时改变他们的算法,而只需要改用不同的策略对象即可。
(5)模板方法模式在超类中提供了一个基础的方法,达到代码复用,并允许子类指定行为,由此它的依赖程度会比策略模式高。

六、总结
1).模板方法定义了算法的步骤,把这些步骤的实现延迟到子类。
2).模板方法模式为我们提供了一种代码复用的重要技巧。
3).模板方法的抽象类可以定义具体方法,抽象方法和钩子。
4).抽象方法由子类实现
5).钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它。
6).为了防止子类改变模板方法中的算法,可以将模板方法申明成final
7).好莱坞原则告诉我们,将决策权放在高层模板中,以便决定如何以及何时调用底层模块
8).你将在真实世界代码中看到模板方法模式的许多变体,不要期待它们全都是一眼就可以被你认出的。
9).策略模式和模板方法模式都是封装算法,一个用组合一个是用继承
10).工厂方法时模板方法的一种特殊版本

原文地址:https://www.cnblogs.com/lovegrace/p/12218294.html