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

如何优化基于策略模式的Product过滤,避免策略类膨胀?

问题描述

现有Product类定义如下:

class Product {
    double Price;
    String name;
    String category;
}

我通过策略模式实现了面向客户端的API,支持按名称、价格过滤Product列表,相关代码如下:

interface IFilterStrategy {
    boolean apply(Product product);
}

class NameMatchStrategy implements IFilterStrategy {
    String name;
    public NameMatchStrategy(String name) {
        this.name = name;
    }

    boolean apply(Product product) {
        return product.getName().equals(name);
    }
}

class PriceMatchStrategy implements IFilterStrategy {
    double minVal;
    double maxVal;
    // 注:原代码构造方法名存在笔误,应为PriceMatchStrategy
    public PriceMatchStrategy(double minVal, double maxVal) {
        this.minVal = minVal;
        this.maxVal = maxVal;
    }

    boolean apply(Product product) {
        return product.getPrice() >= minVal && product.getPrice() <= maxVal;
    }
}

现在需要新增名称前缀匹配策略(如输入"iph"匹配"iphone"),未来还要扩展到分类前缀匹配(如输入"ph"匹配"phone"分类)。若直接创建NamePrefixMatchStrategy、CategoryPrefixMatchStrategy会导致策略类数量激增难以维护。请问:

  1. 采用哪种设计模式解决该问题?
  2. 是否可实现通用StringMatchStrategy并融入现有设计?

解决方案

1. 解决思路:用参数化策略+函数式抽象优化原有策略模式

无需引入复杂的新设计模式,核心是把「从Product中提取属性」和「属性匹配规则」这两个独立逻辑拆解开,作为参数传入通用策略类,用参数化配置替代硬编码的类实例,从根源避免类爆炸问题。

2. 完全可以实现通用字符串匹配策略并融入现有设计

具体实现步骤如下:

第一步:抽象字符串匹配规则

将精确匹配、前缀匹配等字符串逻辑抽成可复用的独立单元:

// 定义匹配规则的函数接口,仅需一个match方法
@FunctionalInterface
interface StringMatcher {
    boolean match(String source, String target);
}

// 实现常用匹配规则,方便全局复用
enum CommonMatchers implements StringMatcher {
    EXACT_MATCH {
        @Override
        public boolean match(String source, String target) {
            return source != null && source.equals(target);
        }
    },
    PREFIX_MATCH {
        @Override
        public boolean match(String source, String target) {
            return source != null && target != null && source.startsWith(target);
        }
    }
    // 后续扩展后缀匹配、包含匹配等,直接新增枚举项即可
}

第二步:实现通用字符串属性过滤策略

创建一个通用策略类,接收三个核心参数:属性提取函数、匹配规则、目标匹配值:

class StringPropertyFilterStrategy implements IFilterStrategy {
    private final Function<Product, String> propertyExtractor;
    private final StringMatcher matcher;
    private final String targetValue;

    public StringPropertyFilterStrategy(Function<Product, String> propertyExtractor, StringMatcher matcher, String targetValue) {
        this.propertyExtractor = propertyExtractor;
        this.matcher = matcher;
        this.targetValue = targetValue;
    }

    @Override
    public boolean apply(Product product) {
        String sourceValue = propertyExtractor.apply(product);
        return matcher.match(sourceValue, targetValue);
    }
}

第三步:替换原有策略,简化客户端调用

原来的NameMatchStrategy可以直接被通用策略替代,客户端调用时只需指定对应的属性和规则:

// 原精确匹配名称的逻辑
IFilterStrategy exactNameFilter = new StringPropertyFilterStrategy(
    Product::getName,
    CommonMatchers.EXACT_MATCH,
    "iphone"
);

// 新增的名称前缀匹配逻辑
IFilterStrategy namePrefixFilter = new StringPropertyFilterStrategy(
    Product::getName,
    CommonMatchers.PREFIX_MATCH,
    "iph"
);

// 未来扩展的分类前缀匹配逻辑
IFilterStrategy categoryPrefixFilter = new StringPropertyFilterStrategy(
    Product::getCategory,
    CommonMatchers.PREFIX_MATCH,
    "ph"
);

额外优化:用Lambda简化简单场景

因为IFilterStrategy是函数式接口(仅一个抽象方法),如果是简单的一次性过滤逻辑,甚至不用创建通用策略类,直接用Lambda表达式实现:

// 名称精确匹配的Lambda实现
IFilterStrategy exactNameFilter = product -> product.getName().equals("iphone");

// 名称前缀匹配的Lambda实现
IFilterStrategy namePrefixFilter = product -> product.getName() != null && product.getName().startsWith("iph");

但如果匹配规则需要多处复用,还是建议用StringMatcher抽象,避免重复编写相同的判断逻辑。

方案优势

  • 避免类爆炸:新增属性(如品牌)或匹配规则(如后缀匹配)时,无需创建新策略类,仅需扩展匹配规则或传入新的属性提取函数。
  • 灵活性强:可任意组合属性提取逻辑与匹配规则,快速实现各种复杂字符串过滤需求。
  • 代码复用性高:匹配规则(如前缀匹配)可在名称、分类等不同属性的过滤逻辑中复用。

内容的提问来源于stack exchange,提问作者Will Su

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 05:42:09