Design Patterns Quick Reference (GoF)
Creational Patterns
| Pattern | Intent | Problem Solved | When to Use | When to Avoid |
|---|---|---|---|---|
| Factory Method | Let subclasses decide which class to instantiate | A base class algorithm needs an object whose type it shouldn’t fix | Subclasses already exist and creation is one of the things they vary | Nothing else varies between subclasses (inject the object or a factory delegate) |
| Abstract Factory | Create families of related objects without naming concrete classes | Objects that must be used together have to come from the same family | Cross-platform UI widgets, database providers | Only one family exists, or new product kinds are added often |
| Builder | Separate construction of a complex object from its representation | Many optional parts, cross-field validation, or a required order of steps | Immutable results, fluent configuration APIs | A few optional properties (use an object initializer) |
| Prototype | Create objects by copying a prototypical instance | Setup is expensive, or only an instance of unknown concrete type is at hand | Configured templates, objects loaded from slow sources | Construction is cheap, or the object is immutable (use with) |
| Singleton | Ensure one instance with a global point of access | Exactly one shared instance is needed | Code with no DI container | A DI container is available (register a singleton lifetime instead) |
Structural Patterns
| Pattern | Intent | Problem Solved | When to Use | When to Avoid |
|---|---|---|---|---|
| Adapter | Convert one interface into another that clients expect | An existing class does the right work behind the wrong interface | Legacy or third-party code you can’t change | You own both sides (fix one of them) |
| Bridge | Decouple an abstraction from its implementation so both can vary | A hierarchy that varies in two dimensions grows to N × M classes | Two independent dimensions of variation | Only one dimension varies |
| Composite | Treat individual objects and groups of them uniformly | Code must handle a part-whole tree without checking which is which | File systems, UI control trees, nested bundles | Flat collections |
| Decorator | Attach responsibilities to an object dynamically | Subclassing for every combination of optional behaviors | Logging, retries, caching, and stream wrappers that stack | The combination never changes at runtime |
| Facade | Give a subsystem one simpler interface | Callers must learn many services and the order to call them | One entry point for a multi-step operation | The subsystem is already simple |
| Flyweight | Share fine-grained objects to save memory | Very many near-identical objects exhaust memory | Glyphs, map tiles, particles | Few objects, or state that is mostly unique |
| Proxy | Provide a stand-in that controls access to another object | Creation, permission, location, or caching must be managed transparently | Lazy loading, authorization checks, remote calls | The hidden work would surprise callers (make it explicit) |
Behavioral Patterns
| Pattern | Intent | Problem Solved | When to Use | When to Avoid |
|---|---|---|---|---|
| Chain of Responsibility | Pass a request along a chain until a handler takes it | The sender shouldn’t choose the handler | Approval chains, request pipelines | The right handler is known up front |
| Command | Encapsulate a request as an object | Requests must be queued, logged, retried, or undone | Undo stacks, job queues, UI actions | The action is only ever called directly |
| Interpreter | Represent a small language’s grammar as classes and evaluate it | Users write rules or formulas the program must evaluate | Small rule and filter languages | Large grammars (use a parser generator) |
| Iterator | Access elements in sequence without exposing the structure | Callers need a traversal without knowing the storage | A custom traversal order over your own collection | The data is already in a list or array |
| Mediator | Encapsulate how a set of objects interact | Pairwise links between peers grow with the square of their number | Dialogs, chat rooms, peers with shared coordination rules | Two or three stable collaborators |
| Memento | Capture and restore an object’s state without exposing it | Snapshots needed while internal state stays private | Editor undo, checkpoints | Large objects with small, reversible changes (use commands) |
| Observer | Notify dependents automatically when state changes | A subject must inform listeners it doesn’t know | UI updates, domain events, price feeds | One listener that must respond (call it directly) |
| State | Let an object change behavior when its state changes | Every method switches on the current state | Lifecycles where states differ in behavior | States that only decide what comes next (use a transition table) |
| Strategy | Make a family of algorithms interchangeable | Conditionals choose between algorithms | Pricing, shipping, sorting, and comparison rules | Only one algorithm exists |
| Template Method | Fix an algorithm’s skeleton and defer steps to subclasses | Several variants share an outline and differ in steps | Importers and workflows with fixed step order | Steps vary independently (use strategies) |
| Visitor | Define new operations over elements without changing their classes | Operations over a stable hierarchy keep multiplying | Syntax and expression trees | New element types arrive often, or few operations exist (use pattern matching) |
Patterns in .NET
Types in the .NET libraries that implement a GoF pattern, useful for recognizing the pattern in code you already use.
| Pattern | .NET type | How it applies |
|---|---|---|
| Abstract Factory | DbProviderFactory |
Each provider’s factory creates a matching connection, command, and parameter |
| Builder | WebApplication.CreateBuilder(), UriBuilder, StringBuilder |
Collect parts step by step, then produce the result |
| Singleton | AddSingleton in Microsoft.Extensions.DependencyInjection |
The container keeps one instance without a static access point |
| Adapter | StreamReader |
Presents a byte Stream through the character-based TextReader interface |
| Decorator | BufferedStream, GZipStream, CryptoStream, DelegatingHandler |
Each wraps another stream or HTTP handler of the same type and adds behavior |
| Flyweight | string.Intern |
Equal strings share one instance |
| Proxy | Lazy<T>, EF Core lazy-loading proxies |
Defer creation or loading until first access |
| Chain of Responsibility | ASP.NET Core middleware | Each component handles the request and decides whether to call the next |
| Command | WPF ICommand |
Binds a control to an action it knows nothing about |
| Interpreter | System.Linq.Expressions expression trees |
Code represented as a tree of objects that can be evaluated or translated |
| Iterator | IEnumerable<T>, IEnumerator<T>, yield return |
Built into the language through foreach |
| Observer | C# events, IObservable<T> and IObserver<T> |
Subjects notify subscribers without knowing them |
| Strategy | IComparer<T>, IEqualityComparer<T> |
Collections keep their algorithm and take the comparison as an object |
| Template Method | BackgroundService |
StartAsync runs the lifecycle and calls the subclass’s ExecuteAsync |
| Visitor | ExpressionVisitor |
Walks and rewrites expression trees without changing the node classes |
EF Core’s DbContext is often cited here too. Microsoft’s documentation describes it as a combination of the Unit of Work and Repository patterns, which come from Martin Fowler’s Patterns of Enterprise Application Architecture rather than the GoF book.
Found this useful? Share it:
Share on LinkedIn