依赖倒置原则疑问:低层模块能否隐式引用其他低层模块?
1. 支付处理器内部实例化网关是否违反DIP?
这个示例的核心目标是让高层模块Store与底层支付网关(Stripe/PayPal)解耦,从这个角度看它已经符合依赖倒置原则——Store只依赖支付处理器的抽象行为(调用pay方法),而非具体的网关实现。
但StripePaymentProcessor和PayPalPaymentProcessor内部直接实例化网关类,确实属于隐藏依赖,会导致处理器与对应网关紧耦合。不过这并不违反DIP的核心规则:DIP约束的是高层模块与低层模块之间的依赖关系,而非低层模块内部的依赖结构。这种做法只是将依赖从Store转移到了处理器类,对于Store来说依然保持了松耦合,满足了示例的演示需求。
当然,从可维护性和可测试性角度,这种紧耦合并不理想——比如无法在测试时模拟网关,也无法轻松替换网关的实现。如果要进一步优化,可以通过构造函数注入网关实例,让处理器也依赖抽象而非具体类。
2. 是否允许低层模块实例化其他低层模块?
可以,但要根据场景权衡。DIP并没有禁止低层模块之间的直接依赖,它的核心要求是高层模块不能依赖低层模块的具体细节。如果低层模块之间的关系稳定、不需要频繁变更或测试,直接实例化是完全可行的。但如果未来需要替换或模拟其中某个组件,依赖注入会是更优选择,能提升代码的灵活性和可测试性。
3. TypeScript中使用接口注入能否解决依赖问题?
是的,使用PaymentProcessor接口可以进一步强化抽象层,完美契合DIP的要求:
- 首先定义
PaymentProcessor接口,明确规定pay方法的参数和返回值; - 让
StripePaymentProcessor和PayPalPaymentProcessor都实现这个接口; Store的构造函数接收PaymentProcessor类型的参数,而非具体类。
这种做法不仅让Store彻底与具体支付实现解耦,还带来了TypeScript的类型安全保障——任何不符合接口规范的处理器都无法被注入到Store中。需要注意的是,这并不能自动解决处理器内部依赖网关的问题(比如直接实例化Stripe),但它解决了Store与支付系统之间的核心依赖问题,让整体架构更清晰、更易扩展。
内容的提问来源于stack exchange,提问作者ElMoscaviador

