A logbook for curious waters
← Software Engineering Field Guide

Hide Delegate

Before you begin: Objects, properties, and delegation.

Hide Delegate adds an operation to one object that forwards a request to a collaborator. Callers use the forwarding operation instead of navigating to that collaborator themselves. 1

Original version: the caller knows how a booking stores its destination

A booking holds a route. A confirmation screen only needs the destination name, but reaches through the booking to retrieve it.

public sealed class Route
{
    public string Destination { get; }
    public Route(string destination) { Destination = destination; }
}

public sealed class Booking
{
    public Route Route { get; }
    public Booking(Route route) { Route = route; }
}
var booking = new Booking(new Route("Harbor"));
Console.WriteLine(booking.Route.Destination);
class Route:
    def __init__(self, destination):
        self.destination = destination


class Booking:
    def __init__(self, route):
        self.route = route
booking = Booking(Route("Harbor"))
print(booking.route.destination)

The screen depends on the relationship between booking and route, not just on the destination it displays.

Refactored version: ask the booking directly

public sealed class Route
{
    public string Destination { get; }
    public Route(string destination) { Destination = destination; }
}

public sealed class Booking
{
    private readonly Route route;
    public Booking(Route route) { this.route = route; }
    public string Destination { get { return route.Destination; } }
}
var booking = new Booking(new Route("Harbor"));
Console.WriteLine(booking.Destination);

Booking.Destination forwards the read. The screen no longer knows whether the name comes from a route, a saved itinerary, or another internal representation.

class Route:
    def __init__(self, destination):
        self.destination = destination


class Booking:
    def __init__(self, route):
        self._route = route

    @property
    def destination(self):
        return self._route.destination
booking = Booking(Route("Harbor"))
print(booking.destination)

The property forwards the read. _route is internal by convention; the improvement is the public API callers are asked to use.

Do not forward an entire universe

Hiding a meaningful detail can reduce callers’ dependencies. Forwarding every operation of a large collaborator can instead create a class full of tedious wrappers. In that case, exposing a focused collaborator may be clearer; that is the motivation for Remove Middle Man.

This change removes public navigation in the example. Existing consumers using the old property must be migrated, or the property retained temporarily. Also preserve whether a value is live or a snapshot: the forwarding property reads the current route value each time.

Sources

  1. Martin Fowler — Hide Delegate.