如何优化基于策略模式的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会导致策略类数量激增难以维护。请问:
- 采用哪种设计模式解决该问题?
- 是否可实现通用
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
相关产品推荐
相关产品推荐

