A logbook for curious waters
← Software Engineering Field Guide

Remove Middle Man

Before you begin: Objects, properties, and delegation.

Remove Middle Man lets callers use a collaborator directly when an intermediate object only forwards requests. It can undo Hide Delegate when the growing collection of forwarding methods costs more than the hidden relationship is worth. 1

Original version: the team repeats its manager’s interface

A team has a manager. Screens legitimately need the manager’s contact details, but the team exposes them one at a time.

public sealed class Manager
{
    public string Name { get; }
    public string Email { get; }
    public Manager(string name, string email) { Name = name; Email = email; }
}

public sealed class Team
{
    private readonly Manager manager;
    public Team(Manager manager) { this.manager = manager; }
    public string ManagerName { get { return manager.Name; } }
    public string ManagerEmail { get { return manager.Email; } }
}
class Manager:
    def __init__(self, name, email):
        self.name = name
        self.email = email


class Team:
    def __init__(self, manager):
        self._manager = manager

    @property
    def manager_name(self):
        return self._manager.name

    @property
    def manager_email(self):
        return self._manager.email

Adding a phone number, calendar link, and department would invite more team properties that do nothing except forward reads.

Refactored version: expose the relevant collaborator

public sealed class Manager
{
    public string Name { get; }
    public string Email { get; }
    public Manager(string name, string email) { Name = name; Email = email; }
}

public sealed class Team
{
    public Manager Manager { get; }
    public Team(Manager manager) { Manager = manager; }
}

The call changes from team.ManagerEmail to team.Manager.Email. The manager remains the owner of contact information; the team stops duplicating its interface.

class Manager:
    def __init__(self, name, email):
        self.name = name
        self.email = email


class Team:
    def __init__(self, manager):
        self.manager = manager

The call changes from team.manager_email to team.manager.email. No contact value changes merely because the forwarding property disappears.

Exposing an object is a design decision

Callers now know the team-manager relationship. That dependency is acceptable in this example because contact screens already work with managers as a meaningful concept.

Do not expose a database connection, a mutable collection, or a privileged service merely to delete wrappers. In Python, the exposed manager also has writable attributes; a read-only view may be necessary when callers should only inspect it.

A forwarding method can still be valuable when it expresses a team-level operation such as “request approval,” rather than merely repeating a manager’s getter. Keep meaningful behavior at its proper level and remove only forwarding that adds no useful contract.

Sources

  1. Martin Fowler — Remove Middle Man.