Category
Dealing with Generalization
Techniques that move members up and down class hierarchies, create or collapse hierarchies, and swap inheritance for delegation.
What these techniques solve
Inheritance hierarchies drift. Sibling classes copy the same method, a base class carries fields only one subclass uses, a subclass inherits a contract it can't honor, or a hierarchy has outlived the variation that justified it. These techniques reshape hierarchies, and replace inheritance with composition when the "is-a" relationship no longer holds.
Put shared behavior at the lowest level that all of its users share, and prefer delegation whenever a subclass would ignore part of what it inherits.
Members
- Pull Up Field: move a field that all subclasses declare into the superclass.
- Pull Up Method: move identical methods from subclasses into the superclass.
- Pull Up Constructor Body: move shared constructor logic into a superclass constructor.
- Push Down Method: move a superclass method used by only some subclasses into those subclasses.
- Push Down Field: move a superclass field used by only some subclasses into those subclasses.
- Extract Subclass: move features used only in some instances into a new subclass.
- Extract Superclass: create a shared parent for two classes with common fields and methods.
- Extract Interface: declare the subset of methods that several clients rely on as an interface.
- Collapse Hierarchy: merge a subclass and superclass that are no longer different enough to keep apart.
- Form Template Method: move a shared algorithm skeleton up and leave only the differing steps in subclasses.
- Replace Inheritance with Delegation: hold the former superclass in a field and forward the calls you need.
- Replace Delegation with Inheritance: subclass the delegate when a class forwards nearly its whole interface.
How to choose within this category
- Siblings duplicate members: pull them up with Pull Up Field, Pull Up Method and Pull Up Constructor Body. If the methods are similar but not identical, use Form Template Method, or pass the differing step as a function.
- The base class holds things only some subclasses use: Push Down Method and Push Down Field.
- Two unrelated classes share behavior: consider Extract Superclass only if one really is a kind of the other. Otherwise extract a shared helper class and delegate to it. If clients need only a shared role, Extract Interface. In Go, TypeScript and Python (with
Protocol), structural typing means the interface can live with the client, and the classes don't need to declare it. - Some instances use features others don't: Extract Subclass if the variation is fixed at creation. If it changes at runtime, use Replace Type Code with State/Strategy instead.
- A subclass adds nothing meaningful: Collapse Hierarchy.
- A subclass uses a small part of its parent or overrides methods to throw: Replace Inheritance with Delegation. This is the usual fix for Refused Bequest.
- A class forwards almost every method of a delegate it truly is a kind of: Replace Delegation with Inheritance. Check that the full inherited contract is acceptable first; when in doubt, keep delegation.
Go has no inheritance, so only the interface and delegation techniques apply there. Moving members along a hierarchy can change which override runs, so run the tests for every subclass after each move.
See also
- Moving Features between ObjectsTechniques that relocate methods, fields and whole classes so each responsibility lives next to the data it uses.
- Organizing DataTechniques that turn raw fields, magic values, arrays and type codes into objects and types that carry meaning and protect their own invariants.
- Refused BequestA subclass inherits methods or data it does not want, overriding them to throw or do nothing, so the hierarchy lies about what it can do.
- Duplicate CodeThe same logic appears in two or more places, so every fix and change must be made several times and the copies drift apart.
- Parallel Inheritance HierarchiesEvery new subclass in one hierarchy forces a matching subclass in another, so two trees must always grow in lockstep.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.