Composition over Inheritance
Favor assembling behavior from smaller, focused objects (composition) rather than inheriting behavior from parent classes. Composition is more flexible and less fragile than inheritance.
Composition over Inheritance = LEGO vs a carved statue. Inheritance is chiseling a statue — beautiful but permanent, and changing the nose means recarving the whole face. Composition is LEGO — snap pieces together, swap the hat anytime.
Inheritance creates a compile-time 'is-a' relationship that is difficult to change. Composition creates a runtime 'has-a' relationship — the component can be swapped, mocked, or replaced. Concrete example: instead of TextFileReader extends FileReader, use TextFileReader { private final FileReader reader; } — you can inject a MockFileReader for tests. The Decorator and Strategy patterns are both expressions of composition over inheritance.
The fragile base class problem: a change to a base class method can break all subclasses, even those that do not override it. This is because subclasses may depend on the internal behavior of the base class (e.g., calling super.method() at the right time, in the right sequence). Josh Bloch's advice: design for inheritance or prohibit it. If a class is not designed with extension in mind (documented override points, invariant preservation), mark it final. Inheritance is appropriate for: (1) a genuine 'is-a' relationship with LSP satisfaction; (2) framework extension points explicitly designed for subclassing; (3) mixin-style interfaces (Java default methods). For everything else, prefer composition. In Go and Rust, inheritance is not available — composition via embedding and traits/interfaces is the only mechanism, which forces better design.
Composition over inheritance is my default. I use inheritance only for a true 'is-a' relationship where the subclass satisfies the base class contract completely (LSP). For reusing behavior, I compose: a Duck has a FlyBehavior and a QuackBehavior injected via constructor. Rubber duck gets SilentQuack; flying duck gets StandardFly. To change rubber duck to squeak, I swap the QuackBehavior — no class hierarchy change. The test benefit is equally important: composed behaviors can be mocked independently. With inheritance, mocking the base class requires complex framework support.
Composition over inheritance is a guideline, not an absolute rule. Frameworks (e.g., Spring MVC controller base classes, JUnit test case) use inheritance deliberately. The rule is: do not use inheritance for code reuse alone. Use it only when the subclass genuinely IS the base type and satisfies all its contracts.