Why Mediator, Visitor, and Interpreter Are the Rarest Design Patterns (And When to Avoid Them)
This article examines the three least‑used behavioral design patterns—Mediator, Visitor, and Interpreter—by explaining the problems each solves, showing core implementations with order‑page examples, highlighting their narrow applicability, common pitfalls, and why developers should often steer clear of them.
Three behavioral design patterns—Mediator, Visitor, and Interpreter—are the least commonly used among the 23 patterns because their applicability is narrow. The focus is on the problems each pattern solves, the core mechanism, and why they are rarely appropriate.
Mediator
Problem: many objects directly reference each other, forming a tangled web where a change in one object can affect many others.
Solution: introduce a single mediator object so that each component communicates only with the mediator, converting many‑to‑many relationships into a one‑to‑many star topology.
Example: an order page with CouponComponent, AddressComponent, and AmountComponent. The mediator coordinates updates when any component changes.
public class OrderPageMediator {
private CouponComponent coupon;
private AddressComponent address;
private AmountComponent amount;
public void changed(Component source) {
if (source == coupon) { amount.recalculate(); }
if (source == address) { amount.recalculate(); /* also freight */ }
// other coordination logic
}
}
public class CouponComponent {
private OrderPageMediator mediator;
public void select() {
// select coupon
mediator.changed(this);
}
}Benefits: components are fully decoupled; interaction rules are centralized; adding a new component only requires connecting it to the mediator.
Compared with Facade, which provides a one‑way simplification of subsystem calls, Mediator coordinates bidirectional peer communication.
Risk: the mediator can grow into a “God class” if it accumulates excessive coordination logic, making the class large and hard to maintain.
Appropriate when objects have complex many‑to‑many interactions that can be centrally managed, such as GUI form bindings or chatroom message routing.
Visitor
Problem: a stable data structure (the element hierarchy) needs to support frequently added operations.
Solution: extract each operation into a separate visitor class; element classes provide an accept(visitor) method that passes themselves to the visitor.
Example: an order system with element types PhysicalItem, VirtualItem, and ServiceItem. Operations such as price calculation, Excel export, and weight statistics are implemented as distinct visitors.
public interface OrderVisitor {
void visit(PhysicalItem item);
void visit(VirtualItem item);
}
public class PriceVisitor implements OrderVisitor {
public void visit(PhysicalItem item) { /* calculate physical price */ }
public void visit(VirtualItem item) { /* calculate virtual price */ }
}
public class ExportVisitor implements OrderVisitor { /* export logic */ }
public class PhysicalItem implements Item {
public void accept(OrderVisitor visitor) { visitor.visit(this); }
}Adding a new operation (e.g., a WeightVisitor) only requires creating a new visitor class; element classes remain unchanged.
Limitation: adding a new element type forces changes to every visitor interface and implementation, violating the Open/Closed Principle.
Thus Visitor is suitable when element types are virtually immutable and the set of operations is expected to grow, a situation commonly found in compiler abstract syntax tree (AST) processing.
Interpreter
Problem: a simple custom “small language” (e.g., a discount rule) must be parsed and evaluated.
Solution: define a class hierarchy that mirrors the grammar, build an abstract syntax tree (AST), and interpret the tree by recursive evaluation.
Example: the rule "amount>100 AND member" is parsed into an AndExpression node with left and right child expressions representing the amount check and the membership check.
public interface Expression {
boolean interpret(Context ctx);
}
public class AndExpression implements Expression {
private Expression left, right;
public boolean interpret(Context ctx) {
return left.interpret(ctx) && right.interpret(ctx);
}
}The structure mirrors the Composite pattern (tree + recursion).
Why rarely used: (1) narrow applicability—only needed when a bespoke mini‑language must be interpreted; (2) class explosion as the grammar grows; (3) mature alternatives such as regular expressions, scripting engines (Groovy, JavaScript), rule engines (Drools), or parser generators (ANTLR) are usually more robust.
Common Characteristics
All three patterns have narrow applicability and a clear risk of unnecessary complexity if misapplied.
Mediator solves many‑to‑many interaction but can become a God class.
Visitor separates operations from a stable element hierarchy but is hostile to adding new element types.
Interpreter provides a hand‑crafted parser for a small language but suffers from class explosion and better tooling exists.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
