Template Method——模板方法模式

什么是模板方法模式

  1. 在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为模板方法模式,模板方法通过把不变的行为移到父类,去除子类中的重复代码,提高代码复用率。<br />
  2. 如果要完成在某**_一些细节层次一致的一个过程或一系列步骤,旦其个别步骤在更详细的层次上的实现可能不同_**时,可以考虑用模板方法模式。

模板方法模式中的角色构成

模板方法模式类图

TemplateMethod模式类图

  • AbstractClass (抽象类)
    1. AbstractClass角色负责实现模板方法以及声明模板方法中所使用到的抽象方法,这些抽象方法由子类ConcreteClass角色负责实现;模板方法作为具体方法,会给出一个顶级逻辑骨架,逻辑的组成步骤在相应的抽象方法中,会在子类中具体实现。在顶级逻辑中也可能调用一些具体方法。<br />
    注意:模板方法不应被重写,所以应使用final修饰符
  • ConcreteClass (具体类)
    1. ConcreteClass角色负责具体实现AbstractClass角色中定义的抽象方法,具体类中实现的方法将会在抽象类的模板方法中被调用;每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

示例程序

类组成表:

类名 说明
AbstractDisplay 包含open(),print(),close()三个抽象方法及模板方法display()
CharDisplay 继承了AbstractDisplay类,实现了open(), print(), close()方法
StringDisplay 继承了AbstractDispaly类,实现了open(), print(), close() 方法
TemplateMain 测试类

示例程序类图

TemplateMethod模式示例程序类图

AbstractDisplay类:
  1. AbstractDispaly类中有四个方法,具体实现的只有display()方法,其中open()、print()、 close()为抽象方法,方法的实际处理交给了继承的子类。
  1. package com.example.templatemethod;
  2. public abstract class AbstractDisplay {
  3. public abstract void open(); //交给子类去实现的抽象方法(1)open()
  4. public abstract void print(); //交给子类去实现的抽象方法(2)print()
  5. public abstract void close(); //交给子类去实现的抽象方法(3)close()
  6. public final void display() { //本抽象类中实现的display()方法
  7. open();
  8. for (int i = 0; i < 5; i++) {
  9. print();
  10. }
  11. close();
  12. }
  13. }

CharDisplay类:
    AbstractDisplay类的一个子类,具体了实现父类中的三个抽象方法
package com.example.templatemethod;

public class CharDisplay extends AbstractDisplay{
    private char cd;

    public CharDisplay(char cd) {
        this.cd = cd;
    }

    @Override
    public void open() {
        System.out.print("<<");
    }

    @Override
    public void print() {
        System.out.print(cd); //显示构造函数接收的一个字符
    }

    @Override
    public void close() {
        System.out.println(">>");
    }
}

StringDisplay类:
    AbstractDisplay类的另一个子类,实现了父类中的三个抽象方法,其具体实现与CharDisplay有所不同
package com.example.templatemethod;

public class StringDisplay extends AbstractDisplay{
    private String string;  //需要显示的字符串
    private int width;      //以字节为单位计算出的字符串长度

    public StringDisplay(String string) {
        this.string = string;
        this.width = string.getBytes().length;
    }

    @Override
    public void open() {
        printLine();
    }

    @Override
    public void print() {
        System.out.println("|" + string + "|"); //显示构造函数接受的字符串,并在前后加上"|"
    }

    @Override
    public void close() {
        printLine();
    }

    private void printLine() {
        for (int i = 0; i < width; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }
}

TempalteMain类:
    测试类
package com.example.templatemethod;

public class TemplateMain {
    public static void main(String[] args) {
        AbstractDisplay ad1 = new CharDisplay('Q');
        AbstractDisplay ad2 = new StringDisplay("hello,world");
        AbstractDisplay ad3 = new StringDisplay("String3");

        ad1.display();
        ad2.display();
        ad3.display();
    }
}

执行结果:
<<QQQQQ>>
-----------+
|hello,world|
|hello,world|
|hello,world|
|hello,world|
|hello,world|
-----------+
-------+
|String3|
|String3|
|String3|
|String3|
|String3|
-------+

拓展延伸

类的层次

    通常在理解类的层次时,容易站在子类的角度进行思考:
  • 在子类中可以使用父类中的哪些方法
  • 在子类中可以增加什么方法以实现新的功能
  • 在子类中重写父类的方法可以改变哪些程序行为
    转换角度,站在父类的角度进行思考,通过声明抽象方法,将方法的具体实现交给子类。也就是说子类具有实现在父类中所声明的抽象方法的责任,这种责任被称为“子类责任”(subclass reponsibility)

InputStream中的模板方法

    java.io.InputStream中有一个抽象方法read(),它会被InputStream的模板方法read(byte b[], int off, int len) 循环调用。InputStream的子类负责具体实现"读取一个字节"的操作,而InputStream中定义的模板方法只负责"将指定数量的字节读取到数组中的指定位置"的操作。
public abstract int read() throws IOException;

```java 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; }



<a name="fb4653a6"></a>
#### 画蛇添足

        尝试将抽象类改写成接口,将其中的模板方法改写成Java 8加入的接口默认方法

<a name="e4b59405"></a>
##### IAbstractDisplay接口:

        包含三个抽象方法open()、print()、close(),一个默认方法disPlay()替换原抽象类中的模板方法disPlay()

```java
package com.example.templatemethod.interfaceImpl;

public interface IAbstractDisplay {
    void open();
    void print();
    void close();

    default void disPlay() {
        open();
        for (int i = 0; i < 5; i++) {
            print();
        }
        close();
    }
}

CharDisPlayImpl类:
    IAbstractDisplay接口的一个实现类,具体实现了接口当中的三个抽象方法
package com.example.templatemethod.interfaceImpl;

public class CharDisplayImpl implements IAbstractDisplay{
    private char cd;

    public CharDisplayImpl(char cd) {this.cd = cd;}

    @Override
    public void open() {
        System.out.print("<<");
    }

    @Override
    public void print() {
        System.out.print(cd);
    }

    @Override
    public void close() {
        System.out.println(">>");
    }
}

StringDisplayImpl类:
    IAbstractDisplay接口的另一个实现类,实现了接口当中的三个抽象方法,与CharDisplayImpl的具体实现不同

InterfaceImplMain类:
    测试类
package com.example.templatemethod.interfaceImpl;

public class InterfaceImplMain {
    public static void main(String[] args) {
        IAbstractDisplay ad1 = new CharDisplayImpl('Q');
        IAbstractDisplay ad2 = new StringDisplayImpl("hello,world");
        IAbstractDisplay ad3 = new StringDisplayImpl("String3");

        ad1.disPlay();
        ad2.disPlay();
        ad3.disPlay();
    }
}

执行结果:
<<QQQQQ>>
-----------+
|hello,world|
|hello,world|
|hello,world|
|hello,world|
|hello,world|
-----------+
-------+
|String3|
|String3|
|String3|
|String3|
|String3|
-------+