Parallel Inheritance Hierarchies

Sign of Smell

This code smell is a special case of Shotgun Surgery. It occurs when making changes to one part of the code requires making simultaneous changes to another part. However, in this code smell, the focus is solely on creating new subclasses. When we create a new subclass for a class, we also need to create a subclass for another class.

This smell typically occurs in object-oriented programming when you have two sets of class hierarchies that are closely related to each other and tend to reflect each other's changes.

All was well as long as the hierarchy stayed small. However, as the organization grew and new classes were added, making changes to the hierarchy became increasingly challenging.

Reason of Smell

Let's explore why it's considered problematic:

  • Readability: Parallel hierarchies can make the code harder to read and understand because developers need to navigate two separate hierarchies that mirror each other's structure. This can hinder collaboration and make it difficult for new team members to understand the code's design.

  • High Coupling: Parallel hierarchies often indicate high coupling between different parts of your codebase. In fact, parallelism is an indication of coupling. It means that we have to perform the same changes as if it were a couple of classes.

  • Code Duplication: Maintaining parallel hierarchies often leads to code duplication. Similar code or behavior might exist in both hierarchies, which is not only wasteful but also increases the likelihood of inconsistencies and bugs if the two sides diverge over time.

  • Complexity: Parallel Inheritance Hierarchies introduce additional complexity into your codebase. You have two sets of hierarchies that must be kept in sync. Any change in one hierarchy often requires a corresponding change in the other. This complexity makes the code harder to understand and maintain.

  • Reduced Flexibility: The presence of parallel hierarchies can make it more challenging to add new functionality or adapt to changing requirements. Any change may require modifications in both hierarchies, leading to increased development time and effort.

  • Maintenance Challenges: Keeping two parallel hierarchies synchronized can be error-prone and time-consuming. When you add a new class or make a change in one hierarchy, you must remember to make the corresponding changes in the other hierarchy. This can lead to inconsistencies and errors if you forget to update one side.

Refactoring Recipes

To address the Parallel Inheritance Hierarchies code smell, you can consider several refactoring techniques:

  • Move Method

  • Move Field

  • Extract SuperClass

  • Extract Interface

  • Replace Inheritance with Delegation

Move Method / Move Field

To de-couple parallel class hierarchies, we can follow these steps:

  1. Establish a relationship between instances of one hierarchy and instances of another hierarchy by allowing instances of one hierarchy to refer to instances of the other hierarchy.

  2. Eliminate the hierarchy in the referred class by using techniques like Move Method or Move Field, which involve relocating methods or fields to more suitable classes.

By implementing these steps, we can effectively decouple parallel inheritance hierarchies, resulting in a codebase that is more flexible and maintainable.

Extract SuperClass

When we have two classes where creating a new subclass in one part necessitates creating a subclass in the other, it indicates that these two classes may have similar behavior that can be shared.

In this case, we should consider to extract the common superclass for classes in one or both of the parallel hierarchies

Extract Interface

Similarly, we can also consider defining a common interface that classes in one or both of the parallel hierarchies can work with. To avoid we have to create a subclass in the other when we create a subclass in the base class.

Replace Inheritance with Delegation

We can often solve this smell by replacing inheritance with delegation. This approach can help reduce complexity, improve code organization, and eliminate the need for parallel hierarchies.

In this refactoring technique, we can replace the hierarchical relationship between classes with a composition-based approach. Instead of inheriting from a base class, a class will delegate specific functionality to another class that specializes in providing that functionality.

Reference

https://refactoring.guru/smells/parallel-inheritance-hierarchies

https://www.jyt0532.com/2020/04/13/parallel-inheritance-hierarchies/

https://refactoring.guru/replace-inheritance-with-delegation