从Servlet来看接口和抽象类在业务中的使用
当接手一整套业务时,梳理清晰业务流程,就可以考虑代码架构设计了。代码架构的设计就是一系列相关的抽象。
以经典的Servlet为例来看接口和抽象类是如何应用的。
业务描述:
浏览器发起 HTTP 请求,这个时候需要服务器解析请求,之后执行“定制”的业务逻辑,最后将业务逻辑执行结果返回。如下图
如此一来,会有一个问题,业务逻辑部分和服务器耦合,把业务和非业务强关联。这里就可以抽象一层,将HTTP请求和解析分离开。
这个有固定套路“计算机科学中的每个问题都可以用一间接层解决”。计算机中的大部分问题都可以加一层抽象来解决,如果不行就再加一层。
所以演变成这样
到这里就成为根据HTTP请求找到对应的业务实现类(逻辑)然后执行,最终返回的本质。
由于业务复杂多变,所以需要规定一套接口,用于描述可以用来做什么,将相对稳定的部分即抽象和相对不稳定的实现分离开。
这一套接口就是Servlet,而Servlet容器就是用于管理Servlet接口以及它的所有实现。
到这里出现了一个新的名词,Servlet容器,并未涉及到我们的业务逻辑,没必要将业务逻辑和Servlet容器关注的事情(管理Servlet接口以及它的所有实现)耦合到一起,所以可以将Servlet容器再分离开来,因为Servlet容器也可能会有多种实现。
把请求和哪个 Servlet 对应关系也抽象出来,就是 web.xml 了,在配置里面告诉 Servlet 容器对应关系即可。
业务实现其实对应的就是我们平常的 war 包,这就是业务和 Servlet 容器的解耦。
相关的中间件按照 Servlet 规范实现,我们也按 Servlet 规范来实现业务代码,这样我们就能在不同场景选择不同的 Web 中间件。
而 Web 容器其实就是 HTTP 服务器 + Servlet 容器,因为单单 Servlet 容器没有解析 HTTP 请求、通信等相关功能。所以把 Tomcat、Jetty 等实现包含了 HTTP 服务器和 Servlet 容器的功能,称之为 Web 容器。所以Tomcat可以理解为对Servlet规范的一种实现。
到这里总结下
1.先是抽象出 HTTP 服务,用来通信和解析协议。
2.再因为业务的复杂,为了不和 HTTP 服务耦合又抽象了一层 Servlet。
3.由 Servlet容器加载和管理Servlet,来控制请求转发到指定的 Servlet 实现类。
4.对于业务开发人员,只需要关注于具体的业务逻辑。
因为抽象所以灵活易扩展,比如现在是 HTTP1.1 服务,可以换成 HTTP 2。
现在用 Tomcat 来作为 Servlet 容器,也可以换成 Jetty。
现在用原生的实现 Servlet 来做业务,也可以换成 SpringMVC。
//这里是Servlet接口,用于定义Servlet能做些什么,init,service,destroy...这是一套生命周期的管理,实现了这些就可以达到管理Servlet的目的
public interface Servlet { void init(ServletConfig config) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest req, ServletResponse res)throws ServletException, IOException; String getServletInfo(); void destroy(); }
//抽象类GenericServlet,省略了一部分代码
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private transient ServletConfig config;
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
public Enumeration getInitParameterNames() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameterNames();
}
}
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
所以类比业务代码开发:
先定义一个接口来约定一些动作,定义能做啥。
然后再定义一个抽象类来实现这个接口,用来实现一些通用的逻辑,做到代码的复用。
然后再定义常用的实现类继承抽象类,方便开发者的使用。
剩下的留给开发者自行扩展。
抽象类一般会使用模板方法,也就是定义执行的流程,具体实现逻辑由子类自行实现。