Inheritance 使用继承作为重用代码的方法有哪些缺点?

Inheritance 使用继承作为重用代码的方法有哪些缺点?,inheritance,code-reuse,Inheritance,Code Reuse,使用继承作为重用代码的一种方式有哪些缺点?它需要继承(对其进行反射),这只是代码的许多可能结构之一。这就是为什么我们有过程式编程、函数式编程、面向对象编程、面向方面编程、声明式编程等。参见。IIRC,Liskov替换原则1)假设一个人应该能够用任何派生类替换一个类;也就是说,派生类的行为不应该与它们的基类完全不同,也不应该违反它们所建立的契约 显然,这个原则有意限制了一个(基类)类如何被另一个类(从它派生的类)“重用”。因此,使用类的其他方法(如聚合或组合)不受该原则的限制 1)参见例如(PD

使用继承作为重用代码的一种方式有哪些缺点?

它需要继承(对其进行反射),这只是代码的许多可能结构之一。这就是为什么我们有过程式编程、函数式编程、面向对象编程、面向方面编程、声明式编程等。参见。

IIRC,Liskov替换原则1)假设一个人应该能够用任何派生类替换一个类;也就是说,派生类的行为不应该与它们的基类完全不同,也不应该违反它们所建立的契约

显然,这个原则有意限制了一个(基类)类如何被另一个类(从它派生的类)“重用”。因此,使用类的其他方法(如聚合或组合)不受该原则的限制



1)参见例如(PDF文档的链接)。

使用继承实现代码重用会遇到以下问题:

  • 不能在运行时更改重用的行为。继承是编译时依赖项,因此如果
    GameClient
    类继承自
    TCPSocket
    以重用
    connect()
    write()
    成员函数,则它具有硬编码的TCP功能。您不能在运行时更改此设置

  • 您不能为了测试而从外部替换重用的行为。如果
    GameClient
    类继承自
    TCPSocket
    ,因此它获得
    write()
    (用于将数据写入套接字),则无法从外部交换此代码。您不能插入另一个write()函数,该函数记录
    GameClient
    希望写入文件的所有数据

  • 除了最简单的应用程序外,所有应用程序都依赖于多重继承。这打开了一扇门,大大增加了代码的复杂性


  • 与重用代码相比,更倾向于组合避免了所有这些问题。

    使用继承意味着,当您在同一类中调用方法(或C++中的虚拟方法)时,并不立即清楚您是否实际调用了子类的方法。可能产生的代码气味是在类层次结构中上下移动的调用堆栈,这实际上意味着您的超类和子类处于循环依赖关系中

    使用组合和接口可以清楚地表明存在多个可能的实现,并且在存在循环依赖(通常应该删除)时也可以清楚地看到这一点


    (由于以下几个原因,组合使循环依赖关系变得明显(假设您通过构造函数在中使用类的传递依赖关系)。如果A和B相互依赖,则A构造B并将
    this
    self
    传递到B的构造函数中,这是循环依赖的明显标志,或者其他一些类同时构造A和B,这是不可能的,因为A要求先构造B,B要求先构造A。)

    如果您使用继承,您将被绑定到一个可变状态的面向对象范例中。如果尝试使用不可变对象,则最终会编写[伪代码]

    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)
    

    而且没有代码重用。因此,你使用可变对象,然后疯狂:“< /P>不是在你的教科书中吗?当你想继承一个以上的类的功能时,会发生什么,但是你的语言不支持多重继承?C++支持多继承的java支持,使用接口不知道其他的。”