Parallel Inheritance Hierarchies is a code smell where two separate class hierarchies mirror each other in lockstep — every time a new subclass is added to Hierarchy A, a corresponding subclass must be created in Hierarchy B, creating a maintenance dependency between the two trees that doubles the work of every extension and introduces a systematic risk that the hierarchies fall out of sync over time.
What Is Parallel Inheritance Hierarchies?
The smell manifests as two class trees that grow together:
- Shape/Renderer Split: Shape → Circle, Rectangle, Triangle — and separately ShapeRenderer → CircleRenderer, RectangleRenderer, TriangleRenderer. Adding Diamond to the Shape hierarchy mandates adding DiamondRenderer to the Renderer hierarchy.
- Vehicle/Engine Split: Vehicle → Car, Truck, Bus — and Engine → CarEngine, TruckEngine, BusEngine. Every new vehicle type requires a new engine type.
- Entity/DAO Split: Entity → User, Order, Product — and DAO → UserDAO, OrderDAO, ProductDAO. Every new entity requires a new DAO.
- Notification/Handler Split: Notification → EmailNotification, SMSNotification, PushNotification — mirrored by NotificationHandler → EmailHandler, SMSHandler, PushHandler.
Why Parallel Inheritance Hierarchies Matter
- Extension Cost Doubling: Every new concept requires additions to two hierarchies instead of one. If there are 5 parallel hierarchies mirroring each other (entity + DAO + validator + serializer + factory), adding one new domain concept requires creating 5 new classes. This multiplier grows with the number of parallel hierarchies and directly increases the per-feature cost.
- Synchronization Burden: Teams must remember to update both hierarchies simultaneously. Under time pressure, developers add Diamond to the Shape hierarchy but forget DiamondRenderer. Now Shape handles diamonds but the renderer silently falls back to a default or crashes when a Diamond is rendered. The error is non-obvious and potentially reaches production.
- Cross-Hierarchy Coupling: Code that works with both hierarchies must manage the pairing — "for this Circle I need a CircleRenderer." This coupling is fragile: changing the naming convention, splitting a hierarchy, or rebalancing the hierarchy structure requires updating all the cross-hierarchy pairing code.
- Violated Locality: The logic for handling a concept is divided across two (or more) classes in separate hierarchies. Understanding how Circle is fully handled requires reading both Circle and CircleRenderer — related logic that should be together is separated by the hierarchy structure.
Refactoring: Merge Hierarchies
Move Method into Hierarchy: If Hierarchy B's classes only serve to operate on Hierarchy A's corresponding class, move the methods into Hierarchy A's classes directly. Circle gains a render() method; CircleRenderer is eliminated.
Visitor Pattern: When rendering (or any processing) logic must be separated from the shape hierarchy (e.g., for dependency reasons), the Visitor pattern provides a cleaner alternative to parallel hierarchies — a single ShapeVisitor interface with visit(Circle), visit(Rectangle) methods. Adding a new shape requires one class addition plus updating the visitor interface, with compile-time enforcement that all visitors handle the new shape.
Generics/Templates: For structural pairings like Entity/DAO, generics can eliminate the parallel hierarchy entirely: GenericDAO<T extends Entity> replaces UserDAO, OrderDAO, ProductDAO with one parameterized class.
When Parallel Hierarchies Are Acceptable
Some frameworks mandate parallel hierarchies (particularly DAO/Entity, ViewModel/Model patterns in some MVC frameworks). When dictated by architectural constraints: document the pairing rule explicitly and enforce it through code generation or convention checking rather than relying on developers to remember.
Tools
- NDepend / JDepend: Hierarchy analysis and dependency visualization.
- IntelliJ IDEA: Class hierarchy views that visually expose parallel tree structures.
- SonarQube: Module coupling analysis can expose parallel dependency structures.
- Designite: Design smell detection for structural hierarchy problems.
Parallel Inheritance Hierarchies is coupling the trees — the structural smell that locks two class hierarchies into a lockstep dependency relationship, doubling the work of every extension, introducing systematic synchronization risk, and dividing the logic for each concept across two separate locations that must always be updated in tandem.