每天认识一个设计模式 - 过滤器模式:构建灵活筛选逻辑的核心法则

一、前言

在软件开发的广袤天地中,设计模式宛如闪耀的星辰,作为久经锤炼的智慧结晶,极大地增强了代码的可扩展性与可维护性。它们为开发者提供了一套可复用的精妙蓝图,让代码结构更加明晰,可读性显著提升。今天咱们就来聊聊其中之一的过滤器模式~

过滤器模式顾名思义,它的作用肯定是过滤了。与其他模式的模块解耦方式不同,它更多聚焦于数据的筛选处理逻辑,赋予系统灵动筛选的能力,在应对复杂多变的数据处理需求时,这一特性显得尤为重要。本文将深度剖析过滤器模式的底层原理,探索其适用场景,并通过实际代码示例,助力大伙透彻理解并熟练运用这一强大模式。

二、过滤器模式原型设计及说明

过滤器模式的本质在于解耦筛选逻辑与业务代码。通过将筛选逻辑封装在过滤器中,业务代码只需关注核心业务,无需过多关心复杂的筛选细节,从而显著提升代码的扩展性。当筛选需求发生变化时,只需修改或添加相应的过滤器,而不会对业务代码造成较大影响。

除此以外,该模式具备动态组合条件的能力,能够根据不同的业务场景,灵活地组合各种过滤条件,满足多样化的筛选需求。同时,它符合开闭原则,即对扩展开放,对修改关闭。在增加新的过滤逻辑时,只需创建新的具体过滤器类并实现抽象过滤器接口,而无需修改已有的过滤器类和业务代码。

因此当需要根据多个不同的条件或标准来筛选一组对象时,过滤器模式提供了一种灵活的方式来定义这些条件,避免在客户端代码中硬编码筛选逻辑。

具体设计结构咱们可以通过UML图来研究:

可以看到,过滤器模式包含以下几个主要角色:

过滤器接口(Filter/Criteria):定义一个接口,用于筛选对象。该接口通常包含一个方法,用于根据特定条件过滤对象。

这种抽象的设计,使得所有具体的过滤器类都遵循相同的规范。好处在于,当需要添加新的过滤逻辑时,只需创建新的类实现该接口即可,极大地提升了代码的扩展性。例如,后续若有新的筛选维度,像基于对象某种特定属性的筛选,开发人员可以轻松创建新类实现Filter接口,而无需修改原有过滤器类和业务代码。

具体过滤器类(Concrete Filter/Concrete Criteria):实现过滤器接口,各自专注于单一维度的过滤逻辑。

以SpecificFilter1为例,它内部维护着specificCondition1,并在filter方法中依据该条件筛选目标对象。遵循单一职责原则,每个具体过滤器只负责一种过滤逻辑,这使得代码结构清晰,维护和调试变得容易。当某个具体过滤器的逻辑需要修改时,仅需调整该类,不会对其他过滤器造成影响,保障了系统的稳定性。

客户端(Client):使用具体过滤器类来筛选对象集合。客户端将对象集合和过滤器结合起来,以获得符合条件的对象。

通过addFilter方法,客户端能够动态添加不同的具体过滤器,从而构建出符合业务需求的过滤器链。applyFilters方法则负责按顺序执行过滤器链中的各个过滤器,对目标对象集合进行逐步筛选。这种设计赋予了客户端灵活组合过滤逻辑的能力,能根据不同的业务场景,快速调整筛选规则,满足多样化的筛选需求。

对象集合(Items/Objects to be filtered):要被过滤的对象集合。这些对象通常是具有共同属性的实例,例如一组人、一组产品等。

所以其实他算是比较好理解的一个设计模式了,熟悉Servlet的应该都知道Filter,也算是过滤器模式在实际框架上了具体应用了。

所以在 Spring Boot 项目中,我们可以优先考虑使用 Spring 原生的组件来实现过滤器模式。例如,Spring 的Filter接口和FilterChain机制,它们提供了强大的功能和良好的扩展性,能够与 Spring 框架无缝集成,方便开发和维护。

除此以外,我们也可以根据自己的业务场景去灵活选择这一模式的应用。

三、过滤器模式的自定义应用

假设我们正在开发一个在线教育平台,平台拥有海量的课程资源,需要根据多种复杂条件筛选出符合用户需求的课程,以精准推荐给用户。

例如,要筛选出价格在特定区间、授课时长满足一定要求、适合特定学习阶段(如初级、中级、高级)且好评率高于某个阈值的课程。这涉及多个维度的动态筛选,契合过滤器模式的应用场景。

Step1:定义过滤对象

