Sobes.tech
Middle

In what situations is inheritance in object-oriented programming useful, and in which cases should it be avoided?

sobes.tech AI

Answer from AI

Inheritance is useful for:

  • Code reuse: Common functionality is extracted into a base class.
  • Creating type hierarchies (is-a relationship): When a derived class is a kind of the base class.
  • Polymorphism: The ability to treat objects of different types through a common interface of the base class.
  • Extending functionality: Adding new methods or fields to an existing class.

Inheritance should be avoided when:

  • Liskov substitution principle is violated: A derived class cannot fully replace the base class without affecting program correctness.
  • Tight coupling between classes: Changes in the base class may undesirably affect all derived classes.
  • Combining functionalities from unrelated sources (has-a relationship): Composition is preferable.
  • Excessive hierarchy complexity: Deep inheritance chains are hard to maintain and understand.
  • Inheritance for code reuse when functionality is not a common "type": Use utility classes or delegation instead.
// Example where inheritance is useful
open class Animal {
    open fun makeSound() {
        println("Generic animal sound")
    }
}

class Dog : Animal() { // Dog IS-A Animal
    override fun makeSound() {
        println("Woof")
    }
}

class Cat : Animal() { // Cat IS-A Animal
    override fun makeSound() {
        println("Meow")
    }
}
// Example where composition is preferable over inheritance for "has-a" relationship
class Engine {
    fun start() { println("Engine started") }
}

// Instead of inheriting from Engine for Car
// class Car : Engine() // Bad design, Car IS-NOT an Engine

class Car {
    private val engine = Engine() // Car HAS-A Engine
    fun drive() {
        engine.start()
        println("Car driving")
    }
}