Home Design Patterns Delegation

Delegation

Modern Practice

Forward work to a held object rather than inheriting the ability to do it.

Purpose

An object is asked to do something and, instead of doing it, passes the request to another object it holds and returns whatever comes back. The caller sees no difference: the operation is still part of the receiver's public contract, and the helper doing the actual work is a private detail that can be swapped, wrapped or replaced while the program is running.

That is deliberately close to "composition", and it is fair to ask what the extra name buys. It buys the distinction between holding a collaborator and being answerable for what it does. A class that holds a Database and exposes save() is composing. A class that promises area() in its own interface and implements it as return shape.area(); is delegating: the responsibility is the receiver's, the work is somebody else's, and the client is never told. Inheritance is the other way to arrange that, which is why delegation is best understood as the runtime alternative to subclassing rather than as a kind of composition.

Design

The delegator holds a field typed as an interface, and each delegated method is one line that forwards. Because the field is an interface and is assigned rather than declared by a class header, the choice of helper is made at run time and can be changed at run time, which is precisely what inheritance cannot do.

UML class diagram: Client calls Delegator, which holds a Delegate interface and forwards the request to a ConcreteDelegate that implements it.
The client's arrow stops at the Delegator. Everything to the right of that box is invisible from outside, which is what separates this from simply handing the client the helper.

There is one detail that makes delegation more than forwarding, and it is the one the Gang of Four picked out in their introduction: the delegator often passes this to the delegate, so the delegated operation can refer back to the object it is acting on behalf of. That is what recovers the thing inheritance gives you for free, where a method in the base class can call back into the subclass. It also lets one stateless helper serve many delegators, since everything it needs about the caller arrives as an argument.

Example

Autobot Robotics needed the Arm802 controller to enforce the same motion limits as the Arm601: joint ranges, velocity ceilings, and a check that a commanded pose does not put the wrist through the table. The first attempt was the obvious one, making Arm802Controller a subclass of Arm601Controller. It worked, and it was wrong in a way that took a year to show up.

When the Arm1210 arrived with a seventh axis, the inherited limit checking came with assumptions baked into the parent: an array sized for six joints, a protected field the subclass was expected to keep in step, and a homeAll() that the constructor called before the subclass had finished initializing. Fixing the 1210 meant editing Arm601Controller, which meant re-testing the 601 and the 802 on real hardware to ship a change that had nothing to do with either. Inheritance had made three products into one release.

Limit checking became a LimitChecker object that each controller holds and forwards to. The controller keeps checkMove() in its own interface, so no caller changed, and passes itself to the checker so the checker can read the current pose without holding any state of its own. Each arm now gets the checker it needs, a test build gets a permissive one, and the diagnostics tool swaps in a checker that records rejections instead of refusing them, on a live controller, without a rebuild. The cost was about a dozen one-line forwarding methods, which is the honest price of the pattern and is worth it here.

// Before: reuse by subclassing, which fixes the choice at compile time
// and welds Arm802 to Arm601's internals.
class Arm802Controller : Arm601Controller
{
    // inherits checkMove(), and everything else it never wanted
}

interface LimitChecker
{
    bool allows(ArmController arm, Pose target);
}

class Arm802Controller : ArmController
{
    private LimitChecker limits;

    public Arm802Controller(LimitChecker l) { limits = l; }

    // The delegation. checkMove() stays part of this class's contract,
    // and "this" goes along so the checker can read the live pose.
    public bool checkMove(Pose target)
    {
        return limits.allows(this, target);
    }

    public void useChecker(LimitChecker l) { limits = l; }
}

class SixAxisLimits : LimitChecker
{
    public bool allows(ArmController arm, Pose target)
    {
        // joint ranges, velocity ceiling, table clearance
    }
}

// Same controller, different rules, chosen at run time.
class SevenAxisLimits : LimitChecker { }
class RecordingLimits : LimitChecker { }   // logs rejections, refuses nothing
Three lines are the pattern: the limits field, the forwarding body of checkMove, and useChecker, which is the ability inheritance cannot offer at any price.

