Design Patterns in C#: A Practical Introduction
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.
What Is It?
The Simple Explanation
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 Technical Definition
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.
Why Does It Exist? — The Problem It Solves
- The same handful of design problems — "decouple object creation from object use," "let one object notify many others without knowing who they are," "swap an algorithm at runtime" — recur constantly across unrelated codebases. Without shared vocabulary, every team re-derives its own solution, re-explains it from scratch in every code review, and often reinvents a worse version of a well-understood idea
- A shared, precise vocabulary — names that map to well-understood structural shapes — so that two developers, possibly from entirely different companies or backgrounds, can communicate a design decision in a sentence instead of a diagram-and-paragraph explanation every time
- The GoF catalog — 23 named, documented shapes, organized into three categories — became the industry's common design vocabulary. Learning the catalog isn't about memorizing code to paste; it's about recognizing the shape of a problem you already have, and reaching for its name
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.
Big Picture — the Three GoF Categories
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:
Creational
- Question answered: "How do objects get created?"
- Controls and centralizes object construction, so the rest of the code doesn't need to know which concrete class gets built
- Covered in this Part: Factory (240), Builder (245)
Structural
- Question answered: "How do objects fit together?"
- Concerned with how classes and objects are composed into larger structures, without collapsing into a rigid, fragile hierarchy
- Covered in this Part: Decorator (242), Adapter (243)
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) |
How It Works — Recognizing a Pattern, Rather Than "Applying" One
- Not "I should use a pattern today" — instead, a genuine, concrete problem shows up: "every caller of this method needs a different concrete logger depending on a config value," or "three unrelated classes all need to react when an order ships."
- "Centralize which concrete class gets constructed" is the Factory shape. "Notify many unknown listeners when state changes" is the Observer shape. Recognizing the shape is what years of exposure to the catalog actually buys you.
- Not a copy-paste template — the pattern gives you the structure (an abstraction the caller depends on, a component that owns the decision), which you then shape around your actual classes and actual business rules.
- A code comment, a class name, a PR description that says "this is a Factory" lets any future reader who knows the vocabulary understand the design's intent immediately, without re-deriving it from the code alone.
Simple Example — the Same Design Decision, With and Without a Name
Without shared vocabulary
"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..."
With shared vocabulary
"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.
Real-World Example — a Code Review, Sped Up by Vocabulary
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.
Analogy — Musical Chord Names
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.
Under the Hood — Where This Vocabulary Actually Comes From
- Every GoF pattern is built entirely from ordinary object-oriented tools you already know: interfaces, inheritance, composition, delegation. The patterns are arrangements of those tools, not new primitives layered on top of C#.
- Look closely at almost any GoF pattern in this Part and you'll find one or more SOLID principles (235) doing the actual work underneath the name — Factory and Adapter both lean on Dependency Inversion; Decorator and Strategy both lean on Open/Closed. The pattern names are a higher-altitude vocabulary sitting on top of the lower-level discipline SOLID already taught you.
- The GoF authors didn't sit down and invent 23 new techniques — they studied real, working object-oriented systems, noticed the same handful of shapes recurring independently across unrelated codebases, and gave each recurring shape a name. That's why the patterns still hold up decades later: they describe genuinely recurring structural problems, not a passing trend.
Common Confusion
"Design pattern" vs. "algorithm"
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.
"Design pattern" vs. "architecture"
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.
"Using a named pattern" doesn't automatically mean "good code"
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.
Common Mistakes
Mistake 1 — "Pattern-itis": forcing a named pattern where direct code would be clearer
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.
Mistake 2 — Treating the catalog as a checklist to work through
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.
Mistake 3 — Using pattern names as a substitute for actually explaining the design
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.
When Should I Use It?
- Reach for a named pattern when you recognize its specific recurring problem genuinely present in your code — not preemptively, "just in case" a future requirement might need it.
- Use the vocabulary constantly in code reviews, design discussions, and documentation, even when you choose not to apply the pattern's full structure — naming the shape you're deliberately avoiding is still useful communication.
- Treat every pattern lesson in this Part as teaching you to recognize a shape first, and only secondarily as teaching you to write one from a template.
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.
Mental Model
Creational = how objects get made. Structural = how objects fit together. Behavioral = how objects communicate.
Remember:
· Patterns are vocabulary, not code you paste in — they compress a paragraph of design reasoning into a word.
· Every pattern in this Part builds on tools you already know: interfaces, composition, delegation, SOLID.
· "Pattern-itis" — forcing a named pattern where direct code is clearer — is a real, well-known mistake in its own right.
Key Takeaway
- A design pattern is a reusable, named solution to a recurring design problem — a documented structural shape, not a library and not a language feature.
- The GoF catalog organizes 23 patterns into three categories: Creational (how objects get created — Factory 240, Builder 245), Structural (how objects fit together — Decorator 242, Adapter 243), and Behavioral (how objects communicate — Strategy 241, Observer 244, Mediator 246).
- The value of a pattern is shared vocabulary — instant, precise communication between developers who both already recognize the shape.
- "Pattern-itis" — forcing a named pattern where plain, direct code would be clearer — is a real anti-pattern. Reach for a pattern when its specific problem is genuinely present, never as a checklist item.
- Every remaining pattern lesson in this Part is depositing one more entry into a vocabulary you'll use with every team you ever work on.
Check Your Understanding
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?
Show answer
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?
Show answer
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?
Show answer
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?
Show answer
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)?
Show answer
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.