Lesson Completion
Back to course

Design Principles: Inheritance Best Practices

Intermediate
18 minutes4.8Java

1. The Hook (The "Byte-Sized" Intro)

In a Nutshell: Inheritance is powerful but dangerous when misused. Three critical design principles govern good inheritance: Liskov Substitution Principle (LSP), Favor Composition Over Inheritance, and Program to an Interface. These principles prevent fragile, tightly-coupled code hierarchies that become unmaintainable nightmares.

Think of power tools. A chainsaw (inheritance) can build amazing things but can also cause damage if misused. Following safety principles (design principles) ensures you build, not destroy!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy: Building Construction

  • LSP: A replacement part must work exactly like the original
  • Composition > Inheritance: Use modular components instead of a monolithic structure
  • Program to Interface: Design for pluggability, not specifics

Hand-Drawn Logic Map

graph TD A[Design Question] --> B{True 'is-a'?} B -- No --> C[Use Composition] B -- Yes --> D{Passes LSP test?} D -- No --> C D -- Yes --> E{Need polymorphism?} E -- Yes --> F[Inherit from Interface] E -- No --> C

3. Technical Mastery (The "Deep Dive")

1. Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of a subclass without breaking the application.

The "Why": If a Square extends Rectangle and overrides setWidth() to also change height (since squares have equal sides), code expecting Rectangle behavior breaks!

java
// ❌ Violates LSP class Rectangle { void setWidth(int w) { this.width = w; } void setHeight(int h) { this.height = h; } int area() { return width * height; } } class Square extends Rectangle { @Override void setWidth(int w) { this.width = w; this.height = w; // ❌ Breaks Rectangle's contract! } } // Test breaks: Rectangle r = new Square(); r.setWidth(5); r.setHeight(10); assert r.area() == 50; // ❌ FAILS! (Returns 100)

2. Favor Composition Over Inheritance

Use Inheritance When:

  • True "is-a" relationship exists
  • Polymorphism is needed
  • Hierarchy is shallow (2-3 levels max)

Use Composition When:

  • "Has-a" relationship
  • Need flexibility to swap components
  • Avoiding tight coupling

3. Program to an Interface

Definition: Depend on abstractions (interfaces/abstract classes), not concrete implementations.

java
// ✅ Good List<String> list = new ArrayList<>(); // Depend on interface // ❌ Bad ArrayList<String> list = new ArrayList<>(); // Depend on concrete class

4. Interactive & Applied Code

java
// Example: Payment System // ❌ BAD: Inheritance-heavy class Payment { void process() { } } class CreditCardPayment extends Payment { } class PayPalPayment extends Payment { } // Problem: Can't combine behaviors (e.g., PayPal + Insurance) // ✅ GOOD: Composition + Interface interface PaymentMethod { void process(double amount); } class PaymentProcessor { private PaymentMethod method; // Composition! PaymentProcessor(PaymentMethod method) { this.method = method; } void processPayment(double amount) { method.process(amount); } void changeMethod(PaymentMethod newMethod) { this.method = newMethod; // Flexibility! } } class CreditCard implements PaymentMethod { public void process(double amount) { System.out.println("Charging $" + amount + " to credit card"); } } class PayPal implements PaymentMethod { public void process(double amount) { System.out.println("Transferring $" + amount + " via PayPal"); } } public class Main { public static void main(String[] args) { PaymentProcessor processor = new PaymentProcessor(new CreditCard()); processor.processPayment(100); // Easy to switch! processor.changeMethod(new PayPal()); processor.processPayment(50); } }

5. The Comparison & Decision Layer

Versus Table

ScenarioInheritanceComposition
Car HAS-AN Engine❌ Car extends Engine✅ Car contains Engine
Dog IS-AN Animal✅ Dog extends Animal❌ Dog contains Animal
Employee HAS-A Role❌ Employee extends Role✅ Employee contains Role

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Explain the Liskov Substitution Principle with an example." Answer: LSP states that subclasses must be substitutable for their parent classes. Classic violation: Circle/Ellipse problem. If Circle extends Ellipse, setting different radii breaks Circle's contract. Solution: Use separate classes or composition.

Pro-Tip: The SOLID principles summary:

  • Single Responsibility
  • Open/Closed
  • Liskov Substitution (we covered this!)
  • Interface Segregation
  • Dependency Inversion

These are the foundation of maintainable OOP!

Topics Covered

Object-Oriented ProgrammingInheritance

Tags

#java#inheritance#polymorphism#super-class#sub-class#interview-prep

Last Updated

2025-02-01