使用继承作为重用代码的方法有哪些缺点?
答案 0 :(得分:6)
显然,这个原则对一个(基类)类如何被另一个类(从它派生)“重用”有意限。其他使用类的方法,例如聚合或组合,不受此原则的限制。
1)参见例如The Liskov Substitution Principle(链接到PDF文档)。
答案 1 :(得分:6)
使用继承来实现代码重用会遇到以下问题:
您无法在运行时更改重用的行为。继承是编译时依赖项,因此如果GameClient
类继承自TCPSocket
以重用connect()
和write()
成员函数,则它具有硬编码的TCP功能。您无法在运行时更改此内容。
为了进行测试,您无法从外部替换重用的行为。如果GameClient
类继承自TCPSocket
以使其获得write()
(用于将数据写入套接字),则无法从外部交换此代码。您无法插入另一个write()函数,该函数会记录GameClient
想要写入文件的所有数据。
除了最简单的应用程序之外,您依赖于多重继承。这为diamond shaped inheritance trees打开了大门,这大大增加了代码的复杂性。
优先使用继承的组合来重用代码可以避免所有这些问题。
答案 2 :(得分:3)
使用继承意味着当您在同一个类中调用方法(或C ++中的虚方法)时,您可能实际上无法实际调用子类的方法。可能导致的代码气味是一个在类层次结构中上下移动的调用堆栈,这实际上意味着您的超类和子类处于循环依赖中。
使用组合和接口清楚地表明存在多个可能的实现,并且当存在循环依赖(通常应该被删除)时也很明显。
(由于几个原因,组合使循环依赖显而易见(假设您通过构造函数使用类的传递依赖关系)。如果A和B相互依赖,则A构造B并传递this
或者self
进入B的构造函数,这是循环依赖的明显标志,或者其他一些类构造A和B,结果证明是不可能的,因为A要求首先构造B,B要求A为首先构建。)
答案 3 :(得分:0)
它需要继承(反思它),这只是代码的许多可能结构之一。这就是为什么我们有程序编程,函数编程,面向对象编程,面向方面编程,声明性编程等等。参见programming paradigms。
答案 4 :(得分:0)
如果使用继承,则会陷入可变状态面向对象的范例。如果您尝试使用不可变对象,则最终会编写[pseudocode]
class A (int X, int Y)
def self.nextX(int nextX) = A(newX, self.Y)
class B (int X, int Y, int Z) extends A(X, Y)
def self.nextX(int nextX) = B(newX, self.Y, self.Z)
并且没有代码重用。因此,你使用可变对象,然后疯狂:)。