Poltergeist
A Poltergeist is a do-nothing class that only forwards calls or data, adding indirection without responsibility. Inline it and let meaningful objects collaborate directly, keeping intermediaries only when they truly add value.
A Poltergeist (also called a 'gypsy' or proxy ghost) is a class that appears briefly, does almost nothing, and vanishes — its only job is to invoke methods on another object or shuttle data from one place to another. It haunts the design with extra indirection while holding no meaningful state or behavior of its own.
Why It Happens
Poltergeists often come from over-applying patterns or layering: a belief that every operation needs a dedicated 'manager,' 'controller,' or 'helper' class, even when that class only forwards calls. They also appear when developers add a wrapper to feel 'decoupled' without any real decoupling, or when a class is created solely to kick off a sequence and is then discarded.
Why It Hurts
The extra hop makes control flow harder to follow — to understand what happens, you trace through a class that contributes nothing. The codebase grows with files that add cognitive load but no value. The real collaboration between meaningful objects is obscured behind a ghost. Such classes also tempt further additions ('it's already there, let me put logic in it'), gradually turning noise into a confusing middleman.
Warning Signs
- A class whose methods only call methods on another object.
- 'Manager'/'controller' classes with no state and trivial bodies.
- Objects instantiated only to invoke one method and then thrown away.
- Long call chains where some classes pass everything straight through.
Better Alternatives
Apply Inline Class and Remove Middle Man: fold the poltergeist's trivial responsibility into the object that actually does the work, and let collaborators talk to each other directly. Favor direct collaboration between meaningful objects over chains of pass-through wrappers. Reserve intermediary classes for cases that earn their keep — genuine adaptation, access control, or abstraction over multiple implementations — not mere forwarding.
How to Refactor Out of It
Find classes that only delegate and hold no state. For each, move its single responsibility into a caller or callee, then update references to bypass it and delete the class. Where a wrapper exists to start a process, replace it with a direct method call on the object that owns the process. Keep an intermediary only if it adds real value (it transforms, guards, or abstracts). After removal, confirm the control flow reads more directly and that tests still pass against the simplified structure.