设计模式:Template Method (模板方法)

137 阅读3分钟

所谓设计模式,其实就是编程中的常用"套路"。

今天我们说一下一个叫做模板方法(Template Method)的套路。

首先看一道经典的数学题,1 + 2 + ... + 99 + 100,这是很多人小学时就听过的数学家小高斯智斗迂腐老师啪啪打脸的故事。不过今天我们不采用高斯当时的等差数列求和法,而是直接采用累加法,毕竟又不是人算,而是机算。

首先带大家快速写出第一版:

// Accumulator.java
public class Accumulator {
    public void accumulation(final int min, final int max) {
        int result = 0;
        int summand = min; // 被加数

        while (summand <= max) {
            result += summand;
            summand++;
        }

        System.out.println(min + " + ... + " + max + " = " + result);
    }

    public static void main(String[] args) {
        (new Accumulator()).accumulation(1, 100);
    }
}

执行一下,输出

1 + ... + 100 = 5050

以上就是第一版,不知道各位同学在上面代码中有没有发现什么熟悉的"套路"?没有,那我们再来看几个栗子

  1. 读取一个文件的每一行
初始化文件句柄
while (文件还没到结尾) {
	读一行
	游标下移一行
}
关闭文件句柄
  1. 网络请求
初始化网链接,拿到连接句柄
while (还有内容) {
	读一行
	游标下移一行
}
关闭网络连接
  1. 读取一个数据库查询集
初始化数据库连接
while (还有数据) {
	读一条记录
	游标移到下一条记录
}
关闭数据库连接

这下是不是看出点什么,不同的业务场景,但是都有类似的业务逻辑顺序。我们尝试抽象一下相同的地方。

初始化
while (未完成) {
	循环
} 
结束

写成代码就是

init(); // 初始化
while (isUnfinish()) { // 判断是否结束
    execute(); // 没结束的情况下继续处理
}
finish(); // 收尾工作

再进一步,我们把这个模板方法,给封装成一个类。其中一些抽象方法需要由子类根据自己的业务逻辑去实现。

//Application.java
public abstract class Application {
    protected abstract void init();
    protected abstract void execute();
    protected abstract void finish();
    protected abstract boolean isUnfinish();

    // 这就是模板
    public final void run() {
        init();
        while (isUnfinish()) {
            execute();
        }
        finish();
    }
}

所谓套路,就是可以让你下次再遇到这样的问题,不再做重复的事情。接下来我们用着个模板,来实现第二版的累加器,来计算1到100的数列求和。

// Accumulator.java
public class Accumulator extends Application {
    private int min;
    private int max;
    private int summand;
    private int result;

    public Accumulator(int min, int max) {
        this.min = min;
        this.max = max;
    }

    @Override
    protected void init() {
        summand = min;
        result = 0;
    }

    @Override
    protected boolean isUnfinish() {
        return summand <= max;
    }

    @Override
    protected void execute() {
        result += summand;
        summand++;
    }

    @Override
    protected void finish() {
        System.out.println(min + " + ... + " + max + " = " + result);
    }

    public static void main(String[] args) {
        Application app = (new Accumulator(1, 100));
        app.run();
    }
}

以上代码,依旧是输出:

1 + ... + 100 = 5050

模板方法就是给你一个模子,然后你去填充它就好了。

同样,我们使用这个模板方法"套路",来实现另一个应用,读取一个http链接的每一行。其实也只需要继承 Application 基类,重写实现模板中的方法就行了。

// HttpLineReader
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpLinkReader extends Application {
    private String httpurl;
    private HttpURLConnection connection = null;
    private InputStream is = null;
    private BufferedReader br = null;
    private String result = "";// 返回结果字符串
    private StringBuffer sbf;
    private boolean isUnfinish;

    public HttpLinkReader(String httpurl) {
        this.httpurl = httpurl;
    }

    @Override
    protected void init() {
        try {
            URL url = new URL(httpurl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();
            is = connection.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            sbf = new StringBuffer();
            isUnfinish = true;
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected boolean isUnfinish() {
        return isUnfinish;
    }

    private void setFinish() {
        isUnfinish = false;
    }

    @Override
    protected void execute() {
        try {
            String temp = br.readLine();
            if (temp == null) {
                setFinish();
                return;
            }
            sbf.append(temp);
            sbf.append("\r\n");

            result = sbf.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void finish() {
        try {
            br.close();
            is.close();
            connection.disconnect();

            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        String httpUrl = "http://ihaohong.coding.me/pages/test/test01.txt";
        Application app = new HttpLinkReader(httpUrl);
        app.run();
    }
}

输出内容就是我们url返回的内容。

嗯,Template Method 模式就到这里了。

引用原文