
本文介绍如何利用泛型方法和面向接口的设计,合并两个高度相似但返回类型不同的搜索过滤器构建方法,避免代码重复,同时规避继承滥用导致的脆弱基类问题。
本文介绍如何利用泛型方法和面向接口的设计,合并两个高度相似但返回类型不同的搜索过滤器构建方法,避免代码重复,同时规避继承滥用导致的脆弱基类问题。
在 Java 开发中,当面对多个逻辑完全一致、仅参数或返回类型存在细微差异(如父子类)的方法时,直接复制粘贴会严重损害可维护性。您当前的 getWarehouseSimpleSearchFilter 和 getWarehouseSearchFilter 正是典型场景:二者核心逻辑(权限校验、ID 集合赋值、空集合兜底处理)完全相同,仅操作对象类型不同——WarehouseSimpleSearchFilter 与它的子类 WarehouseSearchFilter。
✅ 推荐方案:泛型 + 接口抽象(而非继承)
首先,不推荐通过泛型直接约束子类(如
因此,应重构为组合优于继承 + 接口契约驱动:
1. 定义统一操作接口
public interface WarehouseSearchFilterContract {
void setCarrierIds(Set<long> carrierIds);
void setShipperIds(Set<long> shipperIds);
Set<long> getCarrierIds();
Set<long> getShipperIds();
}</long></long></long></long>
2. 让两类过滤器实现该接口(移除继承)
public class WarehouseSimpleSearchFilter implements WarehouseSearchFilterContract {
private long companyId;
private Set<long> shipperIds = new HashSet();
private Set<long> carrierIds = new HashSet();
@Override
public void setCarrierIds(Set<long> carrierIds) {
this.carrierIds = Collections.unmodifiableSet(new HashSet(carrierIds));
}
@Override
public void setShipperIds(Set<long> shipperIds) {
this.shipperIds = Collections.unmodifiableSet(new HashSet(shipperIds));
}
// ... getter 实现
}
public class WarehouseSearchFilter implements WarehouseSearchFilterContract {
private long companyId;
private String search;
private Boolean isLocked;
private Set<long> shipperIds = new HashSet();
private Set<long> carrierIds = new HashSet();
@Override
public void setCarrierIds(Set<long> carrierIds) {
this.carrierIds = Collections.unmodifiableSet(new HashSet(carrierIds));
}
@Override
public void setShipperIds(Set<long> shipperIds) {
this.shipperIds = Collections.unmodifiableSet(new HashSet(shipperIds));
}
// ... 其他字段 getter/setter
}</long></long></long></long></long></long></long></long>
3. 编写单一泛型方法(或更简洁的 void 方法)
由于方法始终是就地修改对象且返回原引用,语义上无需返回值(除非刻意构建 Fluent API)。推荐使用 void 方法提升可读性与意图明确性:
private void populateWarehouseFilter(
Set<long> carrierIds,
Set<long> shipperIds,
Authorization authorization,
WarehouseSearchFilterContract filter) {
if (CollectionUtils.isEmpty(carrierIds) && CollectionUtils.isEmpty(shipperIds)) {
Set<long> newCarrierIds = getCompanyIds(WarehousePermission.READ_AVAILABLE, authorization);
Set<long> newShipperIds = getCompanyIds(WarehousePermission.READ_COMPANY, authorization);
filter.setCarrierIds(newCarrierIds);
filter.setShipperIds(newShipperIds);
return;
}
if (!CollectionUtils.isEmpty(carrierIds)) {
checkPermission(WarehousePermission.READ_AVAILABLE, authorization);
filter.setCarrierIds(carrierIds);
}
if (!CollectionUtils.isEmpty(shipperIds)) {
checkPermission(WarehousePermission.READ_COMPANY, authorization);
filter.setShipperIds(shipperIds);
}
}</long></long></long></long>
调用方式简洁清晰:
// 使用简单过滤器 WarehouseSimpleSearchFilter simpleFilter = new WarehouseSimpleSearchFilter(); populateWarehouseFilter(carrierIds, shipperIds, auth, simpleFilter); // 使用增强过滤器 WarehouseSearchFilter enrichedFilter = new WarehouseSearchFilter(); populateWarehouseFilter(carrierIds, shipperIds, auth, enrichedFilter);
⚠️ 注意事项与最佳实践
- 避免无意义的返回值:原方法返回入参对象属于冗余设计,除非明确需要链式调用(此时应让 setXxx() 返回 this 并统一接口定义)。
- 防御性拷贝:示例中 setXxx() 内部已做 unmodifiableSet 封装,确保外部无法篡改内部状态,这是不可变性的重要保障。
- 权限校验前置:checkPermission 应在实际赋值前执行,防止未授权用户触发后续逻辑(即使最终未使用数据)。
- 扩展性考虑:若未来新增第三种过滤器(如 WarehouseAdminSearchFilter),只需实现同一接口,无需修改 populateWarehouseFilter 方法。
通过接口抽象与泛型无关的统一方法,您不仅消除了 100% 的逻辑重复,更构建出高内聚、低耦合、易于测试与演进的领域模型结构。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











