Skip to main content

Servant

Servant centralizes shared behavior in a separate object that operates on served classes through a small interface, instead of duplicating the behavior in each. It keeps served classes lightweight but requires them to expose enough state.

Type
Behavioral
When to Use
Shared Behavior Across Classes, Avoid Method Duplication, Keep Classes Lightweight

Servant is a behavioral pattern used to offer functionality to a group of classes without defining that functionality in each of them. A servant is a separate object that provides a behavior, and the classes it serves expose just enough interface for the servant to do its work. Instead of every class implementing the same operation, the operation lives once in the servant, which takes the served objects as parameters.

How It Works

The served classes implement a small serviced interface that gives the servant the access it needs, for example getters and setters for position. The servant holds the actual algorithm and accepts one or more serviced objects as arguments to its methods. To perform the behavior, a client calls the servant, passing the objects to operate on, rather than calling a method on the objects themselves. The objects remain simple and free of the shared logic.

There are two flavors: the client triggers the servant directly, or the served object forwards a request to its servant. Either way, the behavior is centralized and the served classes stay lightweight.

When to Use It

Use Servant when several unrelated or loosely related classes need the same behavior and you do not want to duplicate it or force a common base class, when adding the behavior directly to the classes would bloat them, or when the behavior is conceptually a service applied to objects rather than an intrinsic capability of them. Geometric operations like move or align across different shapes are a classic example.

Trade-offs

The served classes must expose enough of their state for the servant to operate, which can weaken encapsulation. Moving behavior out of the objects can feel less object-oriented and may surprise developers who expect the method on the object. The servant becomes a place where logic concentrates and must know the serviced interface. For behavior that truly belongs to one class, keeping it there is clearer than introducing a servant.

Related Patterns

Visitor similarly externalizes operations from the classes they act on, but uses double dispatch over a known hierarchy, whereas Servant relies on a shared lightweight interface and a simple direct call. Strategy externalizes an algorithm choice rather than shared behavior across object types. Command packages a request as an object that may delegate to a servant-like receiver. The pattern is closely related to the broader idea of helper or utility services and to extension methods in languages that support them, both of which attach behavior to types from the outside; Servant differs by being expressed through a small, explicit serviced interface rather than ad hoc static helpers.

Example

Several UI elements, a button, a label, and an image, all implement a Movable interface exposing position through getters and setters. A Mover servant provides moveBy, alignLeft, distribute, and center, each taking one or more Movable objects and computing the new coordinates. The elements stay simple and free of layout math, and the alignment logic is written once in the servant and reused across every movable type, including ones added later that simply implement Movable. The same arrangement suits a Formatter servant that pretty-prints any object implementing a small Describable interface, or a Validator servant that checks any Validatable, keeping cross-cutting behavior in one tested place while the participating classes remain lightweight and unrelated to each other.