Inheritance, Composition, and Meaningful Types

Discusses how inheritance and composition should be applied in framework foundations, UI architecture, and domain modeling, using meaningful types as the key design criterion.

This note continues from Inheritance and Composition in Business Application Design , Why Modern Developers Avoid Inheritance , and Form AJAX Standardization .

Introduction

In the earlier articles, I discussed the modern tendency to avoid inheritance and the reasons that reaction became so strong.

I do not personally reject inheritance in general. At the same time, there are clearly places where inheritance should not be used, and that judgment still has to be made carefully in every design.

So the real question is practical rather than ideological. In application design, when does inheritance fit naturally, and when is composition the better structural choice?

Modern Frontend Culture

In modern development, especially on the frontend side, the common guideline “prefer composition over inheritance” is now very familiar.

That recommendation exists for good reasons. Frameworks such as React are designed around composition-oriented architecture.

Historically, React also provided class-based components through React.Component. Modern React development, however, is largely centered on function components and hooks.

Even so, inheritance still exists inside many frameworks when they structure their own internal architecture.

So the issue is not inheritance as such. The issue is where inheritance is placed, and what kind of responsibility it is being asked to represent.

Cotomy’s Position

Cotomy takes a fairly simple position on that point.

At the framework foundation level, inheritance is used positively. CotomyElement is a base boundary for DOM-oriented UI handling. CotomyForm extends that boundary for form behavior. CotomyPageController is also designed as a page-level structural base and is meant to be extended through CotomyPageController.set(class extends CotomyPageController { … }).

Those classes are not utility boxes. They exist as named structural roles inside the framework.

At least for now, I also do not see much need to push Cotomy toward something like React-style function components. CotomyElement is fundamentally a wrapper around HTMLElement, and CotomyPageController can already aggregate page-level behavior around those elements and forms. Just as importantly, Cotomy is designed with server-rendered HTML and client-side generated UI living together in the same screen model. In that kind of environment, the current inheritance-oriented structural base still feels like the better fit to me.

Inside an individual screen, however, the structure is usually assembled through composition. A page is made from forms, elements, and other local parts placed together for that screen.

That split is intentional. The framework foundation uses inheritance for stable roles. Screen content uses composition for local structure.

Windows Forms as a Useful Comparison

I already used Windows Forms briefly in the previous article, but it is worth returning to here from a slightly different angle because it shows the boundary between inheritance and composition very clearly.

A Form represents a screen. Inside that screen, controls such as Button, TextBox, Label, and grid components are placed together.

In that sense, the structure of the screen is compositional from the start. A screen is a collection of controls.

At the same time, each concrete screen is normally defined by inheriting from Form.

class CustomerForm : Form
{
}

class OrderForm : Form
{
}

This is easy to understand because the base type already has an independent meaning. Form means a screen. CustomerForm and OrderForm are more specific screens built on that role.

That kind of inheritance does not feel forced because the type relationship itself is meaningful.

A Note on Swing

For many years I used Windows Forms heavily. Later, after my personal development environment moved to Mac, I switched some client-side work to Java Swing.

At the time, NetBeans was available without cost and was quite practical as a development environment.

What mattered more to me was that, in one important structural sense, Swing and Windows Forms were not very different. A screen was still a screen, and the screen was still built from controls and other contained elements.

The event model was different. Swing is built around listener-based event handling, while Windows Forms exposes events through the Control hierarchy.

However, that difference does not change the basic structure. A screen is still composed from controls and contained UI elements.

Where Inheritance Was Actually Used

What is interesting is that, even in those desktop UI environments, I almost never used inheritance between screen classes in everyday screen design.

The screen layout itself was nearly always built through composition. Buttons, inputs, labels, tables, and small reusable pieces were arranged inside a screen. Even when two screens looked similar, they were often only superficially similar.

That is an important distinction. Similar appearance does not automatically imply a meaningful inheritance relationship.

Custom Controls Are Different

Custom controls are a different case.