这时候我们就可以根据需求采用过滤器模式的构建方式来设计代码:首先定义要过滤的对象,这里为课程信息:

@Data
public class Course {
    private String courseName;
    private double price;
    private int duration;
    private String learningStage;
    private double rating;

    public Course(String courseName, double price, int duration, String learningStage, double rating) {
        this.courseName = courseName;
        this.price = price;
        this.duration = duration;
        this.learningStage = learningStage;
        this.rating = rating;
    }

}

这个类作为筛选的目标对象,封装了课程的各项属性,这些属性将作为后续过滤的依据。确保属性的完整性和准确性,以便满足复杂业务筛选条件的需求。

Step2:定义抽象过滤器接口

import java.util.List;

public interface CourseFilter {
    List<Course> filter(List<Course> courses);
}

为所有具体过滤器定义了统一的行为规范。通过抽象出filter方法,使得不同的过滤逻辑可以通过实现该接口来达成,这是过滤器模式可扩展性的基础。后续具体过滤器类必须严格实现该接口方法,保证过滤器链的正确运行。

Step3:实现具体过滤器

这里我们分别创建四个过滤器,用来筛选课程价格、时长、特定学习阶段与好评信息: 

import java.util.ArrayList;
import java.util.List;
//筛选价格在特定区间的课程
public class PriceFilter implements CourseFilter {
    private double minPrice;
    private double maxPrice;

    public PriceFilter(double minPrice, double maxPrice) {
        this.minPrice = minPrice;
        this.maxPrice = maxPrice;
    }

    @Override
    public List<Course> filter(List<Course> courses) {
        List<Course> filteredCourses = new ArrayList<>();
        for (Course course : courses) {
            if (course.getPrice() >= minPrice && course.getPrice() <= maxPrice) {
                filteredCourses.add(course);
            }
        }
        return filteredCourses;
    }
}
import java.util.ArrayList;
import java.util.List;
//筛选授课时长满足一定要求的课程
public class DurationFilter implements CourseFilter {
    private int minDuration;

    public DurationFilter(int minDuration) {
        this.minDuration = minDuration;
    }

    @Override
    public List<Course> filter(List<Course> courses) {
        List<Course> filteredCourses = new ArrayList<>();
        for (Course course : courses) {
            if (course.getDuration() >= minDuration) {
                filteredCourses.add(course);
            }
        }
        return filteredCourses;
    }
}
import java.util.ArrayList;
import java.util.List;
//筛选适合特定学习阶段的课程
public class LearningStageFilter implements CourseFilter {
    private String targetStage;

    public LearningStageFilter(String targetStage) {
        this.targetStage = targetStage;
    }

    @Override
    public List<Course> filter(List<Course> courses) {
        List<Course> filteredCourses = new ArrayList<>();
        for (Course course : courses) {
            if (targetStage.equals(course.getLearningStage())) {
                filteredCourses.add(course);
            }
        }
        return filteredCourses;
    }
}
import java.util.ArrayList;
import java.util.List;
//筛选好评率高于某个阈值的课程
public class RatingFilter implements CourseFilter {
    private double minRating;

    public RatingFilter(double minRating) {
        this.minRating = minRating;
    }

    @Override
    public List<Course> filter(List<Course> courses) {
        List<Course> filteredCourses = new ArrayList<>();
        for (Course course : courses) {
            if (course.getRating() >= minRating) {
                filteredCourses.add(course);
            }
        }
        return filteredCourses;
    }
}

每个具体过滤器类都专注于单一维度的过滤逻辑,遵循单一职责原则。这使得代码结构清晰,当某个过滤条件发生变化时,只需修改对应的具体过滤器类,而不影响其他过滤器。例如,如果价格区间的筛选逻辑改变,仅需调整PriceFilter类,其他过滤器不受影响。 

Step4:组合过滤器链

这里我们主要是创建一个工具类来管理过滤器链。 

import java.util.ArrayList;
import java.util.List;

public class CourseFilterChain {
    private List<CourseFilter> filters = new ArrayList<>();

    public void addFilter(CourseFilter filter) {
        filters.add(filter);
    }

    public List<Course> applyFilters(List<Course> courses) {
        List<Course> result = courses;
        for (CourseFilter filter : filters) {
            result = filter.filter(result);
        }
        return result;
    }
}

CourseFilterChain类负责组合多个具体过滤器形成过滤器链。通过addFilter方法可以动态添加过滤器,实现灵活的筛选逻辑组合。在applyFilters方法中,按照添加顺序依次对课程列表进行过滤,保证了过滤顺序的可控性。例如,先进行价格筛选,再进行学习阶段筛选,符合业务逻辑顺序。

Step5:测试 

