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

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);
        }
​
    }
    ...省略其它代码
}
相关推荐
JustHappy5 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
Hommy885 小时前
【剪映小助手】添加图片接口(Add Images)
后端·github·剪映小助手·视频剪辑自动化
GetcharZp6 小时前
别再盲目用 OpenCV 读图了,这才是 CV 预处理的终极杀手锏!
后端
一水鉴天8 小时前
不确定性问题确定解的 DevOps 九宫格内核 20260612(腾讯元宝)
人工智能·架构
小短腿的代码世界8 小时前
Qt行情协议解析与二进制编解码优化:从FIX到自定义协议的全链路架构
开发语言·qt·架构
是温不嗜温9 小时前
QR 准谐振反激架构:当下中小功率快充的主流选择
架构·电源管理·电源芯片·ac-dc
AI焦点9 小时前
2026年AI应用架构:如何避坑并选对API聚合中转服务?
大数据·人工智能·架构
TOPGO智能9 小时前
AI PC 端侧 AI 实战:知易智能知识管家的全栈架构与踩坑实录
人工智能·架构·高通开发
IT_陈寒9 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端
椰椰椰耶10 小时前
[SpringCloud][14]OpenFeign参数传递方法
后端·spring·spring cloud