A logbook for curious waters
← Software Engineering Field Guide

Extract Superclass

Before you begin: Classes, inheritance, constructors, and shared behavior.

Extract Superclass creates a common base class for existing classes that share a genuine contract and implementation. Common fields and methods move into that base, while specialized features remain in the original classes. 1

Original version: two asset types repeat their common identity

An editor manages image and audio assets. Both have a name and use the same browser label.

public sealed class ImageAsset
{
    private readonly string name;
    public ImageAsset(string name) { this.name = name; }
    public string Label() { return "Asset: " + name; }
}
public sealed class AudioAsset
{
    private readonly string name;
    public AudioAsset(string name) { this.name = name; }
    public string Label() { return "Asset: " + name; }
}
class ImageAsset:
    def __init__(self, name):
        self._name = name

    def label(self):
        return "Asset: " + self._name


class AudioAsset:
    def __init__(self, name):
        self._name = name

    def label(self):
        return "Asset: " + self._name

The duplicated code represents the same editor concept: a named asset that can supply a label. That shared meaning matters more than the similar syntax.

Refactored version: make the asset contract explicit

public abstract class Asset
{
    private readonly string name;
    protected Asset(string name) { this.name = name; }
    public string Label() { return "Asset: " + name; }
}
public sealed class ImageAsset : Asset
{
    public ImageAsset(string name) : base(name) { }
}
public sealed class AudioAsset : Asset
{
    public AudioAsset(string name) : base(name) { }
}
class Asset:
    def __init__(self, name):
        self._name = name

    def label(self):
        return "Asset: " + self._name


class ImageAsset(Asset):
    pass


class AudioAsset(Asset):
    pass

Both types inherit one initialization and labeling implementation. A browser can work with the common asset capability without knowing whether it is displaying an image or audio item.

Similar code does not always imply a shared parent

If two classes merely happen to format strings similarly, a helper function may be sufficient. If they share a capability but not implementation, an interface can express that contract without imposing shared storage or a base constructor.

A base class also creates coupling: changes to common initialization or behavior can affect all subclasses. Keep image-specific decoding and audio-specific playback out of the shared base unless every asset truly supports the same contract.

The example omits those specialized features to isolate the extraction itself. In a production hierarchy, verify that inherited behavior remains valid for every subtype and that existing callers retain their expected results.

Sources

  1. Martin Fowler — Extract Superclass.