When to use abstract classes?
Object-Oriented Design practice on Codemia
Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.
Introduction
Abstract classes are useful when related types must share both a common contract and reusable implementation. They help enforce workflow invariants while allowing subclass-specific behavior at defined extension points. The right decision depends on whether your problem needs shared stateful logic or only a capability interface.
What Abstract Classes Provide
An abstract class can include:
- abstract methods that subclasses must implement
- concrete methods with shared behavior
- protected helper methods and fields
- constructors for shared initialization
This combination makes abstract classes suitable for template-style workflows.
generate defines invariant flow, while subclasses customize fetch and format behavior.
When Abstract Classes Are a Good Fit
Choose an abstract class when:
- subclasses are conceptually in one family
- meaningful implementation must be shared
- extension points should be controlled
- common initialization logic belongs in one place
Both implementations reuse orchestration and cleaning logic, reducing duplication.
When Interface Is Better
If you only need a contract without shared stateful behavior, prefer interfaces.
Interface-first design is often better when:
- implementations are from unrelated domains
- multiple inheritance of behavior contracts is needed
- you want low coupling and flexible composition
Modern Java default interface methods can provide small shared helpers without forcing inheritance hierarchy.
Abstract Class Versus Composition
Inheritance is not always the best tool. If behavior varies by runtime policy or changes often, composition with strategy objects can be cleaner.
Composition avoids deep inheritance chains and keeps changes localized.
Practical Decision Checklist
Use abstract class when:
- shared algorithm skeleton is required
- protected reusable internals make design clearer
- constructor-level shared setup is necessary
Use interface or composition when:
- shared implementation is minimal
- multiple independent behaviors must be mixed
- hierarchy depth would increase complexity
This checklist helps prevent overusing inheritance.
Testing Implications
Abstract-class designs benefit from two testing layers:
- base-class contract tests that verify invariant workflow behavior
- subclass tests that verify specialized steps
For template-method patterns, test that base orchestration calls extension points in the expected order. This protects behavior when subclasses are added later. Without these tests, inheritance hierarchies can drift and violate assumptions silently.
Example test idea:
- instantiate a minimal test subclass
- capture method call sequence
- assert shared template method still enforces required order
This approach catches regressions that simple output assertions may miss. It improves long-term maintainability.
Common Pitfalls
- Creating abstract base classes with little or no shared implementation.
- Building deep inheritance hierarchies for simple capability sharing.
- Using abstract classes where composition would isolate change better.
- Exposing too many protected internals and weakening encapsulation.
- Mixing unrelated domain concepts into one inheritance tree.
Summary
- Abstract classes are best for shared behavior plus controlled extension points.
- Interfaces are better for pure contracts and loose coupling.
- Composition is often better when behavior changes frequently.
- Keep inheritance shallow and purposeful.
- Choose abstraction style based on change patterns, not habit.
Related reading
- Where do operations on models belong in Application Design Patterns?
- Where does Microsoft.Practices.ServiceLocation come from?
- Where to put Bean in Spring Boot?
- Which .NET Dependency Injection frameworks are worth looking into?
- Why are arrays covariant but generics are invariant?
- Why are properties without a setter not serialized
- Why are Python's 'private' methods not actually private?
- Why are Python's 'private' methods not actually private?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Object-Oriented Design practice on Codemia
Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.