A design pattern isn't code you copy — it's a name experienced developers already agree on for a shape you'll recognize the moment you see it.
Picture two developers in a code review. The first one writes: "This class is directly constructing three different concrete logger implementations based on a config value, and every caller that needs a logger duplicates that same conditional. Could we pull that decision into one place, behind an interface, so callers just ask for 'a logger' without knowing which concrete class they're getting, and centralize the construction logic so adding a fourth logger type means changing one method instead of every call site?" The second developer writes: "Use a Factory here." Both comments describe the identical piece of feedback. One took four sentences; the other took three words.
That compression is the entire point of a design pattern. It's not a library you install, and it's not a C# language feature like async/await or pattern matching. It's a named, documented shape of solution — a recurring way of arranging classes and interfaces to solve a recurring problem — that enough developers already recognize that naming it communicates the whole idea instantly, without re-deriving it from scratch in every conversation.
In this lesson, you'll learn what a design pattern actually is, meet the three classic categories that organize the entire pattern catalog — Creational, Structural, and Behavioral — see how they map onto the specific patterns covered in this Part, and learn the honest, balanced caveat every experienced developer eventually learns: patterns solve specific, recurring problems, not everything, and reaching for one where it doesn't fit is its own well-known mistake.
A design pattern is a reusable, named solution to a problem that keeps showing up, again and again, in object-oriented software — not a specific block of code you paste in, but a general shape: which classes exist, what they depend on, and how they talk to each other. When someone says "this is a Strategy" or "we need an Adapter here," they're pointing at that shape, and everyone who knows the name pictures the same structure immediately.
The term comes from a specific, famous source: Design Patterns: Elements of Reusable Object-Oriented Software (1994), written by four authors — Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides — universally nicknamed the "Gang of Four," or GoF. Their book catalogued 23 recurring solutions observed across real object-oriented systems, gave each one a name, and organized all 23 into three categories based on the kind of problem each one addresses. That vocabulary is still the industry standard three decades later — when a developer today says "GoF pattern," they mean one of these 23, or something clearly descended from them.
You can't using your way into a design pattern, and no NuGet package installs one for you. A pattern is a way of arranging code you write yourself — interfaces, classes, and the relationships between them — to solve a shape of problem that recurs across countless unrelated applications. Two completely different codebases, in different industries, with different business rules, can both contain a genuine Factory pattern — because "Factory" describes a structural shape, not a specific piece of business logic.
This is worth being explicit about, because it's easy to misunderstand: the value of a design pattern isn't that it makes your code "more correct" by existing. Plenty of perfectly good code contains no named pattern at all. The value is communication — a shared shorthand that collapses a paragraph of design reasoning into a word, once both people already know what that word means.
The GoF organized all 23 patterns by asking one question of each: what kind of problem is this pattern actually about? Every pattern answers exactly one of three questions:
Question answered: "How do objects communicate and share responsibility?" — concerned with the assignment of responsibilities between objects and the patterns of communication between them: who calls whom, who's notified of what, and how an algorithm gets selected or coordinated at runtime.
Covered in this Part: Strategy (241), Observer (244), Mediator (246).
| Category | Core question | Patterns in this Part |
|---|---|---|
| Creational | How do objects get created? | Factory (240), Builder (245) |
| Structural | How do objects fit together? | Decorator (242), Adapter (243) |
| Behavioral | How do objects communicate? | Strategy (241), Observer (244), Mediator (246) |
"So what I'm thinking is, instead of every place that needs to send a notification hard-coding which specific sender class it uses, what if we made an interface that all the senders implement, and then had one class whose whole job is picking the right one based on the user's preference, and everywhere else just depends on the interface instead of the concrete types..."
"Let's put a Factory in front of the notification senders."
Meaning: Both sentences describe the exact same design. The second one only works because both people already know what "Factory" means — that's the entire value proposition of learning the catalog. It doesn't make the underlying design any better or worse; it makes talking about the design dramatically faster.
A pull request adds payment processing that supports Stripe, PayPal, and wire transfers, with a switch statement picking the right gateway in every controller that charges a customer. A reviewer, instead of writing several paragraphs of restructuring advice, leaves one comment: "This looks like it wants a Factory — pull the switch into one PaymentGatewayFactory.Create(method), and have every controller depend on IPaymentGateway instead." The author, having studied lesson 240, immediately knows exactly what shape of change is being requested — no meeting required, no diagram needed. That's the payoff this entire Part is building toward: every pattern lesson from here on is depositing one more entry into a vocabulary you and your teammates will use to move faster together.
A guitarist could say "play G, then B, then D, then F, all at once, in this specific voicing" — or they could just say "play a G7." Both instructions produce the identical sound. The chord name doesn't create anything new; the notes existed and could be combined that way long before anyone named the combination. What the name buys you is speed of communication between musicians who already share the vocabulary — and the ability to recognize "oh, that's a G7" the instant you hear it, rather than puzzling out the individual notes from scratch every time.
Design patterns work exactly the same way. "Factory," "Adapter," "Observer" don't create new capabilities C# didn't already have — interfaces, constructors, and delegates already existed. The names just let developers who share the vocabulary recognize and discuss a familiar shape instantly, the way musicians recognize a chord by name instead of by note-by-note description.
An algorithm (like binary search, or quicksort) is a specific, step-by-step procedure for computing a result. A design pattern is not a procedure at all — it's a structural arrangement of classes and interfaces describing who depends on whom and who talks to whom. You could implement the exact same algorithm inside a Strategy pattern, a Factory Method, or plain unstructured code — the pattern is about the surrounding shape, not the computation inside it.
Patterns operate at the scale of a handful of classes solving one recurring, local problem. Architecture — covered later in this Part with Clean Architecture (249) and Hexagonal Architecture (250) — operates at the scale of an entire application's structure: which layers exist, and which direction dependencies are allowed to flow between them. A single architecture can, and usually does, contain dozens of individual design patterns inside it; they're related ideas at very different zoom levels, not the same thing.
A Factory wrapped around a single implementation that will never have a second one isn't better code for being "a real GoF pattern" — it's unnecessary indirection wearing a familiar name. The pattern's presence isn't the goal; solving the actual recurring problem cleanly is the goal, and sometimes a pattern is the cleanest way to do that, and sometimes it very much isn't. The rest of this section addresses that directly.
Wrapping a single, simple, unlikely-to-change piece of logic in a full Strategy interface with one concrete implementation, or building an Observer/event setup for a notification that will only ever have exactly one, fixed listener — reaching for a pattern's ceremony because it's a pattern, not because the actual problem calls for it.
Ask first: is this recurring problem actually present — multiple interchangeable implementations, a genuine need to decouple creation from use, several unrelated listeners? If the answer is no, plain, direct code is the correct choice, and it's not a lesser choice for being pattern-free.
Deciding a new feature "needs" a Factory, a Strategy, and an Observer because those are the patterns from the most recent lesson, rather than because the feature's actual shape calls for any of them.
Learn the catalog so you can recognize a shape when it genuinely appears — not so you can manufacture opportunities to use each name at least once in every project.
Saying "it's a Mediator" in a code review and stopping there, when the listener doesn't actually know the pattern well enough to picture the resulting structure — the name only compresses communication between two people who both already share the vocabulary.
Use the name as shorthand once you know it lands — and be ready to unpack it back into plain language for anyone still learning the catalog, exactly the way this Part is doing for you right now.
And when it's overkill: almost always, at the moment you're tempted to add a pattern "for flexibility" without a concrete, present need driving it. Every pattern in this catalog adds a layer of indirection — an interface, an extra class, an extra level of delegation — and that cost is only worth paying when it's actually buying you something: real decoupling from a real, current source of change. "Pattern-itis" is a real, named anti-pattern precisely because unnecessary indirection has a genuine cost — code that's harder to read, harder to step through in a debugger, and harder for a new developer to follow — for no corresponding benefit.
You've met the GoF categories that will organize every remaining lesson in this Part. Let's confirm you can place a pattern correctly, and recognize when reaching for one is a mistake.
1. What is the most accurate description of a design pattern?
Correct: B
Why B is correct: A design pattern is a named, documented structural shape — an arrangement of classes and interfaces — that recurs across unrelated codebases, built entirely from ordinary object-oriented tools already available in the language.
Why A is incorrect: A pattern isn't a fixed block of code to paste in — it's a shape you adapt to your own specific classes and problem.
Why C is incorrect: Patterns predate and are independent of any specific language's keywords — they're implemented using ordinary features a language already provides, not new syntax.
Why D is incorrect: No package installs a design pattern — patterns are a way of arranging code you write yourself.
Reinforcement: A pattern is a reusable shape and a shared name for it, not a piece of installable or copy-pasteable code.
2. Which GoF category does the Observer pattern (covered in lesson 244) belong to, and why?
Correct: C
Why C is correct: Observer defines a one-to-many notification relationship between objects — precisely the "how do objects communicate" question that defines the Behavioral category.
Why A is incorrect: Observer doesn't concern itself with how Observer objects get constructed — that would be a Creational pattern's job. Observer is about notifying already-existing objects.
Why B is incorrect: Structural patterns are about composing objects into larger structures, not about the flow of notifications between independent objects — Observer's Subject and Observers aren't composed into a rigid structure at all.
Why D is incorrect: Every one of the 23 GoF patterns, Observer included, was explicitly assigned to one of the three categories in the original catalog.
Reinforcement: Ask "what kind of problem does this pattern solve?" — creation, composition, or communication — to place any pattern in its correct category.
3. A developer wraps a single, simple validation check — one that will never have a second implementation — inside a full Strategy interface with one concrete class, "because Strategy is a design pattern and design patterns are good practice." What does this lesson call this?
Correct: B
Why B is correct: This is the exact scenario the lesson names "pattern-itis" — applying a pattern's structure because it's a recognized pattern, not because the concrete, recurring problem the pattern solves is actually present.
Why A is incorrect: SOLID principles guide toward flexibility where change is expected — they don't call for indirection around code that will never vary.
Why C is incorrect: Plain, direct code is just as testable as code wrapped in an unnecessary interface — testability isn't the justification here.
Why D is incorrect: The lesson explicitly warns against "just in case" pattern usage — future-proofing without a concrete present need is precisely the mistake being described, not a best practice.
Reinforcement: A pattern earns its place when its specific problem is genuinely present — not by default, and not as insurance against a hypothetical future.
4. Two developers, both familiar with the GoF catalog, are discussing a design. One says "let's put a Factory in front of this." Why does that short sentence work as effective communication?
Correct: B
Why B is correct: The entire value of pattern vocabulary is that it lets people who share the same mental model communicate instantly — the name substitutes for a longer structural explanation precisely because both parties already know what that structure looks like.
Why A is incorrect: "Factory" isn't a language keyword at all — it's a name from a design catalog, unrelated to C# syntax.
Why C is incorrect: Naming a pattern doesn't write any code — a developer still has to design and implement the actual classes and interfaces.
Why D is incorrect: Word count has nothing to do with why the communication works — it's about a shared mental model, not brevity for its own sake.
Reinforcement: Pattern names function as shorthand only between people who already share the underlying vocabulary — that's the entire reason the catalog exists.
5. Which statement most accurately distinguishes a design pattern from an architecture like Clean Architecture (249)?
Correct: A
Why A is correct: Patterns and architectures operate at very different zoom levels — a pattern solves one recurring, local design problem among a few classes, while an architecture like Clean Architecture governs the whole application's layer structure, and commonly contains many individual GoF patterns working inside it.
Why B is incorrect: The lesson explicitly distinguishes the two by scale — treating them as identical loses that important distinction.
Why C is incorrect: The Creational/Structural/Behavioral categories apply specifically to GoF design patterns, not to architectural styles, which aren't classified that way at all.
Why D is incorrect: Neither patterns nor architectures require any specific project structure by definition — that's an implementation detail, not the distinguishing factor between the two ideas.
Reinforcement: Zoom level is the key distinction: patterns are local and small-scale; architecture is global and whole-application-scale.
You now have the map for the rest of this Part's pattern catalog — Creational, Structural, Behavioral — and the vocabulary to recognize each pattern the moment it shows up in real code. Next up: Factory, the first Creational pattern.
dotnetmadeeasy.com — Learn C# and .NET, the right way.