
本文介绍如何将一个职责混杂的数据库聚合函数拆分为多个高内聚、低耦合的私有方法,通过提取查询构建逻辑、聚合计算和数据获取职责,显著提升代码可测试性与复用性。
本文介绍如何将一个职责混杂的数据库聚合函数拆分为多个高内聚、低耦合的私有方法,通过提取查询构建逻辑、聚合计算和数据获取职责,显著提升代码可测试性与复用性。
在实际开发中,一个看似简单的 getTotalProductsNumber() 方法,若长期承载数据源初始化、条件组装、SQL 聚合与类型转换等多重职责,会逐渐演变为难以调试、难以复用、难以单元测试的“上帝函数”。重构的核心目标不是“让代码更短”,而是明确边界、分离关注点、增强可测性。
以下是一个专业、可持续的重构方案:
✅ 重构原则
- 单一职责:每个函数只做一件事(如“构造查询器”、“执行求和查询”、“提取首行结果”);
- 无副作用:私有辅助方法不修改外部状态,仅接收参数并返回结果;
- 类型安全:全程保持 PHP 7.4+ 类型声明,避免运行时隐式转换风险。
✅ 重构后代码
public function getTotalProductsNumber(): int
{
$dataset = 'supplier_invoice_products INNER JOIN supplier_invoices AS si USING (supplier_invoice_id)';
$extras = $this->getConditionsAndOptions();
$queryBuilder = [
'dbm' => new Dbm_Supplier($dataset),
'where' => $extras['where'],
'opt' => $extras['opt'],
];
return $this->sumOfProductQuantities($queryBuilder);
}
private function sumOfProductQuantities(array $queryBuilder): int
{
$select = 'SUM(product_quantity) AS sumTotal';
$row = $this->queryBuilderFirstRow($queryBuilder, $select);
return (int)($row['sumTotal'] ?? 0); // 防御性处理:空结果返回 0
}
private function queryBuilderFirstRow(array $qb, string $select): array
{
return $qb['dbm']->findFirstSimple($qb['where'], $select, $qb['opt']);
}
? 关键改进说明
- getTotalProductsNumber():仅负责协调——准备数据源与条件,委托给专用方法,自身不再触碰 SQL 或类型转换;
- sumOfProductQuantities():专注业务语义——“对产品数量求和”,并封装空值兜底逻辑(?? 0),避免 (int)null 返回 0 的隐式行为引发歧义;
- queryBuilderFirstRow():抽象数据访问层——统一封装 Dbm_Supplier::findFirstSimple() 调用,未来切换 ORM 或添加日志/缓存时只需修改此处。
⚠️ 注意事项
- 确保 getConditionsAndOptions() 始终返回含 'where' 和 'opt' 键的数组,建议为其添加返回类型声明 : array
并做键存在性校验; - 若项目已引入 Doctrine DBAL 或 Laravel Eloquent,强烈建议逐步迁移至标准 ORM 查询构建器,从根本上消除手写 SQL 字符串带来的 SQL 注入与可维护性风险;
- 所有私有方法均应补充 PHPDoc 注释,明确参数含义与返回约定,例如 @param array{dbm: Dbm_Supplier, where: string, opt: array} $qb。
该重构不仅使原函数行数减少、逻辑清晰,更为后续扩展(如支持按品类统计、分页汇总)打下坚实基础——只需新增类似 countByCategory() 方法,复用相同的 queryBuilderFirstRow 底层能力即可。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