Seen in the Wild

Kotlin has delegation in the language: class Logger(base: Writer) : Writer by base tells the compiler to generate the forwarding methods, which exists because writing them by hand is the pattern's main cost. Ruby ships Forwardable and SimpleDelegator in its standard library for the same reason, and Go's struct embedding promotes the embedded type's methods onto the outer type, which is compiler-generated forwarding with different words. Cocoa builds a large part of its API on it: a UITableView owns the drawing and hands every decision about content and selection to a separate delegate object your code supplies. In Java, FilterInputStream holds an InputStream and forwards every method to it, which is delegation being used to build a Decorator.

Comparisons

  • Strategy, State and Bridge are all delegation with a reason attached. Strategy delegates an algorithm chosen from outside, State delegates to an object that chooses its own successor, Bridge delegates across a whole implementation layer that is expected to keep growing. The mechanism is identical in all three; what differs is why the helper changes and who changes it.
  • Template Method is the inheritance-shaped answer to the same problem, and the pair is the clearest illustration of "favor composition over inheritance" available. Template Method binds the varying step at compile time; delegation binds it at run time and can rebind it later.
  • Decorator and Proxy both delegate while keeping the wrapped object's exact interface. A Decorator adds behavior around the forwarded call, a Proxy decides whether and when to make it at all, and both are delegation with a specific intent.
  • Adapter delegates while deliberately changing the interface, which is the one case where the forwarding method is not allowed to be a straight pass-through.
  • Facade delegates to several objects at once and owns the ordering between them, rather than forwarding one operation to one helper.

Why It Exists

My take

Delegation exists because inheritance is a poor tool for reuse and an excellent one for classification, and for about twenty years the industry used it for both. Subclassing to get a method is a decision made at compile time, binding you to the parent's internals, its constructor order, its protected fields and its future changes, in exchange for not typing a forwarding method. The Arm802 in the example is the standard shape of that mistake: the parent was chosen because it happened to contain code that was needed, and after that every product shared one release.

The reason it is worth naming, rather than filing under composition, is that it names the mechanism that half the catalog is built from. Bridge, Strategy, State, Decorator, Proxy and Adapter are all one object holding another and forwarding to it; they differ in what varies and why, not in how. Once you see that, those patterns stop being six things to memorize and become one thing with six intents, and, more usefully, you stop reaching for the pattern names when what you actually need is a field and a one-line method.

I will happily concede this is barely a pattern. It is a technique, and it is the technique that "favor composition over inheritance" turns into when you type it. That is exactly why it belongs on a list like this: the advice is repeated everywhere and is nearly useless without the concrete move it implies, and the concrete move is one field, one forwarding method and, when the helper needs context, this as an argument.

Criticisms

The boilerplate is real and is the reason several languages grew syntax for it. Inheritance gives you every member of the parent for one line in a class header; delegation makes you write each one, and adding a method to the interface later means visiting every delegator. For a wide interface that is a genuine tax, and it is a tax paid in the kind of code nobody reads carefully, so a forwarding method that quietly does something slightly different is easy to miss in review.

The deeper problem is the broken self reference, sometimes called object schizophrenia. Under inheritance, a call from the base class to an overridable method dispatches back to the subclass automatically. Under delegation it does not: the helper calling its own methods stays inside the helper, and there is no path back to the delegator unless you passed this and the helper calls through it. That is why delegation is not a drop-in replacement for inheritance, and why the translation between them is not mechanical. Two objects also mean two identities, so equality, hashing and any code that compares references now has to decide which object it means.

And it adds a hop that only exists at run time. A stack trace grows a frame, jump-to-definition lands on an interface, and the question "what actually happens when I call this" becomes a question about how the object was wired rather than about what the source says. That is the standard price of every indirection on this site, and it is the reason delegation should be applied where something genuinely varies, not applied everywhere on principle.