过滤器模式

过滤器模式中文讲解

过滤器模式(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:如 AgeCriteriaGenderCriteria,实现具体过滤逻辑。
  • AndCriteriaOrCriteria:组合标准,调用多个 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 接口定义过滤方法。
  • AgeCriteriaGenderCriteria 是具体标准。
  • AndCriteriaOrCriteria 实现组合逻辑。
  • 客户端通过标准接口筛选列表,组合标准支持复杂条件。

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. 过滤器模式的优缺点

优点

  1. 解耦:过滤逻辑与业务逻辑分离,易于维护。
  2. 可组合:支持“与”、“或”等复杂组合逻辑。
  3. 可重用:标准接口可复用于不同数据集。
  4. 灵活性:新增过滤条件只需实现新标准类。

缺点

  1. 复杂性:大量过滤条件可能导致标准类增多。
  2. 性能开销:多次遍历数据集可能影响性能,需优化。
  3. 适用范围有限:适合列表筛选场景,不适合复杂逻辑。

6. 注意事项

  1. 标准设计
  • 标准接口应简单,专注于单一职责。
  • 组合标准(如 AndCriteria)应避免重复计算。
  1. 中文编码问题
  • 如果涉及中文(如用户名),确保 Servlet 使用 UTF-8:
    java resp.setContentType("text/html;charset=UTF-8");
  • 对中文参数编码(如 URLEncoder.encode("管理员", "UTF-8"))。
  1. 性能优化
  • 使用流式处理(如 Java Stream API)优化过滤性能:
    java return persons.stream().filter(p -> p.getAge() >= minAge).collect(Collectors.toList());
  • 缓存频繁使用的标准实例。
  1. 线程安全
  • 标准类通常无状态,线程安全;但组合标准需注意共享数据。
  • 在 Servlet 中,列表(如 users)需同步(如 Collections.synchronizedList)。
  1. 与其他模式结合
  • 策略模式:过滤器类似策略,动态选择条件。
  • 装饰器模式:增强过滤逻辑。
  • 工厂模式:动态创建标准类。
  1. 常见问题解决
  • 空结果:检查标准逻辑和输入数据。
  • 性能瓶颈:避免重复遍历,优化组合逻辑。
  • 中文乱码:统一编码为 UTF-8。

7. 与其他模式的区别

特性过滤器模式策略模式装饰器模式
目的筛选对象集合动态选择算法增强对象功能
实现方式标准接口和组合策略接口包装对象
关注点数据过滤行为替换功能扩展
组合性支持“与”、“或”单策略选择层层包装

8. 总结

  • 过滤器模式通过标准接口从对象集合中筛选满足条件的子集,适合搜索和数据过滤。
  • 核心是标准接口和组合标准,支持灵活的过滤逻辑。
  • 在 Servlet 中,可用于筛选请求数据或用户列表。
  • 优点是解耦和可组合,缺点是可能增加复杂性和性能开销。
  • 注意中文编码、性能优化和线程安全。

如果需要更复杂的示例(如多条件组合、Stream API 优化)、Servlet 集成细节,或与其他模式(如策略模式)的对比,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注