我们首先构建了一个包含多个课程信息的列表。然后创建CourseFilterChain对象,并依次添加价格、时长、学习阶段和好评率过滤器,形成过滤器链。最后调用applyFilters方法对所有课程进行筛选,得到符合多个复杂条件的推荐课程列表,并输出课程信息进行测试。

import java.util.Arrays;
import java.util.List;

public class CourseRecommendationSystem {
    public static void main(String[] args) {
        List<Course> allCourses = Arrays.asList(
            new Course("Java Basics", 99.0, 30, "Beginner", 4.5),
            new Course("Advanced Java", 199.0, 60, "Intermediate", 4.2),
            new Course("Python for Data Science", 149.0, 50, "Intermediate", 4.8),
            new Course("Beginner Math", 49.0, 20, "Beginner", 4.0)
        );

        CourseFilterChain filterChain = new CourseFilterChain();
        filterChain.addFilter(new PriceFilter(100, 200));
        filterChain.addFilter(new DurationFilter(40));
        filterChain.addFilter(new LearningStageFilter("Intermediate"));
        filterChain.addFilter(new RatingFilter(4.5));

        List<Course> recommendedCourses = filterChain.applyFilters(allCourses);
        for (Course course : recommendedCourses) {
            System.out.println("Course Name: " + course.getCourseName() + ", Price: " + course.getPrice() +
                ", Duration: " + course.getDuration() + ", Learning Stage: " + course.getLearningStage() +
                ", Rating: " + course.getRating());
        }
    }
}

通过这种方式,我们就可以用过滤器模式在复杂业务场景下进行动态组合筛选条件,使得课程推荐系统能够根据不同用户需求灵活调整筛选逻辑,从而提供精准的课程推荐服务。

四、过滤器模式的适用场景及说明 

从上面我可以看到,过滤对于条件筛选的场景特别方便,特别是当业务场景中需要根据多个动态变化的条件对数据进行筛选时,过滤器模式能够很好地应对这种需求,通过动态组合不同的过滤条件,实现精准的数据筛选。

除此以外,像我们管理平台在进行权限校验与请求预处理设计时也是过滤器模式的用武之地,过滤器模式可以将权限校验逻辑封装在具体过滤器中,对每个请求进行预处理,确保系统的安全性和稳定性。这些在日常MVC开发中应该都是比较常见的。

另外在ETL(Extract-Transform-Load)工作中,我们也可以进行使用。在数据处理过程中,我们可能经常需要对原始数据进行清洗和转换,以满足后续分析或存储的要求。过滤器模式可以用于实现各种数据清洗和转换规则,例如去除重复数据、转换数据格式等。通过将这些规则封装在具体过滤器中,可以方便地对数据进行处理和优化。

但是我们在进行使用时仍然需注意使用过滤器模式时,随着筛选条件的增加,系统可能变得复杂。特别是当筛选器组合过于复杂时,可能会影响系统性能。

因此一般情况下有如下几个地方需要我们注意

避免过度设计:过滤器模式虽然强大,但在应用时应避免过度设计。仅在面临多维度且易变的筛选需求时,才考虑使用该模式。对于简单的筛选逻辑,过度使用过滤器模式可能会增加系统的复杂性,反而不利于开发和维护。​

遵循单一职责原则:每个具体过滤器应专注于实现单一的过滤逻辑。这样做不仅符合单一职责原则,使得代码更加清晰易懂,而且便于维护和扩展。当某个过滤逻辑发生变化时,只需修改对应的具体过滤器类,而不会影响到其他过滤器。​

优化高频场景性能:在高频使用过滤器模式的场景中,性能优化至关重要。例如,可以考虑缓存中间结果,避免重复计算。对于一些复杂的过滤条件,可以在首次计算后将结果缓存起来,后续再次使用相同条件时,直接从缓存中获取结果,从而提高系统的响应速度。

五、总结 

综上所述,过滤器模式的过滤功能特别适用于多维度、可变筛选场景。当系统需要根据多个不同维度的条件对数据进行筛选,并且这些筛选条件可能会随着业务发展而不断变化时,过滤器模式能够发挥其优势,提供高效、灵活的解决方案。

当然了,过滤器模式不仅仅局限于代码层面的应用,它还可以为架构设计提供了有价值的启示。比如,在微服务网关中,过滤层可以借鉴过滤器模式的思想,对进入网关的请求进行统一的预处理和筛选。通过将不同的过滤逻辑封装成过滤器,并动态组合这些过滤器,可以实现对请求的灵活管控,提高系统的安全性和稳定性。这种从代码到架构的延伸,大家在平常的开发工作中可以浅尝一下啦~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深情不及里子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值