Concepts / Designing Class Hierarchies

Designing Class Hierarchies

A parent-class change is shared by all subtypes because subtypes inherit the parent's attributes and methods. This makes shared functionality easier to add and maintain.

  • Programming

One Collection, Many Roles

Imagine counting everyone in a school building. You could write separate logic for teachers, students, and administrators. Polymorphism offers a different design: treat each person as a SchoolMember while allowing the object to retain the behavior of its specific subtype. The shared code does not need to check whether it received a Teacher or a Student.

SchoolMemberparent-type referenceTeachersubtype objectTeacher.tell()subtype behaviorStudentsubtype objectStudent.tell()subtype behaviorcan refer tocan refer toselectsselects
How can objects from different subtypes be treated as the same parent type while still using each subtype's behavior?
Definition

Polymorphism allows a subtype to be treated as an instance of its parent type. A Teacher or Student can therefore be handled as a SchoolMember while still using the behavior belonging to the actual subtype.

Runtime Method Selection

The parent-type view of an object does not erase the object's subtype. When a method such as tell() is called, dynamic dispatch examines the object's actual runtime type and chooses the matching method. If a SchoolMember reference holds a Teacher object, the Teacher version of tell() runs. If that reference holds a Student object, the Student version runs. The choice is based on the real object, not merely on the parent type used to refer to it.

What do you think happens?

A variable is treated as a SchoolMember, but the object it refers to is a Teacher. Which version of tell() executes?

  • Only the generic SchoolMember version

  • The Teacher version

  • No version, because the reference is not a Teacher

Reveal answer
Answer:

The Teacher version

Dynamic dispatch uses the actual runtime type of the object. The parent-type reference allows the object to be handled as a SchoolMember, but the Teacher object's behavior remains available.

SchoolMemberobjectsTeacher and Student objectscall tell()same operationTeacher.tell()Teacher behaviorStudent.tell()Student behavioreach objectactual type is Teacheractual type is Student
How does one piece of code process different subtype objects without checking their specific types?

Parent Features Everywhere

Inheritance creates a downward flow of functionality. When a new attribute or method is added to SchoolMember, every subtype inherits it automatically. For example, adding an ID card field to SchoolMember makes that field available to both Teacher and Student without changing either subtype.

SchoolMemberID card field addedTeacherinherits ID cardStudentinherits ID cardinheritsinherits
What happens to each subtype when a new capability is added to the parent class?
Key point

A parent-class change is shared by all subtypes because subtypes inherit the parent's attributes and methods. This makes shared functionality easier to add and maintain.

Contained Subtype Changes

Inheritance also provides isolation in the opposite direction. If a method is added only to Teacher, that method does not appear in Student and does not alter SchoolMember. Changing one subtype changes the behavior of that subtype without accidentally changing its siblings or its parent.

SchoolMemberno new Teacher-only methodTeacherexisting behaviorSchoolMemberunchangedTeachernew methodStudentexisting behaviorStudentunchangedparent ofparent ofparent ofparent of
What changes when functionality is added to one subtype, and what remains unchanged in the parent and sibling subtype?

A Flexible Design Boundary

Polymorphism lets one operation work with multiple subtypes through their shared parent type. Instead of writing separate functions for Teacher and Student, code can work with SchoolMember objects and call the shared operation. Dynamic dispatch supplies the appropriate subtype behavior at runtime.

This design supports the Open-Closed Principle. Existing code can remain closed for modification while the class hierarchy stays open for extension: a new subtype can be added, and existing code that works with the parent type can continue to work without being rewritten for every subtype.

Design actionEffect
Add functionality to SchoolMemberThe functionality becomes available to Teacher and Student
Add functionality only to TeacherTeacher changes; Student and SchoolMember remain unaffected
Process objects as SchoolMember instancesThe same code can work with multiple subtypes
Call tell() through a parent-type referenceDynamic dispatch selects the actual subtype's version

How inheritance and polymorphism distribute behavior

Mistakes in Hierarchy Design

  • Common mistake

    Assuming the parent-type reference determines the method that runs

    What it looks like

    Treating a Teacher object referenced as SchoolMember as if only the generic SchoolMember behavior could execute

    Why it fails

    Dynamic dispatch selects the method using the object's actual runtime type.

    Do this instead

    Determine which subtype object is actually being held; its matching method is selected at runtime.

  • Common mistake

    Expecting a subtype-only change to spread to sibling subtypes

    What it looks like

    Adding a method to Teacher and expecting Student to receive it

    Why it fails

    Changes made within a subtype remain isolated from its siblings and parent.

    Do this instead

    Add shared functionality to SchoolMember when all subtypes should receive it.

  • Common mistake

    Writing separate logic for every known subtype

    What it looks like

    Counting teachers and students separately when both can be treated as SchoolMember objects

    Why it fails

    This gives up the flexibility of polymorphism and makes the shared operation depend on subtype checks.

    Do this instead

    Use the shared parent type when the operation applies uniformly to multiple subtypes.

  • Common mistake

    Forgetting that inherited functionality is available automatically

    What it looks like

    Modifying both Teacher and Student after adding an ID card field to SchoolMember

    Why it fails

    Subtypes inherit the parent's attributes and methods.

    Do this instead

    Place shared functionality in the parent and allow inheritance to propagate it.

Practice the Reasoning

MEDIUM

A SchoolMember hierarchy contains Teacher and Student. First, predict the result of these changes: an ID card field is added to SchoolMember; a new method is added only to Teacher; and tell() is called on a SchoolMember reference that holds a Student object. State which objects receive each change and which tell() implementation executes.

Hints
  • Changes in the parent class propagate downward to its subtypes.
  • Changes in one subtype do not propagate sideways to sibling subtypes.
  • Dynamic dispatch uses the actual runtime type of the object.
Worked example

Tracing Three Hierarchy Changes

Determine the effect of three operations in a SchoolMember hierarchy: add an ID card field to SchoolMember, add a method only to Teacher, and call tell() through a SchoolMember reference holding a Student object.

  1. 1
    Parent change

    The ID card field is added to SchoolMember, so both Teacher and Student inherit access to it.

  2. 2
    Subtype change

    The new method is added only to Teacher, so it remains isolated to Teacher and does not alter Student or SchoolMember.

  3. 3
    Runtime call

    The reference is parent-typed, but the actual object is Student. Dynamic dispatch therefore selects Student's version of tell().

Result

The ID card field is available to Teacher and Student; the new method exists only in Teacher; and Student.tell() executes for the Student object.

Key Takeaways

  1. 1
    Polymorphism allows a subtype such as Teacher or Student to be treated as its parent type, SchoolMember.
  2. 2
    Dynamic dispatch chooses the correct subtype method at runtime based on the actual object type.
  3. 3
    Functionality added to the parent class automatically becomes available to its subtypes.
  4. 4
    Functionality added only to one subtype remains isolated from the parent and sibling subtypes.
  5. 5
    Polymorphism supports flexible code that can be extended with new subtypes without modifying existing shared logic.

At a glance

  • Polymorphism lets code treat different subtype objects uniformly through a shared parent type.
  • Dynamic dispatch preserves subtype-specific behavior when a method is called.
  • Parent-class functionality propagates to all subtypes through inheritance.
  • Subtype-specific changes remain isolated from the parent and sibling subtypes.
  • This combination supports extensible code under the Open-Closed Principle.

Related concepts

More concepts starting with D