You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多筛选条件搜索场景下的设计模式选型及搜索函数重载优化问询

多筛选条件搜索场景下的设计模式选型及搜索函数重载优化问询

嘿,这个问题问得特别戳痛点——我之前做健身类垂直项目的时候,刚好碰到过几乎一模一样的场景,当时也纠结过要不要硬写一堆重载函数,后来用了两个设计模式的组合完美解决了,分享给你:

先说说为啥别写一堆重载函数

  • 首先,函数爆炸问题:如果有n个筛选条件,组合数是2ⁿ-1,比如你说的4个条件(距离、邮编、名称、特色),就有15种组合,这还没算后续加新条件的情况(比如要不要24小时营业、有没有儿童区),到时候类里的函数会多到你自己都找不到,维护起来纯纯的噩梦。
  • 其次,违反开闭原则:每次加新的筛选条件,你都得回去改原有的类,加新的重载函数,很容易不小心改坏旧的逻辑。

推荐的两种解决方案

1. Builder模式(适合简单的多可选条件)

核心思路是把所有筛选条件打包成一个“查询参数对象”,用Builder来构建这个对象,搜索函数只需要接受这个对象作为参数。

举个Java风格的示例代码(其他语言比如Python、JS同理,只是语法不同):

// 定义查询条件的Builder类
public class GymSearchCriteria {
    private Integer maxDistance; // 最大距离
    private String pincode;
    private String nameKeyword;
    private Set<String> requiredFeatures;

    // 私有构造,只能通过Builder创建
    private GymSearchCriteria(Builder builder) {
        this.maxDistance = builder.maxDistance;
        this.pincode = builder.pincode;
        this.nameKeyword = builder.nameKeyword;
        this.requiredFeatures = builder.requiredFeatures;
    }

    // 对外暴露的Builder
    public static class Builder {
        private Integer maxDistance;
        private String pincode;
        private String nameKeyword;
        private Set<String> requiredFeatures;

        // 每个条件的设置方法,返回this支持链式调用
        public Builder maxDistance(Integer distance) {
            this.maxDistance = distance;
            return this;
        }

        public Builder pincode(String pincode) {
            this.pincode = pincode;
            return this;
        }

        public Builder nameKeyword(String keyword) {
            this.nameKeyword = keyword;
            return this;
        }

        public Builder requiredFeatures(Set<String> features) {
            this.requiredFeatures = features;
            return this;
        }

        // 构建最终的查询条件对象
        public GymSearchCriteria build() {
            return new GymSearchCriteria(this);
        }
    }
}

然后你的搜索函数只需要一个:

public List<Gym> searchGyms(GymSearchCriteria criteria) {
    // 内部逻辑:判断哪些条件不为空,动态拼接查询(比如数据库的WHERE子句,或者内存过滤)
    // 比如用MyBatis的话,就用<if test="maxDistance != null">来拼接条件
    // 内存过滤的话,就逐个判断criteria的属性
}

调用的时候特别优雅,想加什么条件就链什么:

// 只按距离搜
searchGyms(new GymSearchCriteria.Builder().maxDistance(5).build());
// 按距离+名称搜
searchGyms(new GymSearchCriteria.Builder().maxDistance(5).nameKeyword("阳光").build());
// 按邮编+特色搜
searchGyms(new GymSearchCriteria.Builder().pincode("100001").requiredFeatures(Set.of("泳池", "瑜伽室")).build());

2. Specification模式(适合复杂的组合筛选)

如果你的筛选逻辑不止是“同时满足多个条件”,还需要“满足A或者满足B”“不满足C”这种复杂逻辑,那Specification模式就太合适了。

核心是把每个筛选条件封装成一个独立的Specification类,然后可以自由组合这些条件(与、或、非)。

先定义一个Specification接口:

public interface GymSpecification {
    // 判断某个健身房是否符合该条件
    boolean isSatisfiedBy(Gym gym);

    // 组合方法:与
    default GymSpecification and(GymSpecification other) {
        return gym -> this.isSatisfiedBy(gym) && other.isSatisfiedBy(gym);
    }

    // 组合方法:或
    default GymSpecification or(GymSpecification other) {
        return gym -> this.isSatisfiedBy(gym) || other.isSatisfiedBy(gym);
    }

    // 组合方法:非
    default GymSpecification not() {
        return gym -> !this.isSatisfiedBy(gym);
    }
}

然后实现每个具体的条件:

// 距离条件
public class MaxDistanceSpecification implements GymSpecification {
    private Integer maxDistance;

    public MaxDistanceSpecification(Integer maxDistance) {
        this.maxDistance = maxDistance;
    }

    @Override
    public boolean isSatisfiedBy(Gym gym) {
        return gym.getDistanceFromUser() <= maxDistance;
    }
}

// 名称条件
public class NameContainsSpecification implements GymSpecification {
    private String keyword;

    public NameContainsSpecification(String keyword) {
        this.keyword = keyword.toLowerCase();
    }

    @Override
    public boolean isSatisfiedBy(Gym gym) {
        return gym.getName().toLowerCase().contains(keyword);
    }
}

搜索函数只需要接受一个Specification:

public List<Gym> searchGyms(GymSpecification spec) {
    List<Gym> allGyms = getAllGymsFromDB(); // 从数据库拿所有健身房
    return allGyms.stream()
            .filter(spec::isSatisfiedBy)
            .collect(Collectors.toList());
}

调用的时候可以自由组合复杂逻辑:

// 距离不超过5公里,或者名称包含“阳光”,并且不在邮编100002的区域
GymSpecification spec = new MaxDistanceSpecification(5)
        .or(new NameContainsSpecification("阳光"))
        .and(new GymSpecification() {
            @Override
            public boolean isSatisfiedBy(Gym gym) {
                return !gym.getPincode().equals("100002");
            }
        });

searchGyms(spec);

最后给你个小总结

  • 如果只是简单的“可选条件组合”,Builder模式足够用,代码简洁易上手,维护成本低;
  • 如果需要复杂的逻辑组合(与、或、非),Specification模式是最优解,完全解耦筛选逻辑,扩展性拉满;
  • 绝对不要写一堆重载函数,除非你能保证筛选条件永远不会变——但现实中几乎不可能。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:23:02