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

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);
        }
​
    }
    ...省略其它代码
}
相关推荐
Asthenia04122 小时前
浏览器缓存机制深度解析:电商场景下的性能优化实践
后端
专注API从业者3 小时前
分布式电商系统中的API网关架构设计
大数据·数据仓库·分布式·架构
databook4 小时前
『Python底层原理』--Python对象系统探秘
后端·python
超爱吃士力架5 小时前
MySQL 中的回表是什么?
java·后端·面试
付聪12105 小时前
装饰器模式
设计模式
扣丁梦想家5 小时前
设计模式教程:外观模式(Facade Pattern)
设计模式·外观模式
強云5 小时前
23种设计模式 - 装饰器模式
c++·设计模式·装饰器模式
強云5 小时前
23种设计模式 - 外观模式
设计模式·外观模式
uhakadotcom5 小时前
Google DeepMind最近发布了SigLIP 2
人工智能·算法·架构
追逐时光者5 小时前
Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
后端·.net