views:

202

answers:

5

What are the disadvantages of using inheritance as a way of reusing code?

A: 

It requires inheritance (to be reflective about it), which is only one of many possible structures for code. That's why we have procedural programming, functional programming, object-oriented programming, aspect-oriented programming, declarative programming, etc. See programming paradigms.

eruciform
+2  A: 

IIRC, the Liskov Substitution Principle 1) postulates that one should be able to substitute a class by any of its derived classes; that is, derived classes should not behave radically different than, or violate the contract set up by their base classes.

Obviously then, this principle puts an intentional limit on how a (base) class can be "re-used" by another class (which is derived from it). Other means of using a class, such as aggregation or composition, aren't thus limited by the principle.


1) See e.g. The Liskov Substitution Principle (links to a PDF document).

stakx
+4  A: 

Using inheritance to achieve code reuse suffers from the following problems:

  1. You cannot change the reused behaviour at runtime. Inheritance is a compile-time dependency, so if a GameClient class inherits from TCPSocket to reuse the connect() and write() member functions, it has the TCP functionality hardcoded. You cannot change this at runtime.

  2. You cannot replace the reused behaviour from the outside for the sake of testing. If a GameClient class inherits from TCPSocket so that it gets write() (for writing data to a socket), you cannot exchange this code from the outside. You cannot plug in a different write() function which logs all data which GameClient wants to write to a file or so.

  3. You are dependant on multiple inheritance for all but the most simple applications. This opens the door for diamond shaped inheritance trees which increase the code complexity a lot.

Preferring composition over inheritance to reuse code avoids all these issues.

Frerich Raabe
+1  A: 

Using inheritance means that when you call a method (or virtual method in C++) in the same class, it isn't immediately clear that you might actually be calling a subclass' method. A code smell that can result is a call stack that goes up and down the class hierarchy, which effectively means that your superclass and subclass are in a circular dependency.

Using composition and interfaces makes it clear that there are multiple possible implementations, and also makes it obvious when there is a circular dependency (which should generally be removed).

(Composition makes circular dependencies obvious for a couple of reasons (assuming you use pass dependencies of a class in via the constructor). If A and B depend on each other, then either A constructs B and passes this or self into B's constructor, which is a clear sign of circular dependency, or some other class constructs both A and B, which turns out to be impossible since A requires B to be constructed first, and B requires A to be constructed first.)

Michael Williamson
A: 

If you use inheritance, you get tied into a mutable-state object-oriented paradigm. If you try to use immutable objects instead, you end up writing [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)

and there isn't code reuse. Thus, you use mutable objects, and madness ensues :).

gatoatigrado