组合优于继承的探讨
组合优于继承的探讨
技术背景
在面向对象编程中,继承和组合是两种重要的代码复用和设计方式。继承允许子类继承父类的属性和方法,形成“is-a”关系;组合则是通过在一个类中包含另一个类的对象,形成“has-a”关系。长期以来,“组合优于继承”的观点被广泛提及,理解两者的区别和适用场景对于编写高质量的代码至关重要。
实现步骤
继承的实现
继承通过定义子类来继承父类的特性。以下是一个简单的 Java 示例:
1 |
|
在这个例子中,Employee
类继承自 Person
类,Employee
“is a” Person
。
组合的实现
组合是将其他类的对象作为成员变量。示例如下:
1 |
|
这里,Employee
类包含一个 Person
对象,Employee
“has a” Person
。
核心代码
继承示例
1 |
|
组合示例
1 |
|
最佳实践
优先使用组合
当代码复用是唯一目的,且父类的部分公共方法对子类没有意义时,应优先考虑组合。例如,一个 Stack
类不应该继承 List
类,因为 Stack
只需要 pop
、push
和 peek
等功能,而不需要 List
的其他方法。
谨慎使用继承
- 当子类和父类之间存在明确的“is-a”关系,且子类可以完全替代父类时,可使用继承。例如,
Dog
类可以继承Animal
类,因为狗是动物,且狗可以做动物能做的一切事情。 - 如果需要实现多态,可使用继承,但要遵循 Liskov 替换原则。
遵循设计原则
遵循单一职责原则,每个类只负责一项职责。使用接口和策略模式来提供可定制的行为,避免过度使用继承。
常见问题
继承的问题
- 难以维护:修改父类可能会影响到所有子类,导致代码难以维护。
- 违反封装原则:父类的实现细节暴露给子类,破坏了封装性。
- 菱形继承问题:在多继承中可能会出现菱形继承问题,导致代码逻辑混乱。
组合的问题
- 代码冗余:有时需要编写额外的代码来委托方法调用,可能会违反 DRY 原则。
- 性能问题:方法调用链过长可能会影响性能。
组合优于继承的探讨
https://119291.xyz/posts/composition-over-inheritance-discussion/