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

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);
        }
​
    }
    ...省略其它代码
}
相关推荐
FreeBuf_6 小时前
黄金旋律IAB组织利用暴露的ASP.NET机器密钥实施未授权访问
网络·后端·asp.net
张小洛7 小时前
Spring AOP 是如何生效的(入口源码级解析)?
java·后端·spring
DKPT7 小时前
Java设计模式之行为型模式(观察者模式)介绍与说明
java·笔记·学习·观察者模式·设计模式
why技术8 小时前
也是出息了,业务代码里面也用上算法了。
java·后端·算法
络78 小时前
Java4种设计模式详解(单例模式、工厂模式、适配器模式、代理模式)
单例模式·设计模式·代理模式·适配器模式·工厂模式
贱贱的剑8 小时前
5.适配器模式
设计模式·适配器模式
JouJz9 小时前
设计模式之工厂模式:对象创建的智慧之道
java·jvm·设计模式
白仑色9 小时前
完整 Spring Boot + Vue 登录系统
vue.js·spring boot·后端
多啦C梦a11 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构
极光雨雨11 小时前
【设计模式】备忘录模式(标记(Token)模式)
设计模式·备忘录模式