As developers, we spend a lot of time reading and writing code.
And, as you know, there’s nothing quite like the satisfaction of seeing clean, elegant code that does what it’s supposed to do.
Design patterns provide a common language that allows developers to share and reuse code and techniques, making it easier to build complex applications. This blog post covers behavioral design patterns in C#.
Behavioral design patterns are solutions to common problems that occur when designing the interaction between objects. These patterns promote more effective communication and collaboration between objects.
The behavioral design patterns are:
- Chain of Responsibility pattern
- Command pattern
- Interpreter pattern
- Iterator pattern
- Mediator pattern
- Memento pattern
- Observer pattern
- State pattern
- Strategy pattern
- Template Method pattern
- Visitor pattern
If you’re unfamiliar with behavioral design patterns, don’t worry. This article explains all of them. So without further ado, let’s get started!
What are design patterns?
A design pattern is a repeatable solution to a commonly occurring problem in software development. A design pattern isn’t a finished design that you can copy and paste directly into code. Instead, it is a description or template for solving a problem that you can use in many different situations.
Design patterns provide detailed solutions that you can apply in real-world scenarios. In addition, they are written in specific language terminology so developers can easily understand them with different experience levels. That’s why you can also use them as a communication tool when discussing design decisions and architectural issues with others.
As a result, you don’t have to reinvent the wheel when encountering a common software design problem. Instead, you can use an existing design pattern to save time and effort.
In 1994, Erich Gamma, Richard Help, Ralph Johnson, and John Vlissides, also known as the Gang of Four, compiled a list of 23 design patterns and published them in the Design Patterns: Elements of Reusable Object-Oriented Software book. The book became a bestseller, read by millions of developers worldwide, and an all-time classic.
Another book related to design patterns that is very popular is Head First Design Patterns. It explains the design patterns in a very casual and interesting way. So I would definitely recommend reading that book as well, in addition to the original Design Patterns book.
The Gang of Four divided the design patterns into three categories:
- Creational design patterns – These are patterns that deal with object creation.
- Structural design patterns – These are patterns for structuring your code to make it more efficient, scalable, and maintainable.
- Behavioral design patterns – These are patterns that deal with the communication between objects. In other words, they define how objects interact with each other.
Let’s dive deeper into behavioral design patterns.
What are behavioral design patterns?
Behavioral design patterns are a type of software design patterns that improve how objects interact with each other. You can create more robust and efficient code using a behavioral design pattern.
There are 11 different behavioral design patterns, each with its own strengths and weaknesses. Some of the most popular behavioral design patterns include the Observer Pattern, the Mediator Pattern, and the State Pattern. Each of these patterns has been proven to be effective in solving common software development problems.
Also, each pattern encourages objects to communicate in a specific way, which can help keep the system running smoothly.
In some cases, behavioral patterns can also help optimize performance by reducing the amount of work an object needs to do. Finally, when used correctly, behavioral design patterns can help to make the code more developer-friendly and easier to maintain.
Let’s dive deeper into each of the patterns.
Chain of Responsibility pattern
The Chain of Responsibility design pattern is a software design pattern in which multiple objects are chained together to form a pipeline. Each object in the chain is responsible for processing a request. If it cannot do so, it passes the request to the next object in the chain.
It allows a flexible and extensible design, as you can add new objects to the chain anytime.
The Chain of Responsibility design pattern is often used in event-driven systems, such as graphical user interfaces, where there may be multiple potential handlers for a given event. By chaining the handlers together, the system can ensure that each event is processed appropriately.
The following sample represents the structural code in C#.
public interface IHandler
{
void SetTheNextHandler(IHandler handler);
void Process(Request request);
}
public class Individual
{
public string Name { get; set; }
public int Age { get; set; }
}
abstract class BaseHandler : IHandler
{
protected IHandler _nextHandler;
public void SetTheNextHandler(IHandler handler)
{
_nextHandler = handler;
}
public abstract void Process(Request request);
}
public class Request
{
public Individual Data { get; set; }
public List<string> ValidationMessages;
public Request()
{
ValidationMessages = new List<string>();
}
}
class MaxHandlerForAge : BaseHandler
{
public override void Process(Request request)
{
if (request.Data.Age > 60)
{
request.ValidationMessages.Add("Invalid age range");
}
if (_nextHandler != null)
{
_nextHandler.Process(request);
}
}
}
class MaxHandlerForNameLength : BaseHandler
{
public override void Process(Request request)
{
if (request.Data.Name.Length > 12)
{
request.ValidationMessages.Add("Invalid name length");
}
if (_nextHandler != null)
{
_nextHandler.Process(request);
}
}
}
If you want to learn more about the Chain of Responsibility pattern, see a real-world code example, then check out this article.
Command pattern
The Command design pattern encapsulates commands, or requests, in objects. This allows the execution of these requests to be sequenced, queued, and logged. The Command pattern is useful for implementing Undo functionality. It is also useful for batch processing.
The advantage of the Command design pattern is that it separates the object that invokes the operation from the object that knows how to perform the operation. This makes it possible to queue up requests and parameters without knowing anything about the object that will execute the request.
In addition, you can use the Command design pattern to queue requests for execution later. It also makes it possible to undo an operation by calling the undo method on the command object.
Overall, the Command pattern is a versatile tool that you can use to increase the flexibility and modularity of software systems.
The following sample represents the structural code in C#.
//The interface that Encapsulates the request
public interface Command
{
public void Execute();
}
//Concrete class based on the interface
//that actually calls the method
public class ConcreteCommand : Command
{
private Receiver receiver;
public ConcreteCommand(Receiver receiver)
{
this.receiver = receiver;
}
public void Execute()
{
receiver.Action();
}
}
//Receiver who knows the operation the actual operation
public class Receiver
{
public void Action()
{
Console.Write("Calling Receiver's Action");
}
}
//The caller of the Command
public class Invoker
{
Command command;
public Invoker(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
If you want to learn more about the Command pattern, see a real-world code example, then check out this article.
Interpreter pattern
The Interpreter design pattern is a behavioral software design pattern that defines a grammatical representation of a language and provides an interpreter to deal with this grammar.
The basic idea behind this design pattern is to promote flexibility and extensibility by allowing new functionality to be added without modifying existing code. In addition, you can use this design pattern to parse and process text-based input such as configuration files or logs.
You can use the Interpreter design pattern in many different areas of software development, including compilers, database systems, and operating systems.
The following sample represents the structural code in C#.
public class Context
{
public string StringToParse { get; set; }
public Context(string stringToParse)
{
StringToParse = stringToParse;
}
}
public interface IExpression
{
void Interpret(Context context);
}
public class TerminalExpression : IExpression
{
public void Interpret(Context context)
{
Console.WriteLine("Terminal Expression " +
"Output in {0}.", context.StringToParse);
}
}
public class NonterminalExpression : IExpression
{
public IExpression Expression1 { get; set; }
public IExpression Expression2 { get; set; }
public void Interpret(Context context)
{
Console.WriteLine("Nonterminal Expression " +
"Output in {0}.", context.StringToParse);
Expression1.Interpret(context);
Expression2.Interpret(context);
}
}
If you want to learn more about the Interpreter pattern, see a real-world code example, then check out this article.
Iterator pattern
The Iterator design pattern allows a sequential data structure traversal without exposing its underlying implementation. This is accomplished by creating an iterator object that encapsulates the data structure and provides a standardized way to access its elements.
The iterator object typically exposes methods such as Next() and HasNext(). These methods allow for the traversal of the data structure in a predictable fashion. In addition, the Iterator design pattern can contain a custom implementation allowing you to iterate over data structures such as arrays, lists, and maps.
When implemented correctly, the Iterator design pattern can provide an efficient and convenient way to access the elements of a data structure.
The following sample represents the structural code in C#.
public interface Iterator
{
string FirstItem();
string NextItem();
bool Done();
string CurrentItem();
}
public class ConcreteIterator : Iterator
{
ConcreteAggregate _aggregate;
int _currentItemIndex = 0;
public ConcreteIterator(ConcreteAggregate C_aggregate)
{
this._aggregate = C_aggregate;
}
public string FirstItem()
{
return _aggregate[0];
}
public string NextItem()
{
string return_obj = null;
if (_currentItemIndex < _aggregate.Count - 1)
{
return_obj = _aggregate[++_currentItemIndex];
}
return return_obj;
}
public string CurrentItem()
{
return _aggregate[_currentItemIndex];
}
public bool Done()
{
return _currentItemIndex >= _aggregate.Count;
}
}
public interface Aggregate
{
Iterator Create();
}
public class ConcreteAggregate : Aggregate
{
List<string> _items = new List<string>();
public Iterator Create()
{
return new ConcreteIterator(this);
}
public int Count
{
get { return _items.Count; }
}
public string this[int index]
{
get { return _items[index]; }
set { _items.Insert(index, value); }
}
}
If you want to learn more about the Iterator pattern, then check out this article.
Mediator pattern
The Mediator design pattern enables communication between multiple objects or components without them needing to be aware of each other. This reduces the coupling between the objects or components and makes them more reusable and maintainable.
The Mediator design pattern is often used in event-driven systems, such as graphical user interfaces, where events need to be handled by different components. It is also used in object-oriented programming to allow objects to interact with each other without knowing each other’s internals.
You can implement the Mediator design pattern in various ways, but the most common implementation is through a Mediator class. This class defines the interface for communication between the objects or components. It also references all the objects or components that need to communicate with each other. Finally, the Mediator class also handles the routing of messages between the objects or components.
The following sample represents the structural code in C#.
public interface IMediator
{
void SendMessage(string message, ICollegue collegue);
}
public interface ICollegue
{
void Send(string message);
void GetMessage(string message);
}
public class ConcreteColleague1 : ICollegue
{
private IMediator _mediator;
public ConcreteColleague1(IMediator mediator)
{
_mediator = mediator;
}
public void GetMessage(string message)
{
Console.WriteLine($"Colleague1 got message: {message}");
}
public void Send(string message)
{
_mediator.SendMessage(message, this);
}
}
public class ConcreteColleague2 : ICollegue
{
private IMediator _mediator;
public ConcreteColleague2(IMediator mediator)
{
_mediator = mediator;
}
public void GetMessage(string message)
{
Console.WriteLine($"Colleague2 got message: {message}");
}
public void Send(string message)
{
_mediator.SendMessage(message, this);
}
}
public class ConcreteMediator : IMediator
{
public ICollegue Collegue1 { get; set; }
public ICollegue Collegue2 { get; set; }
public void SendMessage(string message, ICollegue collegue)
{
if (collegue == Collegue1)
{
Collegue2.GetMessage(message);
}
else
{
Collegue1.GetMessage(message);
}
}
}
If you want to learn more about the Mediator pattern, see a real-world code example, then check out this article.
Memento pattern
The Memento design pattern is a powerful tool for managing the state in an object-oriented program. It allows an object to save and restore its state without exposing its internals to the outside world.
The advantage of this approach is that it preserves the object’s encapsulation while still allowing its state to be easily managed. The biggest downside of the Memento pattern is that it requires additional objects, which can add complexity to the code. However, in many cases, this extra complexity is worth it since you get the increased flexibility and power that the Memento pattern provides.
The following sample represents the structural code in C#.
public class Originator
{
int _statesnap;
public int State
{
get { return _statesnap; }
set
{
_statesnap = value;
Console.WriteLine("State is " + _statesnap);
}
}
public Memento CreateMemento()
{
return new Memento(_statesnap);
}
public void RestoreMemento(Memento memento)
{
Console.WriteLine("The Previous State was: ");
State = memento.Statesnap;
}
}
public class Memento
{
int _statesnap;
public Memento(int statesnap)
{
this._statesnap = statesnap;
}
public int Statesnap
{
get { return _statesnap; }
}
}
public class Caretaker
{
Memento memento;
public Memento Memento
{
set { memento = value; }
get { return memento; }
}
}
If you want to learn more about the Memento pattern, see a real-world code example, then check out this article.
Observer pattern
The Observer design pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement event-handling systems. The Observer design pattern is also known as the Publish-Subscribe or Pub-Sub pattern.
It is commonly used in graphical user interfaces to implement event handlers for user input events such as mouse clicks and key presses. In the Model View Controller (MVC) framework, the Observer design pattern is used to implement the observer role of the Controller class.
The following sample represents the structural code in C#.
public abstract class AbstractSubject
{
private List<AbstractObserver> observers = new();
public void Attach(AbstractObserver observer)
{
observers.Add(observer);
}
public void Detach(AbstractObserver observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (AbstractObserver o in observers)
{
o.Update();
}
}
}
public class ConcreteSubject : AbstractSubject
{
public string SubjectState { get; set; } = string.Empty;
}
public interface AbstractObserver
{
public void Update();
}
public class ConcreteObserver : AbstractObserver
{
private string _observerName;
private string _observerState = string.Empty;
public ConcreteObserver(
ConcreteSubject observedObject, string observerName)
{
Subject = observedObject;
_observerName = observerName;
}
public void Update()
{
_observerState = Subject.SubjectState;
Console.WriteLine($"Observer: {_observerName}\nState Changed to {_observerState}");
}
public ConcreteSubject Subject { get; set; }
}
If you want to learn more about the Observer pattern, see a real-world code example, then check out this article.
State pattern
The State design pattern is a behavioral software design pattern that enables an object to change its behavior when its internal state changes.
Objects utilizing this pattern appear to have changed their class, although this functionality is implemented within the object’s original class. The State design pattern is useful for applications that require dynamic and flexible behavior, such as finite-state machines.
In addition, this pattern can help to reduce conditional complexity within an object by encapsulating state-based behavior in separate classes. As a result, the State design pattern can improve the maintainability and extensibility of software.
The following sample represents the structural code in C#.
public class Computer
{
private State _currentState;
public Computer()
{
_currentState = new OffState();
}
public void PressPowerButton()
{
_currentState.PressPowerButton(this);
}
public void SetState(State state)
{
_currentState = state;
}
}
public interface State
{
void PressPowerButton(Computer computer);
}
public class OnState : State
{
public void PressPowerButton(Computer computer)
{
Console.WriteLine("Computer is already on. Going to sleep mode...");
computer.SetState(new SleepState());
}
}
public class OffState : State
{
public void PressPowerButton(Computer computer)
{
Console.WriteLine("Turning on computer...");
computer.SetState(new OnState());
}
}
public class SleepState : State
{
public void PressPowerButton(Computer computer)
{
Console.WriteLine("Waking up computer from sleep mode...");
computer.SetState(new OnState());
}
}
If you want to learn more about the State pattern, then check out this article.
Strategy pattern
The Strategy design pattern is used to define a family of algorithms, define each of them as an object, and make them interchangeable. This allows the algorithms to be used independently of the client that uses them.
The Strategy design pattern is useful for situations where it is necessary to change an object’s behavior at runtime dynamically.
For example, a web application might use different strategies to route requests to the server. The application can dynamically select the appropriate strategy at runtime by encapsulating these routing algorithms as objects. This flexibility can be particularly useful when developing applications deployed in environments with varying resources or requirements.
This flexibility comes at the cost of increased code complexity and the need for developer expertise to understand and maintain the code. However, in many cases, the benefits of the Strategy pattern outweigh these drawbacks.
When used properly, it can lead to more robust and extensible software that is easier to evolve.
The following sample represents the structural code in C#.
public interface InterfaceStrategy
{
IEnumerable<string> PerformAlgorithm(List<string> list);
}
class DefaultConcreteStrategy : InterfaceStrategy
{
public IEnumerable<string> PerformAlgorithm(List<string> list)
{
list.Sort();
return list;
}
}
class AlternativeConcreteStrategy : InterfaceStrategy
{
public IEnumerable<string> PerformAlgorithm(List<string> list)
{
list.Sort();
list.Reverse();
return list;
}
}
class Context
{
private InterfaceStrategy _strategy;
public Context()
{
}
public Context(InterfaceStrategy strategy)
{
_strategy = strategy;
}
// here we can replace the current or default strategy if we choose
public void SetStrategy(InterfaceStrategy strategy)
{
_strategy = strategy;
}
public void CarryOutWork()
{
Console.WriteLine("Context: Carrying out Sorting Work");
var myResult = _strategy
.PerformAlgorithm(new List<string>
{
"the",
"boy",
"is",
"leaving"
});
Console.WriteLine(String.Join(",", myResult));
}
}
If you want to learn more about the Strategy pattern, see a real-world code example, then check out this article.
Template Method pattern
The Template Method design pattern is a behavioral software design pattern that defines the program skeleton of an algorithm in operation, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the structure of the algorithm. The Template Method pattern is a fundamental technique for code reuse and is frequently used in frameworks.
This design pattern can lead to DRY and clean code when used correctly.
A good example of this is authentication.
Different types of authentication (e.g., basic vs. OAuth) might share some common steps (e.g., checking credentials) but also have different steps (e.g., handling tokens). The Template Method pattern would allow you to define the general outline of the authentication process in a base class and then have subclasses implement the specific details for each type of authentication.
Another advantage of using the Template method pattern is that it is easy to add new variations of an algorithm. You simply need to create a new subclass and override the appropriate methods. Adding another class defines another algorithm without touching the existing codebase.
The following sample represents the structural code in C#.
abstract class AbstractClass
{
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
ChildOperation1();
Hook();
ChildOperation2();
}
protected void PrimitiveOperation1()
{
Console.WriteLine("PrimitiveOperation1 with definition " +
"in Template Method");
}
protected void PrimitiveOperation2()
{
Console.WriteLine("PrimitiveOperation2 with definition " +
"in Template Method");
}
protected abstract void ChildOperation1();
protected abstract void ChildOperation2();
protected virtual void Hook()
{
//Empty body
}
}
class ConcreteClass1 : AbstractClass
{
protected override void ChildOperation1()
{
Console.WriteLine("Operation 1 Implementation " +
"in ConcreteClass1");
}
protected override void ChildOperation2()
{
Console.WriteLine("Operation 2 Implementation " +
"in ConcreteClass1");
}
protected override void Hook()
{
Console.WriteLine("Hook Overriding in ConcreteClass1");
}
}
If you want to learn more about the Template Method pattern, see a real-world code example, then check out this article.
Visitor pattern
The Visitor design pattern separates an algorithm from an object structure on which it operates. As a result, this separation provides the ability to add new operations to existing structures without modifying the structures themselves.
The heart of the Visitor pattern is an abstract class or interface named Visitor:
- The Visitor defines a set of methods, each of which takes a concrete element in the object structure as its argument. Each method in the Visitor class represents a different operation that it can perform on the elements of the object structure.
- Then, for each specific type of element in the structure, there is a corresponding concrete Visitor class that implements the abstract methods in terms of that element’s particular interface.
- Finally, an Element interface defines Accept(visitor) method, which is used to call the appropriate method on the visitor for each element.
By doing this, you can define new operations over an object structure simply by defining new concrete visitor classes.
The following sample represents the structural code in C#.
public interface IElement
{
void Accept(IVisitor visitor);
}
public interface IVisitor
{
void Visit(IElement element);
}
public class Student : IElement
{
public string StudentName { get; set; }
public Student(string name)
{
StudentName = name;
}
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
public class Physician : IVisitor
{
public string Name { get; set; }
public Physician(string name)
{
Name = name;
}
public void Visit(IElement element)
{
Student student = (Student)element;
Console.WriteLine($"Physician: {Name} conducted a " +
$"health checkup of the student: {student.StudentName}");
}
}
class Cook : IVisitor
{
public string Name { get; set; }
public Cook(string name)
{
Name = name;
}
public void Visit(IElement element)
{
Student student = (Student)element;
Console.WriteLine($"Cook: {Name} gave the healthy " +
$"meal to the student: {student.StudentName}");
}
}
public class College
{
private readonly List<IElement> elements;
public College()
{
elements = new List<IElement>
{
new Student("Todd"),
new Student("Maggie"),
new Student("Stella")
};
}
public void PerformOperation(IVisitor visitor)
{
foreach (var student in elements)
{
student.Accept(visitor);
}
}
}
If you want to learn more about the Visitor pattern, see a real-world code example, then check out this article.
Conclusion
Learning about behavioral design patterns is a great start if you want to improve your coding skills. You can create more efficient and reliable code by understanding how these patterns work. Plus, each pattern encourages objects to communicate in a specific way, which can help keep the system running smoothly.
And in some cases, behavioral patterns can also help optimize performance by reducing the amount of unnecessary communication between objects.
So take some time to learn about these patterns and when to use them.
You’ll be glad you did.