Mastering Design Patterns in C# and ASP.NET Core
Part 4 – Behavioral Design Patterns
Series: Design Patterns in C# and ASP.NET Core
Category: Behavioral Design Patterns
Level: Intermediate to Advanced
Prerequisites: C#, OOP, SOLID Principles, Interfaces, Dependency Injection, ASP.NET Core
Introduction
We have completed the Creational Design Patterns and Structural Design Patterns sections.
Now we move to the third major category of the Gang of Four (GoF) Design Patterns:
Behavioral Design Patterns
Behavioral Design Patterns focus on how objects communicate with each other, how responsibilities are distributed, and how algorithms or workflows can change without heavily modifying existing code.
While:
Creational patterns focus on how objects are created
Structural patterns focus on how objects and classes are composed
Behavioral patterns focus on how objects communicate and behave
A simple way to remember them:
CREATIONAL
↓
How do I create objects?
STRUCTURAL
↓
How do I combine objects?
BEHAVIORAL
↓
How do objects communicate?
Why Do We Need Behavioral Design Patterns?
Consider a large enterprise application.
A request might travel through:
Controller
↓
Authentication
↓
Authorization
↓
Validation
↓
Business Logic
↓
Payment
↓
Notification
↓
Audit
If all of this logic is placed into one class, the application becomes difficult to maintain.
For example:
public void ProcessOrder()
{
// Authentication
// Authorization
// Validation
// Payment
// Inventory
// Notification
// Logging
// Audit
}
This creates several problems:
Large classes
Tight coupling
Difficult testing
Difficult maintenance
Hard-to-change business rules
Repeated code
Difficult extension
Behavioral patterns help us distribute these responsibilities more effectively.
Behavioral Patterns We Will Cover
Our series will cover the following 11 behavioral patterns:
| Part | Pattern | Main Purpose |
|---|---|---|
| 4.1 | Chain of Responsibility | Pass a request through a chain of handlers |
| 4.2 | Command | Encapsulate a request as an object |
| 4.3 | Interpreter | Define and evaluate a language or expression |
| 4.4 | Iterator | Traverse a collection without exposing its internal structure |
| 4.5 | Mediator | Centralize communication between objects |
| 4.6 | Memento | Capture and restore an object's state |
| 4.7 | Observer | Notify dependent objects when state changes |
| 4.8 | State | Change behavior when an object's state changes |
| 4.9 | Strategy | Switch algorithms or behaviors dynamically |
| 4.10 | Template Method | Define an algorithm skeleton while allowing steps to vary |
| 4.11 | Visitor | Add operations to object structures without modifying their classes |
4.1 – Chain of Responsibility
Purpose
The Chain of Responsibility Pattern allows a request to pass through a chain of handlers until one of them handles it.
Example
An ASP.NET Core request pipeline is conceptually similar to this idea:
Request
↓
Logging
↓
Authentication
↓
Authorization
↓
Validation
↓
Controller
Each component can process the request or pass it to the next component.
We'll build examples such as:
Approval workflows
Validation pipelines
Support ticket escalation
Authorization
Middleware-like processing
4.2 – Command
Purpose
The Command Pattern encapsulates a request as an object.
Instead of:
service.CreateOrder();
we can represent the operation as:
CreateOrderCommand
This is particularly useful in:
CQRS
Undo/Redo
Queues
Background jobs
Transaction processing
Event-driven applications
Example:
Controller
↓
Command
↓
Command Handler
↓
Business Logic
4.3 – Interpreter
Purpose
The Interpreter Pattern defines a representation for a language or grammar and provides an interpreter for that language.
For example:
age > 18
or:
salary > 50000 AND experience > 5
The application can interpret these expressions dynamically.
Potential applications include:
Rule engines
Search expressions
Filtering
Query languages
Business rules
Expression evaluation
4.4 – Iterator
Purpose
The Iterator Pattern provides a standard way to traverse a collection without exposing its internal representation.
C# already provides excellent support for iterator concepts through:
IEnumerable<T>
IEnumerator<T>
yield return
For example:
foreach (var customer in customers)
{
Console.WriteLine(customer.Name);
}
The client doesn't need to know whether the data is stored in:
Array
List
Tree
Database result
Custom collection
We'll explore how this pattern works internally.
4.5 – Mediator
Purpose
The Mediator Pattern centralizes communication between objects.
Instead of:
A → B
A → C
A → D
B → C
B → D
C → D
we introduce a mediator:
A
|
|
B ---- Mediator ---- C
|
|
D
This reduces direct dependencies between components.
A very important real-world example is:
ASP.NET Core Controller
↓
Mediator
↓
Command Handler
↓
Service
This pattern is especially relevant when discussing MediatR-style architectures and CQRS.
4.6 – Memento
Purpose
The Memento Pattern captures an object's state so it can be restored later.
The classic example is:
Document
↓
Edit
↓
Save State
↓
Edit Again
↓
Undo
↓
Restore Previous State
Real-world applications include:
Undo/Redo
Draft saving
Transaction rollback concepts
Game checkpoints
Editor history
4.7 – Observer
Purpose
The Observer Pattern establishes a one-to-many relationship where multiple objects are notified when another object's state changes.
Example:
Order Created
↓
Subject
|
+------------+------------+
↓ ↓ ↓
Email SMS Audit
Service Service Service
This is highly relevant to:
Event-driven systems
Notifications
Domain events
UI updates
Messaging systems
C# provides related mechanisms through:
event
Action<T>
IObservable<T>
IObserver<T>
4.8 – State
Purpose
The State Pattern allows an object to change its behavior when its internal state changes.
For example, an order could have:
Pending
↓
Confirmed
↓
Shipped
↓
Delivered
Different operations may be allowed depending on the current state.
Instead of:
if (status == "Pending")
{
...
}
else if (status == "Confirmed")
{
...
}
else if (status == "Shipped")
{
...
}
we can represent each state using a separate class.
4.9 – Strategy
Purpose
The Strategy Pattern allows us to define multiple algorithms and select the appropriate algorithm at runtime.
For example, an e-commerce application may support:
Payment
├── Credit Card
├── Debit Card
├── Bank Transfer
└── Digital Wallet
Instead of:
if (paymentType == "Card")
{
// Card
}
else if (paymentType == "Bank")
{
// Bank
}
else if (paymentType == "Wallet")
{
// Wallet
}
we can use:
IPaymentStrategy
↑
|
+------+-------+---------+
| | |
Card Bank Wallet
Strategy Strategy Strategy
This is one of the most useful patterns in enterprise C# development.
4.10 – Template Method
Purpose
The Template Method Pattern defines the skeleton of an algorithm while allowing subclasses to customize certain steps.
For example:
Process Application
↓
Validate
↓
Load Data
↓
Process
↓
Save
↓
Notify
The overall algorithm stays the same, while individual steps can differ.
This pattern is useful when multiple workflows follow the same general process but have different implementations for specific steps.
4.11 – Visitor
Purpose
The Visitor Pattern allows us to add new operations to an object structure without modifying the classes that make up the structure.
It is particularly useful for complex object structures.
For example:
Document
├── Paragraph
├── Image
├── Table
└── Chart
We might want to perform different operations:
PDF Export
Word Export
Validation
Statistics
Security Scan
Rather than adding all these operations directly into every document class, the Visitor Pattern separates them.
Behavioral Patterns – Enterprise Examples
Let's look at where these patterns could appear in a modern .NET enterprise application.
ASP.NET Core API
|
↓
Chain of Responsibility
|
↓
Mediator
|
+------------+------------+
↓ ↓
Command Query
↓ ↓
Handler Handler
|
↓
Strategy
|
+--------+--------+
↓ ↓
Payment Notification
Strategy Strategy
|
↓
State
|
Order Lifecycle
This illustrates how multiple behavioral patterns can work together.
Behavioral Patterns and SOLID Principles
Behavioral patterns often complement SOLID principles.
| SOLID Principle | Related Behavioral Patterns |
|---|---|
| Single Responsibility | Command, Strategy |
| Open/Closed | Strategy, State, Visitor |
| Liskov Substitution | Strategy, State |
| Interface Segregation | Command, Strategy |
| Dependency Inversion | Mediator, Strategy, Observer |
These patterns aren't replacements for SOLID.
Instead:
SOLID provides design principles, while Design Patterns provide proven approaches for applying those principles to recurring problems.
Behavioral Patterns in Modern .NET
Modern .NET provides many built-in mechanisms that resemble or implement ideas from behavioral patterns.
Examples include:
Middleware
Conceptually related to:
Chain of Responsibility
Request
↓
Middleware
↓
Middleware
↓
Middleware
↓
Endpoint
Delegates and Events
Related to:
Observer
public event EventHandler? OrderCreated;
IEnumerable
Related to:
Iterator
IEnumerable<Customer>
Dependency Injection
Can help implement:
Strategy
services.AddScoped<IPaymentStrategy, CreditCardStrategy>();
CQRS
Frequently uses ideas related to:
Command + Mediator
Command
↓
Mediator
↓
Handler
Behavioral Design Patterns – Learning Approach
For every pattern in Part 4, we will follow the same structure.
1. Definition
We'll understand exactly what the pattern is and the problem it solves.
2. Problem Statement
We'll first understand the problem before introducing the pattern.
3. Why Do We Need It?
We'll identify the design problems that make the pattern useful.
4. UML Class Diagram
We'll visualize the relationships between:
Interfaces
Classes
Handlers
Clients
Contexts
5. Complete C# Console Application
Every pattern will include a runnable example.
6. ASP.NET Core Implementation
We'll then transform the concept into an enterprise-style ASP.NET Core example.
7. Real-World Example
We'll use practical examples such as:
Banking
E-commerce
Payment processing
Order management
Notifications
Authentication
Microservices
8. Advantages
We'll discuss when the pattern provides value.
9. Disadvantages
We'll also discuss when the pattern should not be used.
10. Best Practices
We'll cover modern C# and ASP.NET Core recommendations.
11. Common Mistakes
We'll identify common implementation problems.
12. Interview Questions
Each article will finish with interview questions ranging from beginner to architect level.
Behavioral Design Patterns – Roadmap
PART 4
│
├── 4.1 Chain of Responsibility
│
├── 4.2 Command
│
├── 4.3 Interpreter
│
├── 4.4 Iterator
│
├── 4.5 Mediator
│
├── 4.6 Memento
│
├── 4.7 Observer
│
├── 4.8 State
│
├── 4.9 Strategy
│
├── 4.10 Template Method
│
└── 4.11 Visitor
Complete Design Patterns Series Roadmap
At this stage, our complete series looks like this:
Design Patterns in C# and ASP.NET Core
│
├── Part 1 – Introduction
│
├── Part 2 – Creational Patterns
│ ├── Singleton
│ ├── Factory Method
│ ├── Abstract Factory
│ ├── Builder
│ └── Prototype
│
├── Part 3 – Structural Patterns
│ ├── Adapter
│ ├── Bridge
│ ├── Composite
│ ├── Decorator
│ ├── Facade
│ ├── Flyweight
│ └── Proxy
│
└── Part 4 – Behavioral Patterns
├── Chain of Responsibility
├── Command
├── Interpreter
├── Iterator
├── Mediator
├── Memento
├── Observer
├── State
├── Strategy
├── Template Method
└── Visitor
Final Thoughts
Behavioral Design Patterns are particularly important when developing large-scale .NET applications, because enterprise systems frequently contain complex workflows, business rules, communication between services, event handling, and changing algorithms.
The most important thing is not to memorize all 11 patterns.
Instead, learn to recognize the problem:
"What kind of communication or responsibility problem am I trying to solve?"
Then select the appropriate pattern.
For example:
Request needs multiple handlers?
↓
Chain of Responsibility
Operation should be represented as an object?
↓
Command
Objects need centralized communication?
↓
Mediator
Objects need notifications?
↓
Observer
Behavior changes according to state?
↓
State
Algorithm needs to be interchangeable?
↓
Strategy
Same workflow, customizable steps?
↓
Template Method
Need to add operations without modifying object structure?
↓
Visitor
This problem-first approach is much more valuable in real-world development and .NET architecture interviews than simply memorizing pattern definitions.
🚀 Next Article
Part 4.1 – Chain of Responsibility Design Pattern
In the next article, we'll start with the Chain of Responsibility Design Pattern, covering:
What is the Chain of Responsibility Pattern?
Why do we need it?
Request and Handler concepts
Handler chains
UML Class Diagram
Complete C# Console Application
ASP.NET Core Middleware example
Banking approval workflow
Exception handling pipeline
Validation pipeline
Authentication and authorization pipeline
Real-world enterprise examples
Advantages and disadvantages
Best practices
Common mistakes
Chain of Responsibility vs Middleware
Chain of Responsibility vs Decorator
Interview questions
The next article will be particularly useful for understanding how ASP.NET Core Middleware works internally and how request-processing pipelines can be designed using the Chain of Responsibility concept.

No comments:
Post a Comment