Design Patterns
A design pattern is a named template for a recurring problem: a description of the forces, the structure, and the consequences. You adapt it, you do not import it — and above all, you pay for it. The currency is always the same: indirection. An extra interface, an extra call level, an extra hop when navigating the code. A pattern pays off only when the problem it solves genuinely exists.
A C# interview does not check your knowledge of the GoF catalogue — it checks whether you can see that price, and whether you notice that the language and the framework have already implemented half of the patterns for you. Strategy in C# is most often a Func<T> delegate, not an interface with a class per algorithm. Observer is event, built into the language along with subscription and the invocation list. The ASP.NET Core middleware pipeline is a straight Decorator over a RequestDelegate. And a hand-rolled Singleton on a static field almost always loses to a container-registered singleton: the same single instance, but substitutable in tests and disposed on shutdown. The layer-by-layer breakdown is below.
Topic map
- Patterns and the three GoF categories — what a pattern is, how creational differs from structural and behavioral, and when a pattern is not needed.
- Factory Method and Abstract Factory — one product through an overridable method versus a consistent family of products, and what a DI container gives you instead.
- Builder and the object initializer — why an object initializer is only sugar, what
requiredandinitgive you, and whereBuild()is still needed. - Singleton versus the DI container — the price of global
staticstate, thread-safe lazy initialization, andAddSingletonas the replacement. - Strategy with delegates — a single-method interface is a
Func<T>; what you lose and when to go back to an interface. - Observer and event —
eventas a multicast delegate, the synchronous invocation of subscribers, and the leak from a forgotten-=. - Decorator and middleware — the wrapper that preserves the interface, and why the ASP.NET Core pipeline is a chain of decorators.
- Adapter and Facade — compatibility versus simplification, and the one-question test that separates them.
- Mediator and MediatR — dispatch by request type,
IPipelineBehavioras a decorator, and an honest critique of the approach.
Common Mistakes and Traps
| Mistake | Consequence |
|---|---|
| Treating a pattern as ready-made code to import | The structure is copied without its context — you pay in indirection and win nothing |
Calling any static Create() an abstract factory | Abstract Factory is about the consistency of a family of products, not about hiding new |
| Expecting an object initializer to validate required fields | It is sugar over a parameterless new plus assignments — the object gets to live in an invalid state |
Hand-rolling a Singleton on a static field | Global mutable state: the dependency is invisible in the constructor and cannot be substituted in a test |
Writing double-checked locking by hand without volatile | Another thread can see a reference to a not-yet-constructed object |
| Keeping a single-method interface and a class per algorithm | Where a Func<T> is the whole strategy, you pay a type and a class for one expression |
Forgetting -= on an event | The publisher holds a reference to the subscriber — which is never collected: the classic leak |
| Assuming event handlers run asynchronously | They run synchronously on the thread that raised the event, and an exception in one aborts the rest |
Forgetting await next(context) in a middleware | The rest of the pipeline silently does not run — the client gets an empty response |
Confusing Decorator with Adapter | A decorator preserves the interface, an adapter changes it, a facade invents a new one |
Building a Facade that forwards calls to one type | That is an adapter, or just a redundant layer |
Treating MediatR as a queue or a message bus | It is an in-process dispatcher: no buffer, no retries, the call stays synchronous |
Interview relevance
Patterns come up on almost every C# interview — and almost always with a catch. The answer "a Singleton guarantees a single instance" is formally correct and still a failure: the interviewer is waiting for you to name the price and to say how it differs from AddSingleton. What is valuable here is critique, not recitation.
Typical checks:
- What a pattern is and how creational differs from structural and behavioral.
- The difference between
Factory MethodandAbstract Factory— and that the latter exists for family consistency. - Why a hand-rolled
Singletonis a liability, and what the container gives you instead. - How to express
Strategywith a delegate and what is lost by doing so. - Why an
eventis a ready-madeObserver, and where the memory leak lives. - That
Decoratorpreserves the interface,Adapterchanges it, andFacadeinvents a new one. - That the ASP.NET Core middleware pipeline is a
Decorator, and why registration order matters.
Common wrong answer: "Patterns are there to make the code correct." That opens the discussion of the price: every pattern adds a level of indirection, and without a problem that justifies it, it only makes the code worse. A strong candidate names the cases where a pattern is not needed themselves.