天天看點

模闆方法模式

定義

定一個操作中的算法的骨架,而将一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

模闆模式中有兩個角色,抽象模闆角色定義了一個或多個抽象操作以便讓子類實作,定義并實作了一個模闆方法,這個方法包含了一些不可改變的方法執行順序。

具體模闆角色實作父類定義的一個或多個抽象方法,每一個抽象模闆角色都可以有任意多個具體模闆角色與之對應,而每一個具體模闆角色都可以給出這些抽象方法的不同實作。

模闆模式中,在抽象模闆中定義了方法的執行順序,而方法的實作可以選擇讓子類去定制化;另外,統一的不變的方法可以放在抽象模闆類中,這樣子類可以共用。

模闆模式中還經常會出現鈎子方法,一般是由抽象類給出方法的空實作,然後子類進行覆寫。這種空的鈎子方法叫做“Do Nothing Hook”。

鈎子方法放到模闆方法中,就可以實作生命周期中方法的自定義,例如beforeCreate、beforeUpdate、afterUpdate、afterCreated等。

Java中的模闆模式

HttpServlet中應用了模闆方法模式,其中HttpServlet是抽象模闆角色。

service()是抽象方法,do開頭的方法,例如doGet、doPost是可以由子類定制化的方法。

service()方法如下:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }           

方法的執行上下文在service()規定好,剩下的doGet等方法可以自定制。

這裡的模闆方法service沒有final修飾,是以這個方法實際上也能進行定制。

模闆模式執行個體

場景:寫作文,作文是有固定模闆的,例如必須有開頭,内容主體和結尾。并且三者編寫的順序不能打亂,不能出現先寫結尾再寫開頭的情況。

三者的順序就是算法的骨架,而三者的具體實作就會由具體子類去定制化實作,因為每個人寫出的作文内容都是不一樣的。

根據目前場景設計抽象模闆角色:

package com.faith.net.strategy.pay.template;

/**
 * 抽象模闆類
 */
public abstract class Article {

    /**
     * 模闆方法,定義了算法骨架,執行順序
     */
    public final void writeArticle() {
        beforeWriteHook();
        writeHead();
        writeBody();
        writeTail();
        afterWriteHook();
    }

    /**
     * 寫文章開頭
     */
    public abstract void writeHead();

    /**
     * 寫文章主體
     */
    public abstract void writeBody();

    /**
     * 寫文章結尾,不一定非得是abstract修飾,隻要能被子類重寫就可以
     */
    protected void writeTail() {
        System.out.println("~~~~~~~~~~~~~~");
    };

    /**
     * 子類公用的方法
     */
    public void writeSeparateLine() {
        System.out.println("~~~~~~~~~~~~~~~~我是分隔線~~~~~~~~~~~~~~~~~~");
    }

    /**
     * 前置鈎子方法
     */
    public void beforeWriteHook() {
        System.out.println("醞釀一下感情。。。");
        System.out.println();
    }

    /**
     * 後置鈎子方法
     */
    public void afterWriteHook() {
        System.out.println();
        System.out.println("寫完了,欣賞一下");
    }
}           

添加具體實作,登黃鶴樓和登金陵鳳凰台。

package com.faith.net.strategy.pay.template;

/**
 * 登黃鶴樓 崔颢
 */
public class HuangHeLou extends Article {

    @Override
    public void writeHead() {
        System.out.println("昔人已乘黃鶴去,此地空餘黃鶴樓。");
    }

    @Override
    public void writeBody() {
        System.out.println("黃鶴一去不複返,白雲千載空悠悠。");
        System.out.println("晴川曆曆漢陽樹,芳草萋萋鹦鹉洲。");
    }

    @Override
    public void writeTail() {
        System.out.println("日暮鄉關何處是?煙波江上使人愁。");
    }

    @Override
    public void beforeWriteHook() {
        System.out.println("崔颢醞釀一下感情。。。");
        System.out.println();
    }

    @Override
    public void afterWriteHook() {
        System.out.println();
        System.out.println("崔颢寫完了,欣賞一下");
    }
}           
package com.faith.net.strategy.pay.template;

/**
 * 登金陵鳳凰台 李白
 */
public class FengHuangTai extends Article {

    @Override
    public void writeHead() {
        System.out.println("鳳凰台上鳳凰遊,鳳去台空江自流。");
    }

    @Override
    public void writeBody() {
        System.out.println("吳宮花草埋幽徑,晉代衣冠成古丘。");
        System.out.println("三山半落青天外,二水中分白鹭洲。");
    }

    @Override
    public void writeTail() {
        System.out.println("總為浮雲能蔽日,長安不見使人愁。");
    }

    @Override
    public void beforeWriteHook() {
        System.out.println("李白醞釀一下感情。。。");
        System.out.println();
    }

    @Override
    public void afterWriteHook() {
        System.out.println();
        System.out.println("李白寫完了,欣賞一下");
    }
}           

測試類:

package com.faith.net.strategy.pay.template;

/**
 * 測試類
 */
public class TemplateTest {

    public static void main(String[] args) {
        HuangHeLou huangHeLou = new HuangHeLou();
        huangHeLou.writeArticle();

        System.out.println();System.out.println();System.out.println();

        FengHuangTai fengHuangTai = new FengHuangTai();
        fengHuangTai.writeArticle();
    }
}