设计模式——拦截过滤器模式
定义与概念
拦截过滤器模式(Intercepting Filter Pattern)是一种软件设计模式,主要用于在处理请求的过程中,对请求进行一系列预处理和后处理操作。这些操作通过一系列的过滤器来实现,过滤器可以在请求到达目标处理程序之前对请求进行检查、修改或记录等操作,也可以在目标处理程序生成响应之后对响应进行类似的操作。
结构组成
- 过滤器(Filter):
这是模式的核心组件,它定义了对请求和响应进行操作的接口。过滤器可以有多个,每个过滤器都有自己特定的功能,如身份验证过滤器、日志记录过滤器、数据压缩过滤器等。过滤器通常会实现一个doFilter()方法,这个方法接收请求和响应对象,并在其中执行对请求或响应的操作,还会决定是否将请求传递给下一个过滤器或者目标处理程序。例如,身份验证过滤器会在doFilter()方法中检查请求中的用户凭据,只有验证通过的请求才会被继续传递。 - 目标处理程序(Target Handler):
目标处理程序是最终处理请求并生成响应的组件。它接收经过一系列过滤器处理后的请求,执行具体的业务逻辑,如查询数据库、生成 HTML 页面等操作,然后返回响应。例如,在一个 Web 应用中,目标处理程序可能是一个 Servlet,它根据请求中的参数从数据库中获取数据,然后生成包含数据的 HTML 页面作为响应。 - 过滤器链(Filter Chain):
过滤器链用于管理和组织多个过滤器,它决定了过滤器的执行顺序。当一个请求进入系统时,它会按照过滤器链中定义的顺序依次通过各个过滤器,每个过滤器都可以对请求进行操作,然后再传递给下一个过滤器或者目标处理程序。过滤器链可以是一个简单的线性结构,也可以根据不同的请求类型或者其他条件动态地调整过滤器的顺序。 - 客户端(Client):
客户端是发起请求的实体,它可以是一个浏览器、一个移动应用或者其他软件系统。客户端发送请求到过滤器链的第一个过滤器,然后等待接收经过处理后的响应。
工作原理
客户端发起请求,请求首先到达过滤器链中的第一个过滤器。第一个过滤器对请求进行预处理,例如检查请求的合法性、记录请求日志等操作。如果请求满足该过滤器的要求,过滤器会调用过滤器链的doFilter()方法,将请求传递给下一个过滤器(如果有)。这个过程会在过滤器链中依次进行,每个过滤器都可以对请求进行检查、修改或者添加信息等操作。当请求通过所有的过滤器后,它会到达目标处理程序。目标处理程序执行具体的业务逻辑,生成响应。响应会沿着相反的方向通过过滤器链,每个过滤器可以对响应进行后处理,如数据压缩、添加响应头信息等操作。最后,经过处理后的响应返回给客户端。
代码示例(简单的控制台应用模拟)
- 请求和响应结构体
#include <iostream>
#include <string>
#include <vector>
// 表示请求的结构体
struct Request {
std::string url;
std::vector<std::string> parameters;
};
// 表示响应的结构体
struct Response {
int statusCode;
std::string body;
};
- 过滤器接口(Filter)
class Filter {
public:
virtual void doFilter(Request& request, Response& response, Filter* next) = 0;
};
- 具体过滤器 - 日志记录过滤器(LoggingFilter)
class LoggingFilter : public Filter {
public:
void doFilter(Request& request, Response& response, Filter* next) override {
std::cout << "记录请求日志: " << request.url << std::endl;
if (next!= nullptr) {
next->doFilter(request, response, nullptr);
}
}
};
- 具体过滤器 - 身份验证过滤器(AuthenticationFilter)
class AuthenticationFilter : public Filter {
public:
void doFilter(Request& request, Response& response, Filter* next) override {
// 简单模拟身份验证,这里假设URL包含"auth"表示已认证
if (request.url.find("auth")!= std::string::npos) {
if (next!= nullptr) {
next->doFilter(request, response, nullptr);
}
} else {
response.statusCode = 401;
response.body = "未授权访问。";
}
}
};
- 目标处理程序(TargetHandler)
class TargetHandler {
public:
Response handleRequest(Request& request) {
Response response;
response.statusCode = 200;
response.body = "已处理请求: " + request.url;
return response;
}
};
- 过滤器链(FilterChain)
class FilterChain {
private:
std::vector<Filter*> filters;
int currentIndex = 0;
TargetHandler targetHandler;
public:
void addFilter(Filter* filter) {
filters.push_back(filter);
}
void doFilter(Request& request, Response& response) {
if (currentIndex < filters.size()) {
Filter* currentFilter = filters[currentIndex];
currentFilter->doFilter(request, response, this);
currentIndex++;
} else {
response = targetHandler.handleRequest(request);
}
}
};
- 客户端使用示例(模拟)
int main() {
FilterChain chain;
LoggingFilter loggingFilter;
AuthenticationFilter authenticationFilter;
chain.addFilter(&loggingFilter);
chain.addFilter(&authenticationFilter);
Request request;
request.url = "auth/somepage";
Response response;
chain.doFilter(request, response);
std::cout << "响应状态码: " << response.statusCode << std::endl;
std::cout << "响应内容: " << response.body << std::endl;
Request unauthorizedRequest;
unauthorizedRequest.url = "somepage";
Response unauthorizedResponse;
chain.doFilter(unauthorizedRequest, unauthorizedResponse);
std::cout << "未授权响应状态码: " << unauthorizedResponse.statusCode << std::endl;
std::cout << "未授权响应内容: " << unauthorizedResponse.body << std::endl;
return 0;
}
优点
- 分离关注点:
通过将不同的处理功能(如日志记录、身份验证等)封装到不同的过滤器中,使得每个功能模块的职责更加清晰,便于维护和扩展。例如,在一个大型的 Web 应用中,可以方便地添加、删除或者修改某个过滤器,而不会影响到其他功能模块。 - 可复用性高:
过滤器可以在不同的请求处理场景中复用。例如,日志记录过滤器可以用于所有的请求处理流程,只需要将其添加到过滤器链中即可。这提高了代码的复用性,减少了重复代码的编写。 - 灵活性和可配置性:
可以根据不同的应用需求灵活地配置过滤器链,包括过滤器的顺序、是否启用某个过滤器等。例如,在开发环境中,可以启用详细的日志记录过滤器,而在生产环境中可以禁用或者调整日志记录的级别。
缺点
- 增加系统复杂度:
引入多个过滤器和过滤器链会增加系统的层次结构和复杂度。对于简单的应用来说,可能会显得过于复杂,并且在理解和调试请求处理流程时会增加难度。例如,在一个小型的命令行工具中,使用拦截过滤器模式可能会使代码变得臃肿。 - 性能开销:
每个过滤器都需要对请求和响应进行处理,这可能会引入一定的性能开销,尤其是在高并发的应用场景下。如果过滤器的处理逻辑比较复杂或者过滤器数量较多,可能会导致响应时间延长。因此,在设计过滤器时,需要注意优化其性能,避免不必要的操作。