Mediator & Iterator
You'll learn to
- -Use Mediator to centralize how a set of objects communicate instead of wiring them to each other directly
- -Implement a custom Iterator that traverses a collection without exposing its internal structure
Mediator and Iterator round out the behavioral patterns most likely to appear in an LLD round. Mediator reduces chaotic many-to-many object communication to a hub-and-spoke shape; Iterator provides uniform traversal over a collection without exposing how that collection is actually stored internally.
Mediator: Replacing a Web of Direct References
Without Mediator, a chat room where every User object holds direct references to every other User to send messages becomes an N-to-N web of dependencies - adding a user means updating every existing user's reference list, and every User class is coupled to every other. Mediator introduces a central ChatRoom object: each User only knows about the ChatRoom, and the ChatRoom is responsible for routing a message from one user to the others.
class ChatRoomMediator:
def __init__(self):
self._users: list["User"] = []
def register(self, user: "User") -> None:
self._users.append(user)
def send(self, sender: "User", message: str) -> None:
for user in self._users:
if user is not sender:
user.receive(f"{sender.name}: {message}")
class User:
def __init__(self, name: str, mediator: ChatRoomMediator):
self.name = name
self._mediator = mediator # only knows the mediator, not other Users
mediator.register(self)
def send(self, message: str) -> None:
self._mediator.send(self, message)
def receive(self, message: str) -> None:
print(f"{self.name} received: {message}")Every User now depends on exactly one thing (ChatRoomMediator) instead of N-1 other Users, and adding a new user means one registration call, not updating every existing participant's reference list. The trade-off worth naming out loud: the Mediator itself can become a "god object" that knows too much if it grows unchecked, so its responsibility should stay scoped to routing/coordination, not business logic.
Iterator: Traversal Without Exposing Internals
Iterator provides a way to access the elements of a collection sequentially without exposing whether that collection is backed by an array, a linked list, or a tree. Most languages provide this for free via a built-in iterator protocol, but the pattern is worth understanding explicitly because custom collections in an interview (a custom tree structure, a paginated result set fetched from an API) often need their own iterator.
class TreeNode:
def __init__(self, value, children=None):
self.value = value
self.children = children or []
class DepthFirstIterator:
def __init__(self, root: TreeNode):
self._stack = [root]
def __iter__(self): return self
def __next__(self):
if not self._stack:
raise StopIteration
node = self._stack.pop()
self._stack.extend(reversed(node.children))
return node.value
for value in DepthFirstIterator(root): # caller never sees the stack-based traversal logic
print(value)The value of a custom Iterator: the traversal strategy (depth-first here) is fully swappable - a BreadthFirstIterator with the same interface would let callers switch traversal order without changing a single line of calling code.
Interview Signal is part of Pro
See a real weak answer next to a real strong one for this exact topic.
Quiz is part of Pro
Test what you just read with a short quiz, and bank the XP.