When extending an existing control, inheritance usually feels natural. If the goal is to keep button behavior and extend it slightly, inheriting from Button is clearer than starting again from a lower-level control type.

class StateButton : Button
{
    private int _state = 0;

    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);
        _state = (_state + 1) % 3;
        Invalidate();
    }
}

The point here is not the exact implementation. The point is that the type is still a button. It has button behavior, button expectations, and then some additional state or rendering.

That is very different from creating an abstract base screen only because several screens happen to share a toolbar or a few fields.

Why Screens Rarely Need Inheritance

Why, then, do screens so often end up as composition rather than inheritance?

One reason is that screens are independent working surfaces. A screen is usually built as a collection of controls with its own layout, data flow, and operational context. Even if two screens share a toolbar, a set of inputs, or a rough arrangement, that often means only that they happen to resemble each other.

In other words, the commonality is local, not necessarily typological.

When that is the case, composition is usually the more honest structure. A shared toolbar can become a reusable control. A shared form section can become a reusable part. The screen itself does not need to become a subclass merely to reuse those pieces.

Meaningful Types

This is the point that matters most to me.

When designing a class, I have long tried to ask whether that class stands as a meaningful type in its own right.

Many inheritance failures happen because engineers start from shared code and then try to turn that shared code into a base class. But shared code is not the same thing as a meaningful abstraction.

If a base class exists only because several classes coincidentally share some fields, helper methods, or a fragment of layout, the inheritance line is already suspicious. The base may reduce duplication for a while, but it does not necessarily express a stable concept.

That is why I think inheritance should begin from meaning, not from convenience.

A framework base such as CotomyForm has a clear role. A desktop base such as Form has a clear role. A custom button derived from Button still has a clear role. Those types make sense before any concrete subclass is added.

Many partial screen abstractions do not satisfy that condition. They are often only bundles of convenience.

Entity Modeling Has the Same Risk

The same thing happens in entity design.

Textbook examples often use simple classification trees such as mammal, human, and dog. Those examples are easy to draw, but actual business domains often do not look like that.

In real projects, I once created a structure closer to Item and Product. Materials were handled as Item, and sales targets were represented as Product derived from it.

At first that looked reasonable. Later it became clear that the common properties were only accidentally similar. The structure was not expressing a durable type relationship. It was mixing inventory-like identity with sales-specific concerns.

A better design would have been closer to separating Item and SalesInformation, then combining them where necessary.

That experience made the same lesson visible again. Similar fields do not guarantee a meaningful base type.

No Absolute Rule

There is no absolute answer here.

Inheritance and composition are not enemies. They express different structural relationships. The problem begins when one of them is used to represent a relationship it does not actually describe.

If AI-assisted development keeps accelerating implementation speed, that design judgment becomes even more important. Code generation can produce structure quickly, but it does not automatically produce meaningful types.

The value of design is therefore not reduced by faster implementation. If anything, it becomes more visible.

Conclusion

I still do not feel much resistance to inheritance itself. Even so, when I think through domain and screen design carefully, I find that I actually do not use inheritance very often.

In practice, the question is rarely inheritance or composition by itself. The real question is whether the structure being created represents a meaningful type.

When the base type expresses a real role, such as a framework boundary, a UI control type, or another stable structural component, inheritance can be very natural.

When the commonality is only local reuse, composition is usually the more honest design.

Understanding that difference matters more than choosing sides in the inheritance versus composition debate. It may also help explain why modern technical culture moved in this direction. The valid cases for inheritance are real, but they may be fewer than older engineers of my generation once assumed.

Design Series

This article is part of the Cotomy Design Series.

Series articles: CotomyElement Boundary , Page Lifecycle Coordination , Form AJAX Standardization , Inheritance and Composition in Business Application Design , API Exception Mapping and Validation Strategy , Why Modern Developers Avoid Inheritance , and Inheritance, Composition, and Meaningful Types.

Previous article: Why Modern Developers Avoid Inheritance

Next article: Designing Meaningful Types

Learn Cotomy

Cotomy is a DOM-first UI runtime for long-lived business applications.