过滤器模式
过滤器模式中文讲解
过滤器模式(Filter Pattern),也称为标准模式(Criteria Pattern),是一种行为型设计模式,虽然不是 GoF(Gang of Four)提出的23种经典设计模式之一,但它在软件开发中非常实用。过滤器模式用于从一组对象中筛选出满足特定条件的子集,通过定义多个过滤标准(Criteria),并支持组合这些标准来实现灵活的过滤逻辑。
以下用中文详细讲解过滤器模式的定义、结构、代码示例、应用场景、优缺点以及在 Servlet 环境中的使用,重点突出其原理和实际应用。
1. 什么是过滤器模式?
过滤器模式通过定义一组过滤标准,从数据集合中筛选出符合条件的对象。它将过滤逻辑封装为独立的过滤器类,客户端可以通过组合多个过滤器实现复杂的筛选需求。
- 问题:当需要从大量对象中筛选出满足特定条件的子集(如按年龄、性别过滤用户),直接在代码中硬编码条件会导致逻辑复杂且难以维护。
- 解决方案:定义标准接口(Criteria),每个具体标准实现一个过滤器,客户端通过调用过滤器或组合过滤器筛选数据。
关键特点:
- 解耦:将过滤逻辑从业务逻辑中分离。
- 可组合:支持多个过滤器组合(如“与”、“或”操作)。
- 可重用:过滤器可以复用于不同数据集。
2. 过滤器模式的结构
过滤器模式包含以下角色:
角色 | 描述 |
---|---|
对象(Object) | 需要过滤的数据对象,通常是一个简单的 POJO(如 Person )。 |
标准接口(Criteria) | 定义过滤方法的接口,通常包含一个 meetCriteria() 方法。 |
具体标准(Concrete Criteria) | 实现标准接口,定义具体的过滤逻辑(如按年龄、性别)。 |
组合标准(Composite Criteria) | 组合多个标准(如“与”、“或”),实现复杂过滤。 |
客户端(Client) | 使用标准接口筛选对象集合。 |
UML 类图(文字描述):
- Criteria:接口,声明
meetCriteria(List<T>)
方法,返回过滤后的列表。 - ConcreteCriteria:如
AgeCriteria
、GenderCriteria
,实现具体过滤逻辑。 - AndCriteria、OrCriteria:组合标准,调用多个 Criteria。
- 客户端通过 Criteria 接口筛选对象列表。
3. 代码示例
以下是一个 Java 示例,模拟从 Person
列表中按年龄和性别进行过滤。
import java.util.ArrayList;
import java.util.List;
// 对象:Person
class Person {
private String name;
private int age;
private String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getGender() { return gender; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", gender='" + gender + "'}";
}
}
// 标准接口
interface Criteria {
List<Person> meetCriteria(List<Person> persons);
}
// 具体标准:按年龄过滤
class AgeCriteria implements Criteria {
private int minAge;
public AgeCriteria(int minAge) {
this.minAge = minAge;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> result = new ArrayList<>();
for (Person person : persons) {
if (person.getAge() >= minAge) {
result.add(person);
}
}
return result;
}
}
// 具体标准:按性别过滤
class GenderCriteria implements Criteria {
private String gender;
public GenderCriteria(String gender) {
this.gender = gender;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> result = new ArrayList<>();
for (Person person : persons) {
if (person.getGender().equalsIgnoreCase(gender)) {
result.add(person);
}
}
return result;
}
}
// 组合标准:与操作
class AndCriteria implements Criteria {
private Criteria criteria1;
private Criteria criteria2;
public AndCriteria(Criteria criteria1, Criteria criteria2) {
this.criteria1 = criteria1;
this.criteria2 = criteria2;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> firstFilter = criteria1.meetCriteria(persons);
return criteria2.meetCriteria(firstFilter);
}
}
// 组合标准:或操作
class OrCriteria implements Criteria {
private Criteria criteria1;
private Criteria criteria2;
public OrCriteria(Criteria criteria1, Criteria criteria2) {
this.criteria1 = criteria1;
this.criteria2 = criteria2;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> firstFilter = criteria1.meetCriteria(persons);
List<Person> secondFilter = criteria2.meetCriteria(persons);
List<Person> result = new ArrayList<>(firstFilter);
for (Person person : secondFilter) {
if (!result.contains(person)) {
result.add(person);
}
}
return result;
}
}
// 测试
public class FilterTest {
public static void main(String[] args) {
// 初始化数据
List<Person> persons = new ArrayList<>();
persons.add(new Person("张三", 25, "Male"));
persons.add(new Person("李四", 30, "Female"));
persons.add(new Person("王五", 20, "Male"));
persons.add(new Person("赵六", 35, "Female"));
// 按年龄过滤
Criteria ageCriteria = new AgeCriteria(30);
System.out.println("年龄 >= 30:");
for (Person person : ageCriteria.meetCriteria(persons)) {
System.out.println(person);
}
// 按性别过滤
Criteria genderCriteria = new GenderCriteria("Male");
System.out.println("\n男性:");
for (Person person : genderCriteria.meetCriteria(persons)) {
System.out.println(person);
}
// 组合过滤:年龄 >= 30 且男性
Criteria andCriteria = new AndCriteria(ageCriteria, genderCriteria);
System.out.println("\n年龄 >= 30 且男性:");
for (Person person : andCriteria.meetCriteria(persons)) {
System.out.println(person);
}
// 组合过滤:年龄 >= 30 或男性
Criteria orCriteria = new OrCriteria(ageCriteria, genderCriteria);
System.out.println("\n年龄 >= 30 或男性:");
for (Person person : orCriteria.meetCriteria(persons)) {
System.out.println(person);
}
}
}
输出:
年龄 >= 30:
Person{name='李四', age=30, gender='Female'}
Person{name='赵六', age=35, gender='Female'}
男性:
Person{name='张三', age=25, gender='Male'}
Person{name='王五', age=20, gender='Male'}
年龄 >= 30 且男性:
(无结果)
年龄 >= 30 或男性:
Person{name='张三', age=25, gender='Male'}
Person{name='李四', age=30, gender='Female'}
Person{name='王五', age=20, gender='Male'}
Person{name='赵六', age=35, gender='Female'}
说明:
Person
是需要过滤的对象。Criteria
接口定义过滤方法。AgeCriteria
和GenderCriteria
是具体标准。AndCriteria
和OrCriteria
实现组合逻辑。- 客户端通过标准接口筛选列表,组合标准支持复杂条件。
4. 过滤器模式的应用场景
- 数据筛选:如从用户列表中筛选特定条件的用户(年龄、性别、角色)。
- 搜索功能:Web 应用的搜索过滤,如电商网站按价格、品牌筛选商品。
- 日志过滤:筛选特定级别的日志记录。
- Servlet 相关:在 Servlet 中过滤请求数据或响应内容。
Servlet 示例:过滤用户请求数据。
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 用户类
class User {
private String username;
private String role;
public User(String username, String role) {
this.username = username;
this.role = role;
}
public String getUsername() { return username; }
public String getRole() { return role; }
@Override
public String toString() {
return "User{username='" + username + "', role='" + role + "'}";
}
}
// 标准接口
interface UserCriteria {
List<User> meetCriteria(List<User> users);
}
// 按角色过滤
class RoleCriteria implements UserCriteria {
private String role;
public RoleCriteria(String role) {
this.role = role;
}
@Override
public List<User> meetCriteria(List<User> users) {
List<User> result = new ArrayList<>();
for (User user : users) {
if (user.getRole().equalsIgnoreCase(role)) {
result.add(user);
}
}
return result;
}
}
// Servlet
public class UserFilterServlet extends HttpServlet {
private static final List<User> users = new ArrayList<>();
static {
users.add(new User("张三", "Admin"));
users.add(new User("李四", "User"));
users.add(new User("王五", "Admin"));
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
String role = req.getParameter("role");
UserCriteria criteria = new RoleCriteria(role != null ? role : "User");
List<User> filteredUsers = criteria.meetCriteria(users);
StringBuilder html = new StringBuilder("<h1>筛选用户</h1><ul>");
for (User user : filteredUsers) {
html.append("<li>").append(user.toString()).append("</li>");
}
html.append("</ul>");
resp.getWriter().write(html.toString());
}
}
说明:
User
是需要过滤的对象。UserCriteria
是标准接口,RoleCriteria
按角色过滤。- Servlet 根据请求参数(如
/filter?role=Admin
)筛选用户并返回 HTML。
5. 过滤器模式的优缺点
优点:
- 解耦:过滤逻辑与业务逻辑分离,易于维护。
- 可组合:支持“与”、“或”等复杂组合逻辑。
- 可重用:标准接口可复用于不同数据集。
- 灵活性:新增过滤条件只需实现新标准类。
缺点:
- 复杂性:大量过滤条件可能导致标准类增多。
- 性能开销:多次遍历数据集可能影响性能,需优化。
- 适用范围有限:适合列表筛选场景,不适合复杂逻辑。
6. 注意事项
- 标准设计:
- 标准接口应简单,专注于单一职责。
- 组合标准(如
AndCriteria
)应避免重复计算。
- 中文编码问题:
- 如果涉及中文(如用户名),确保 Servlet 使用 UTF-8:
java resp.setContentType("text/html;charset=UTF-8");
- 对中文参数编码(如
URLEncoder.encode("管理员", "UTF-8")
)。
- 性能优化:
- 使用流式处理(如 Java Stream API)优化过滤性能:
java return persons.stream().filter(p -> p.getAge() >= minAge).collect(Collectors.toList());
- 缓存频繁使用的标准实例。
- 线程安全:
- 标准类通常无状态,线程安全;但组合标准需注意共享数据。
- 在 Servlet 中,列表(如
users
)需同步(如Collections.synchronizedList
)。
- 与其他模式结合:
- 策略模式:过滤器类似策略,动态选择条件。
- 装饰器模式:增强过滤逻辑。
- 工厂模式:动态创建标准类。
- 常见问题解决:
- 空结果:检查标准逻辑和输入数据。
- 性能瓶颈:避免重复遍历,优化组合逻辑。
- 中文乱码:统一编码为 UTF-8。
7. 与其他模式的区别
特性 | 过滤器模式 | 策略模式 | 装饰器模式 |
---|---|---|---|
目的 | 筛选对象集合 | 动态选择算法 | 增强对象功能 |
实现方式 | 标准接口和组合 | 策略接口 | 包装对象 |
关注点 | 数据过滤 | 行为替换 | 功能扩展 |
组合性 | 支持“与”、“或” | 单策略选择 | 层层包装 |
8. 总结
- 过滤器模式通过标准接口从对象集合中筛选满足条件的子集,适合搜索和数据过滤。
- 核心是标准接口和组合标准,支持灵活的过滤逻辑。
- 在 Servlet 中,可用于筛选请求数据或用户列表。
- 优点是解耦和可组合,缺点是可能增加复杂性和性能开销。
- 注意中文编码、性能优化和线程安全。
如果需要更复杂的示例(如多条件组合、Stream API 优化)、Servlet 集成细节,或与其他模式(如策略模式)的对比,请告诉我!