面向对象编程内功心法系列十四(聊一聊模板模式)

1.引子

上一篇我们分享了行为型设计模式中的观察者模式,这一篇我们接着来聊一聊模板设计模式,模板设计模式,全名叫做模板方法设计模式

模板设计模式,是一个在实际应用中应用得非常广泛的设计模式。它的字面语义可以这么去理解,利用面向对象编程的继承特性,在父类中定义算法的整体骨架(即模板方法),将可变部分延迟到子类中去实现(即扩展点)

因此,通过模板设计模式,我们可以获得的直观收益有:复用性、及扩展性

在今天的文章中,我将通过实际案例给你演示模板设计模式的复用性以及扩展性。并且我们说模板设计模式应用非常广泛,非常适合应用于封装框架层面,我还将带着你一起看一看jdk的相关源码,看一看jdk类库中模板设计模式的一些应用案例。比如说抽象类机制、IO流、Servlet等。

这些都是你所熟悉的对吧,那就让我们开始吧。

2.案例

2.1.模板模式样例代码

关于模板设计模式,你需要关注三个点:

  • 利用继承关系
  • 在父类中,定义算法骨架,即模板方法
  • 在子类中,实现可变部分,即实现扩展点
csharp 复制代码
// 父类
public abstract class Parent{
    //定义算法骨架(模板方法)
    public void skeletonMethod(){
        ......
        // 扩展点1
        extMethod1();
        
        // 扩展点2
        extMethod2();
        ......    
    }
    
    // 抽象方法扩展点1,延迟到子类实现
    public abstract void extMethod1();
    // 抽象方法扩展点2,延迟到子类实现
    public abstract void extMethod2();
}
​
// 子类
public class Sub extends Parent{
    // 重写扩展点1
    @Overried
    public  void extMethod1(){
        // 子类实现扩展点1,具体业务逻辑
    }
    
    // 重写扩展点2
    @Overried
    public  void extMethod2(){
        // 子类实现扩展点2,具体业务逻辑
    }
    
}
​
// 应用代码
public class Test{
    public static void main(String[] args){
        Parent obj = new Sub();
        obj.skeletonMethod();
    }
}

模板方法设计模式的代码实现非常简单,相信你一看就能明白。我们在父类Parent中定义了算法骨架方法skeletonMethod(),将扩展点方法extMethod1()、extMethod2()延迟到了子类Sub中去实现。

简单的代码实现,完全体现了模板设计模式的精髓:复用性,及扩展性

  • 利用继承关系,收益代码的复用性,所有子类复用skeletonMethod方法
  • 将可变部分,延迟到子类实现,收益代码的扩展性,扩展点extMethod1、extMethod2方法

2.2.IO类库中模板模式的应用

关于jdk的IO类库,你不应该感到陌生,在大多数应用中几乎都会涉及到。从字节流InputStream、OutputStream,到字符流Reader、Writer。并且在前面结构型设计模式中,我们从装饰器设计模式的角度分享过,今天我们从模板设计模式的角度,再来看一看IO类库中,是如何使用模板设计模式的。

我将抽取InputStream中的read方法,带着你一起来看一看。我在代码中配备了相关的注释,方便你理解

java 复制代码
// 抽象父类InputStream
public abstract class InputStream implements Closeable {
    ......省略其它代码
    // 算法骨架,模板方法
    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;
        }
        // 【重点关注】调用扩展点 read方法
        int c = read();
        // 读取不到数据,返回 -1
        if (c == -1) {
            return -1;
        }
        
        // 将读取到的字节数据,存入b字节数组中
        b[off] = (byte)c;
​
        int i = 1;
        try {
            // 循环读取数据
            for (; i < len ; i++) {
                // 【重点关注】,调用扩展点 read方法
                c = read();
                // 数据读取完毕,退出循环
                if (c == -1) {
                    break;
                }
                // 将读取到的字节数据,存入b字节数组中
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        // 返回读取到的字节数量
        return i;
    }
    
    // 扩展点,延迟到子类实现
    /*
    *<p> A subclass must provide an implementation of this method.
    *翻译官方英文注释:子类必须提供read()方法的实现
    */
    public abstract int read() throws IOException;
    
    ......省略其它代码
}

2.3.Web Servlet中模板模式的应用

从许多java开发朋友来说,我们大多数都是java web开发,那么对于Servlet我们都会非常熟悉。不管是早些时候直接写过Servlet、jsp的朋友,还是目前基于框架开发的朋友(struts、springmvc、springboot),我们对Servlet都一定是在人群中多看过几眼的对吧

那么你还记得,基于Servlet开发,有哪些关键步骤吗?

  • 编写一个业务Servlet,继承自HttpServlet
  • 在业务Servlet中,重写doGet,doPost方法
  • 在web.xml文件中,配置servlet mapping映射

你看就是这么简单,当然今天我们基于框架开发,这些事情都不需要做了,由框架代劳。但是我还是想建议你关注一下框架的底层实现,万变不离其宗,懂了底层才算是打通了任督二脉,也才算得上是一个合格的技术开发人员。比如如果你们的项目使用了springmvc,你可以去看看DispatcherServlet的源代码,并且关注一下原生Servlet的体系结构,我截一个Servlet的类结构图给你作为参考

接下来,我们一起来看一下Servlet中模板模式的应用,代码比较简单,比较好理解,我在关键点配备了注释

kotlin 复制代码
// Servlet接口
public interface Servlet {
    // service方法
     void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
​
// 抽象类
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    // service方法未实现,标记为抽象方法
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
​
// 提供给我们继承的父类HttpServlet
public abstract class HttpServlet extends GenericServlet {
    ...省略其它代码
    // 实现了service方法,该方法即模板模式中的算法骨架
    // 根据http请求方法中的method属性,调用扩展点方法:
    // doGet/doHead/doPost/doPut/doDelete/doOptions/doTrace
    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) {
                    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);
        }
​
    }
    ...省略其它代码
}
相关推荐
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
WaaTong3 小时前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠5 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#