第十二章:实现 Facade(外观模式)-MiniTomcat
第十二章:实现 Facade(外观模式)
功能目标
本章的核心目标是使用 Facade(外观)模式 简化外部对 Servlet API 的访问。通过该模式,我们可以隐藏内部复杂的实现细节,提供更简洁、统一的接口供外部调用,从而降低系统的耦合度。
实现内容
- Facade 模式概述:Facade 是一种结构型设计模式,用于封装复杂的子系统。它提供一个统一的接口,使得外部系统无需了解内部实现细节即可轻松访问子系统功能。
- 在 Servlet 容器中的应用:在 Web 容器内部,我们可以通过 Facade 包装
HttpServletRequest和HttpServletResponse等核心对象。这样做不仅限制了对内部结构的直接访问,还简化了对外接口的复杂度。 - 具体实现类:本章将实现
RequestFacade和ResponseFacade类。它们分别封装了请求和响应对象,隐藏了底层复杂细节,对外提供标准的 Servlet 接口。
示例功能
我们将创建一个 RequestFacade 类,封装实际的请求对象,屏蔽不必要的内部细节,从而简化外部对请求信息的访问流程。
12.1 Facade 模式设计
Facade 模式 的关键在于将复杂的子系统操作封装在一个简单的接口背后。对于 Web 容器而言,我们需要简化客户端访问 HttpServletRequest 和 HttpServletResponse 的过程,同时隐藏底层实现的具体细节。
在本项目中,我们将向用户提供 RequestFacade 和 ResponseFacade 对象,代替直接暴露原始的 HttpServletRequest 和 HttpServletResponse 实现类。
12.2 实现 RequestFacade 类
RequestFacade 类封装了对 HttpServletRequest 的操作,隐藏了请求底层的复杂实现细节。客户端仅通过 RequestFacade 来访问请求的相关信息,无法直接操作底层对象。
package com.daicy.minitomcat;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class RequestFacade extends HttpServletRequestWrapper {
/**
* Constructs a request object wrapping the given request.
*
* @param request
* @throws IllegalArgumentException if the request is null
*/
public RequestFacade(HttpServletRequest request) {
super(request);
}
}通过继承 HttpServletRequestWrapper,RequestFacade 可以方便地代理以下常用方法:
getRequestURI():获取请求的 URI。getHeaderNames():获取请求头的名称枚举。getHeader(String name):根据头名称获取请求头的值。getParameter(String name):获取请求参数。getMethod():获取请求方法(如 GET、POST 等)。
通过封装 HttpServletRequest,外部系统不需要直接与复杂的请求对象交互,只需使用 RequestFacade 提供的简洁接口即可。
12.3 实现 ResponseFacade 类
ResponseFacade 类封装了对 HttpServletResponse 的操作,隐藏了响应的复杂实现细节。客户端通过 ResponseFacade 来操作响应数据,确保响应行为符合容器规范。
package com.daicy.minitomcat;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class ResponseFacade extends HttpServletResponseWrapper {
/**
* Constructs a response adaptor wrapping the given response.
*
* @param response
* @throws IllegalArgumentException if the response is null
*/
public ResponseFacade(HttpServletResponse response) {
super(response);
}
}ResponseFacade 主要代理以下关键方法:
setStatus(int statusCode):设置响应的状态码。setHeader(String name, String value):设置响应头。write(String content):将内容写入响应体(通过 Writer 或 OutputStream)。sendRedirect(String location):发送重定向响应。
通过 ResponseFacade,外部系统可以更安全、简单地管理 HTTP 响应。
12.4 使用 Facade 模式简化代码
在 Web 服务器的核心实现中,RequestFacade 和 ResponseFacade 简化了请求和响应的处理流程,让外部调用(如 Filter 链或 Servlet 调用)更加直观且安全。
以下是在处理请求时实例化并使用 Facade 对象的示例代码:
// 创建 Facade 对象包装原始请求和响应
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
// 应用 Header 处理
headerHandler.applyHeaders(requestFacade, responseFacade, requestFacade.getSession().getId());
// 获取过滤器链
List<Filter> filters = HttpServer.filterManager.getFilters();
FilterChain filterChain = new FilterChain() {
int index = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (index == filters.size()) {
try {
// 执行最终的 Servlet 逻辑
wrapper.invoke((HttpServletRequest) request, (HttpServletResponse) response);
} catch (Exception e) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 捕获异常并设置错误状态码
httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpServletResponse.getWriter().write("Internal Server Error: " + e.getMessage());
}
} else {
Filter filter = filters.get(index);
index++;
// 递归调用下一个过滤器
filter.doFilter(request, response, this);
}
}
};
// 启动过滤器链,传入 Facade 对象
filterChain.doFilter(requestFacade, responseFacade);- 在
ServletProcessor类中,通过RequestFacade和ResponseFacade处理请求和响应,使得外部调用变得简单,同时防止用户代码直接操作容器内部对象。
12.5 学习收获
通过实现 Facade 模式,我们获得了以下核心经验:
- Facade 模式的设计思想:Facade 模式通过封装复杂的子系统,提供了一个简化的接口,使得外部系统不需要了解内部的实现细节,符合“迪米特法则”(最少知道原则)。
- 简化接口设计:通过
RequestFacade和ResponseFacade,我们能够把复杂的请求和响应处理逻辑隐藏在内部,只暴露简单易用的接口给外部调用者。 - 提高安全性和易用性:Facade 模式通过限制对复杂系统的直接访问,提供了更高的安全性(防止内部状态被意外修改),同时降低了外部系统的使用难度。
通过 Facade 模式,我们使得 Web 服务器的接口变得更加简洁和安全,同时也更易于扩展和维护。
项目源代码地址:
https://github.com/daichangya/MiniTomcat/tree/chapter12/mini-tomcat
说明:本文代码基于javax.servlet包(Servlet API 4.0 及以前版本)。在 Jakarta EE 9 及更高版本中,包名已变更为jakarta.servlet,实际开发中请根据所使用的容器版本调整依赖。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/di-shi-er-zhang--shi-xian-facade-wai-guan-mo-shi--minitomcat.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。