SOLID依赖倒置原则:工具函数应定义接口还是直接使用具体实现?
方案选型结论与分析
先明确核心需求前提
你的核心诉求是将判断逻辑封装为可跨类复用的工具函数,我们基于这个前提对三个方案逐一分析:
1. 私有方法实现方案:不适用当前场景
export class UserRepository { constructor ( private readonly database: DatabaseProtocol ) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (this.isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } isEmpty (data: Record<string, any>): boolean { return Object.keys(data).length === 0 } }
这个方案只能在当前UserRepository类内部使用isEmpty方法,完全无法支持跨类复用的需求,只有当该判断逻辑永远不需要被其他类调用时才可以考虑,直接排除。
2. 具体实现方案:通用稳定工具函数的最佳实践
// 单独抽离的公共util模块,比如src/utils/object.ts export const isEmpty = (data: Record<string, any>): boolean => Object.keys(data).length === 0 // 业务类中直接导入使用 import { isEmpty } from '@/utils/object' export class UserRepository { constructor (private readonly database: DatabaseProtocol) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } }
适用场景
如果isEmpty是逻辑稳定、无副作用、不需要多态实现的通用工具函数,这个方案是最优解:
- 代码简洁,不需要额外修改类的构造函数注入依赖
- 复用成本极低,任意需要的类直接导入即可使用
- 符合绝大多数项目中通用工具函数的编码规范
3. 接口注入方案:需要多态/测试mock场景的最佳实践
// 接口定义 export interface IsEmptyProtocol { isEmpty: (data: Record<string, any>) => boolean } // 业务类通过构造函数注入实现 export class UserRepository { constructor ( private readonly database: DatabaseProtocol, private readonly emptyValidator: IsEmptyProtocol ) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (this.emptyValidator.isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } }
适用场景
如果满足以下任意一个条件,这个方案是更合理的选择:
isEmpty的判断逻辑存在多种实现变体,比如不同业务场景下对“空”的定义不同,需要灵活替换实现- 需要对
UserRepository做单元测试,希望可以mock掉isEmpty的实现,避免测试依赖外部工具函数的逻辑 - 项目严格遵循依赖倒置原则,要求所有外部依赖都通过注入的方式引入
最终选型建议
绝大多数常规业务场景下,直接选择具体实现方案即可,既满足复用需求也没有过度设计的问题。如果有上述的多态、测试mock需求,再考虑切换为接口注入方案。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

