Inheritance and Abstract Classes

Inheritance models an “is-a” relationship and allows a subclass to specialize a base class. An abstract class cannot be instantiated directly and may require subclasses to implement abstract members. Interfaces are often preferable when only a contract is needed and shared implementation is unnecessary.

Avoid deep inheritance hierarchies that make behavior hard to trace. Composition—building a behavior from smaller collaborators—is often easier to test and evolve. If a base class has many protected hooks or subclasses need to know internal details, reconsider whether the abstraction is too tightly coupled.

Abstract base class
abstract class NotificationChannel {
  abstract send(message: string): Promise<void>;

  protected format(message: string): string {
    return `[Bytes4Dev] ${message}`;
  }
}

class ConsoleChannel extends NotificationChannel {
  async send(message: string): Promise<void> {
    console.log(this.format(message));
  }
}
💡 The abstract method defines a requirement for subclasses. The protected helper shares implementation while restricting access to the class hierarchy.
Key Takeaways
  • Abstract classes can combine contracts and shared behavior.
  • Interfaces are suitable for contracts without implementation.
  • Prefer composition over unnecessarily deep inheritance.
Knowledge Check

Can an abstract class be instantiated directly?