Chapter 32 (Behavioural Design Patterns)
Chapter 32 (Behavioural Design Patterns)
Chapter 32
Behavioural Design Patterns
1. Introduction
Behavioural patterns are concerned with algorithms and the assignment of responsibilities
between objects. They are mostly concerned with communication between objects.
2.1 Introduction
The Chain of Responsibility (CoR) pattern allows a number of classes to handle a request
without any of them knowing about the capabilities of the other classes. Requests are passed
along a chain of handlers. Upon receiving a request, each handler processes the request if
possible and then passes it on to the next handler in the chain if necessary and possible. It
provides a loose coupling between these classes; the only common link is the request that is
passed between them.
The pattern can be used when an expenditure must be approved on the lowest level for which
the level is authorised. It can also be used to control access on different levels or when
diagnosing a patient based on symptoms (see the FPM link below). It can also be used to execute
a series of steps in a predefined order.
• FPM:
https://www.aafp.org/journals/fpm/blogs/inpractice/entry/covid_diagnosis_flowcharts.html
1. The Handler declares the interface which must be implemented by all concrete handlers. It
has a method for handling requests as well as a method for setting the next handler on the
chain.
2. The Base Handler is an optional abstract class that contains the code that are common to all
handler classes. Usually, this class defines a field for storing a reference to the next handler.
3. Concrete Handlers contain the actual code for processing requests. Upon receiving a request,
each handler must decide whether to process it and, additionally, whether to pass it along the
chain.
4. The Client declares all handlers and set up the chain of responsibility.
Handler interface
interface IApprover
{
void SetSuccessor(IApprover successor);
string ProcessRequest(decimal amount);
} //interface IApprover
Base handler
Note that the class is abstract and the ProcessRequest method is abstract. It must be
overridden in the concrete handlers.
Concrete handler
A separate class is defined for every level of approval. Each class overrides the
ProcessRequest method with the specific business rules as applicable.
It is important to
(i) decide if the handler can do the work itself or have to pass it on to the next level;
(ii) check if the next level handler is defined;
(iii) handle the request in case there is no next handler.
Client
The Client class instantiates all handlers and sets up the chain of responsibility. It then kicks
off the processing chain by calling the handler at the lowest level in the hierarchy.
class Client
{
static void Main(string[] args)
{
//Enter amount
Console.Write("\tEnter amount: ");
decimal amount = decimal.Parse(Console.ReadLine());
//Instantiate handlers
Approver director = new Director();
Approver vp = new VicePresident();
Approver pres = new President();
//Process expenditure
Console.WriteLine("\t" + director.ProcessRequest(amount));
Class diagram
CoR can also be used to specify the order of execution of steps. The following, rather
conceptual, example shows how that is done.
Handler interface
interface IHandler
{
void SetSuccessor(IHandler successor);
string Handle();
}
Base handler
Concrete handlers
Client
class Client
{
static void Main(string[] args)
{
//Instantiate step handlers
IHandler step1 = new Step1();
IHandler step2 = new Step2();
IHandler step3 = new Step3();
IHandler step4 = new Step4();
• https://refactoring.guru/design-patterns/chain-of-responsibility/csharp/example#lang-
features
• https://refactoring.guru/design-patterns/chain-of-responsibility
• https://www.dofactory.com/net/chain-of-responsibility-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/chain-of-responsibility-design-pattern-
dotnet
• Test 1, 2020, Question 6 (Approval of expenditure)
• Project 7, 2020 (Admission to CSI)
• Test 2, 2021, Question 3 (Theory)
• Project 6, 2022 (Wireless network troubleshooter)
• Use the Chain of Responsibility pattern when your program is expected to process different
kinds of requests in various ways, but the exact types of requests and their sequences are
unknown beforehand.
• Use the pattern when it is essential to execute several handlers in a particular order.
Advantages
Disadvantage
• Cardoso, p 263
• Cooper, Chapter 21, 358
• Gamma, Helm, Johnson, Vlissides (1995, p 284)
• Nesteruk (2019, p 185)
3. Command pattern
3.1 Introduction
With the Command pattern, a request is turned into a stand-alone object that contains all
information about the request. This transformation lets you parameterize methods with different
requests.
Commands are operations that must be executed on a class, and they can change the state of an
object in the class. We are used to have operations as methods inside a class, but with the
command pattern, the operations are in another class.
With this pattern, we must identify three entities, namely the invoker, receiver, and command.
Depending om the scenario, the client may act as invoker, although that may (i) limit the
functionality of the invoker and (ii) put too much responsibility on the client.
• In a bank account system, the receiver will be the account class. Operations might be deposit
and withdraw. The invoker will be the ATM or interface used by the account holder.
• In a text editor, the receiver will be a document, the invoker will be a word processing
program and the commands may be Insert, Delete, Cut, Copy, Paste, etc.
• For a TV with remote control, the receiver is the TV, the invoker is the remote control and
commands will be channel up, channel down, volume up, volume down, etc.
• For a light in the room, the receiver is the bulb, the invoker is the switch, and the commands
will be SwitchOn and SwitchOff.
1. The Invoker (aka Sender) class is responsible for initiating requests. This class may have a
private field for storing a reference to a command object. The sender triggers that command
instead of sending the request directly to the receiver. Note that the sender is not responsible
for creating the command object. Usually, it gets a pre-created command from the client via
the constructor or else it may be specified as a parameter of the ExecuteCommand method.
2. The Command interface usually declares just a single method for executing the command.
4. The Receiver class contains some business logic. Almost any object may act as a receiver.
Most commands only handle the details of how a request is passed to the receiver, while the
receiver itself does the actual work.
5. The Client creates and configures concrete command objects. The client must pass all of the
request parameters, including a receiver instance, into the command’s constructor.
3.3 Example
Consider the account of an ATM system. This example is very limited but serves to illustrate
the principles.
Receiver
The actual operation and checking of business rules, e.g. Balance > 0, is done by the receiver.
Invoker
The invoker is responsible to trigger execution of the given command. Actually, if we don't use
the invoker to keep track of commands in a stack (see later), we get away without it. The client
will then call the Execute command directly.
Command interface
Note: If you want individual commands to return a value, you can set the return type of Execute
to object and then cast the result to the applicable type in the client.
Commands
Every operation, Deposit, Withdraw, etc. goes into a separate class. Every command class
inherits from ICommand and must implement the Execute method. The receiver of the
command and any scenario-specific values are specified through the constructor parameters.
//Constructor
public Deposit(Account account, decimal amount)
{
this.account = account;
this.amount = amount;
}
Client
The client references the receiver and invoker and instantiate separate command objects for
every operation.
class Client
{
static void Main(string[] args)
{
//Receiver: Account
Account account = new Account();
//Invoker: ATM
ATM atm = new ATM();
//Operation: Deposit
//- Create command
ICommand deposit = new Deposit(account, 100);
//- Execute
atm.ExecuteCommand(deposit);
Console.WriteLine("\tBalance: R " + account.Balance.ToString("0.00"));
//Operation: Withdraw
//- Create command and Execute in one go
atm.ExecuteCommand(new Withdraw(account, 70));
Console.WriteLine("\tBalance: R " + account.Balance.ToString("0.00"));
Class diagram
Conceptual
Client
- rcvr = new Receiver()
- cmd = new Command(rcvr, params)
- inv = new Invoker()
- inv.ExecuteCommand (cmd)
- cmd.Execute()
- cmd.rcvr.Operation(cmd.params)
- State of receiver changed
Scenario specific
Client
- acc = new Account()
- dep = new Deposit(acc, amount)
- atm = new ATM()
- atm.ExecuteCommand(dep)
- dep.Execute()
- dep.acc.SetBalance(dep.amount)
- dep.acc.amount updated
Although there are many ways to implement undo/redo, the Command pattern is perhaps the
most popular of all. To be able to revert operations, you need to implement the history of
performed operations. The command history is a stack that contains all executed command
objects along with related backups of the application’s state.
Command interface
Commands
Invoker
The invoker now has a private stack to keep track of the commands and also an Unexecute
method. Commands are pushed on the stack when they are executed. On UnExecute, a
command is unexecuted and then popped from the stack.
//Execute command
public void ExecuteCommand(ICommand command)
{
command.Execute();
History.Push(command);
} //ExecuteCommand
//Undo command
public void UnExecuteCommand()
{
if (History.Count > 0)
History.Pop().UnExecute();
} //Undo
} //class ATM
Hints:
• Replace the stack with a list of commands.
• Keep track of the current index
• https://refactoring.guru/design-patterns/command/csharp/example#lang-features
• https://refactoring.guru/design-patterns/command
• https://code-maze.com/command/
• https://www.dofactory.com/net/command-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/command-design-pattern-dotnet
• https://www.c-sharpcorner.com/UploadFile/851045/command-design-pattern-in-C-Sharp/
• Exam 1, 2021, Question 5 (ATM)
• Test 2, 2021, Question 4 (TV & remote)
• Project 6, 2021 (TV and Remote)
• Project 7, 2022 (List with commands)
• Poject 6, 2023 (Media player)
Advantages
• Single Responsibility Principle. You can decouple classes that invoke operations from
classes that perform these operations.
• Open/Closed Principle. You can introduce new commands into the app without breaking
existing client code.
• You can implement undo/redo.
• You can implement deferred execution of operations.
• You can assemble a set of simple commands into a complex one.
Disadvantage
• The code may become more complicated because of an extra layer between senders and
receivers.
• Since the commands live outside the receiver class, members that could otherwise be private
must now be public to make them accessible from the commands. This may lead to lower
security. One could solve this through the use of parameters, but then the system becomes
even more complex.
• Since, the Command pattern can turn a specific method call into a stand-alone object, it can
be used to parameterize objects. This allows the passing of commands as method arguments,
storing them inside other objects, switching linked commands at runtime, etc.
• Use the pattern to queue operations, schedule their execution, or execute them remotely.
• Use the Command pattern to implement reversible operations.
• Cardoso, p 217
• Cooper, Chapter 22
• Freeman et al., Chapter 6, p 191
• Gamma, Helm, Johnson, Vlissides (1995, p 299)
• Martin & Martin (2006), Chapter 21
4. Interpreter pattern
This pattern is left for self-study. You can look at the following sources for a start:
• https://www.geeksforgeeks.org/interpreter-design-pattern/
• https://www.tutorialspoint.com/design_pattern/interpreter_pattern.htm
• https://sourcemaking.com/design_patterns/interpreter
• https://dofactory.com/net/interpreter-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/interpreter-design-pattern-c-sharp
• Cooper, Chapter 23
• Gamma, Helm, Johnson, Vlissides (1995, p 314)
• Nesteruk (2019, p 210)
5. Iterator pattern
5.1 Introduction
The Iterator pattern allows traversal through a list or collection of data using a standard
interface without having to know the details of the internal representations of that data.
.Net has an enumerator feature that can do this (cf Section 5.5), but with the Iterator pattern,
you can also define special iterators that return elements in reversed order or perform some
special processing and return only specified elements of the data collection.
It is important to note that the collection class is separated from the iterator class. The collection
class declares methods to add and remove items from the collection. The iterator class contains
methods to traverse the collection, for example First, Next, Previous, Current, etc.
1. The Iterator interface declares the operations required for traversing a collection: fetching
the next element, retrieving the current position, restarting iteration, etc.
2. Concrete iterators implement specific algorithms for traversing a collection. The iterator
object should track the traversal progress on its own. The iterator class also contains a
reference to the collection on which the iterator methods operate.
3. The Collection interface declares one or multiple methods for getting iterators compatible
with the collection. Note that the return type of the methods must be declared as the iterator
interface. The Collection interface also declares methods to add and remove items from the
collection.
4. Concrete collections return new instances of a particular concrete iterator class.
5. The Client works with both collections and iterators via their interfaces. Typically, clients
don’t create iterators on their own, but instead get them from collections.
5.3 Example
interface IList<T>
{
//Basics
int Count { get; }
void Add(T item);
T this[int index] { get; }
Concrete collection
• The CreateIterator method instantiates and returns an instance of the Iterator class.
This object can then be used in the client to step through elements in the array.
Note: One can either decide to send the entire instance through to the iterator (this) or just
the array. In case of the latter, there is no need for an indexer since the iterator can access
the elements though its shallow copy of the array. This approach has the disadvantage that
the iterator do not have access to the Count property in the collection and that iteration will
also include the unused spaces in the array.
//Constructor
public List()
{
array = new T[2];
Count = 0;
} //Constructor
//Add
public void Add(T item)
{
if (Count == array.Length) //Array is full
System.Array.Resize(ref array, array.Length * 2);
array[this.Count] = item;
Count++;
} //Add
//Indexer
public T this[int index]
{
get
{
if (index >= 0 && index < Count)
return array[index];
return default;
}
} //Indexer
//Enumerator
//- This uses System.Collections.Generic and is
// a .Net alternative to the iterator pattern
public System.Collections.Generic.IEnumerator<T> GetEnumerator()
{
int i = 0;
while (i < Count)
yield return array[i++];
} //GetEnumerator
} //class List
The Iterator pattern is based on methods First and Next and read-only property Current to
allow a client to access members in the collection.
interface IIterator<T>
{
T First();
T Current { get; }
T Next();
}
Concrete iterator
//Constructor
public Iterator(IList<T> list)
{
this.list = list;
index = 0; //Go to first element
} //Constructor
//Navigation
public T Current
{
get
{
return list[index];
}
} //Current
public T First()
{
index = 0;
return list[index];
} //First
public T Next()
{
index++;
return list[index];
} //Next
} //class Iterator
We do not need an iterator to traverse the elements of a collection. We can simply use the
indexer (if there is one).
class Client
{
static void Main()
{
//Create list
IList<string> lstNames = new List<string>();
//Add names
lstNames.Add("John"); lstNames.Add("Mike");
lstNames.Add("Susan"); lstNames.Add("Sarah");
//Exit program
Console.Write("\tPress any key to exit ...");
Console.ReadKey();
} //Main
} //class Client
With the iterator, we do not need to access the list by index. It is important to create the iterator
first as the iterate members (First, Next, Current, etc.) are in a separate class. It is also
important to make sure that you reset the iterator before traversal by calling First().
Class diagram
• https://www.dofactory.com/net/iterator-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/iterator-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/iterator
• Test 3, 2021, Question 4
• Project 7, 2021
• Project 7, 2022
The Iterator design pattern has been deliberately hidden in C# in favour of the simple
IEnumerator / IEnumerable. Notice that these interfaces only support forward iteration – there
is no MoveBack() in IEnumerator. The existence of yield allows you to return elements one
at a time (Nesteruk, 2019, 232).
Advantages
• Single Responsibility Principle. You can clean up the client code and the collections by
extracting bulky traversal algorithms into separate classes.
• Open/Closed Principle. You can implement new types of collections and iterators and pass
them to existing code without breaking anything.
• You can iterate over the same collection in parallel because each iterator object contains its
own iteration state.
• You can pause an iteration and continue it when needed.
Disadvantages
• Applying the pattern can be an overkill if your app only works with simple collections and
the IEnumerator interface of .Net will suffice.
• Using an iterator may be less efficient than going through elements of some specialized
collections directly.
• Use the Iterator pattern when your collection has a complex data structure under the hood,
but you want to hide its complexity from clients (either for convenience or security reasons).
• Use the pattern to reduce duplication of the traversal code across your app.
• Use the Iterator when you want your code to be able to traverse different data structures or
when types of these structures are unknown beforehand.
6. Mediator pattern
6.1 Introduction
There are situations when it is not desirable that instances of a class are aware of each other’s
presence or communicate through references. As soon as a reference to another instance is kept,
that object’s lifetime is extended beyond what might originally be desired.
When a large proportion of code has different classes that communicate with one another
through direct references, it becomes problematic. The more each class needs to know about
the methods of another class, the more tangled the class structure can become. Eventually, the
dependencies between classes can become chaotic. This makes the program harder to read and
harder to maintain as any change may affect code in several other classes.
The Mediator pattern is a mechanism for facilitating communication between components. The
Mediator pattern promotes looser coupling between classes and reduces the dependencies
between objects. The mediator class is the only class that has detailed knowledge of the methods
of other classes. Direct communication between the objects is restricted and they are forced to
collaborate only via a mediator object. Classes inform the mediator when changes occur, and
the mediator passes on the changes to any other classes that need to be informed.
1. Components are one or more classes that contain some business logic. Each component has
a reference to a mediator (black association), declared with the type of the mediator interface.
Components are not aware of the actual class of the mediator.
Note: Send and Get in this context refers to the direction of flow of the message parameter
from the perspective of the component. In SendMessage, HandleMessage is called wich
sends a message to the mediator. In GetMessage, an incoming message parameter is
received.
2. The Mediator interface declares methods of communication with components, which usually
include just a single Notify or Send method. (I prefer to refer to this method as Relay or
Handle since it gets a message and passes it on.) Components may pass any context as
arguments of this method, including their own objects, but only in such a way that no
coupling occurs between a receiving component and the sender’s class.
3. Concrete Mediators keep references to all components they manage (blue associations).
4. The Client has references to all mediators and optionally to all and components (red
associations). The client may (i) call the SendMessage of a component which will in turn (ii)
call the HandleMessage method of the mediator which will in turn (iii) call the GetMessage
of some or all other components (green curved arrows).
Components must not be aware of other components. If something important happens within
or to a component, it must only notify the mediator. When the mediator receives the notification,
it identifies the sender, and decides what (other) component should be triggered in return. From
a component’s perspective, it all looks like a total black box. The sender does not know who
will end up handling its request, and the receiver does not know who sent the request in the first
place unless the mediator reveals it.
Components
IMediator interface
• The mediator interface declares methods of communication with components, which usually
include just a single notiofication method.
Concrete Mediator
//Constructor
public ATC()
{
timer = new Timer(5000);
timer.Elapsed += Timer_Elapsed;
timer.Start();
} //Constructor
} //class ATC
Client
class Client
{
static void Main(string[] args)
{
//Mediator
IMediator atc = new ATC();
//Instantiate planes
//- Components must not be aware of other components
Airplane plane1 = new Airplane("ABC", atc);
Airplane plane2 = new Airplane("DEF", atc);
Airplane plane3 = new Airplane("GHI", atc);
Airplane plane4 = new Airplane("JKL", atc);
Airplane plane5 = new Airplane("MNO", atc);
//After a while, more planes join the air space of the airport and request
to land
Thread.Sleep(8000);
plane4.RequestRunway();
plane5.RequestRunway();
Class diagram
Consider the (rather awkward) example of a father and mother who are angry at each other.
They don't speak to each other directly and all communication is through a child who is sitting
at the same table.
In this example, Father and Mother are subclasses of AParent and serve as components. The
Child class is the mediator.
Components
• The components may or may not belong to the same class. If not, we need an abstract class
with the mediator details and then derived classes with specific behaviours. In this example,
we let the Father and Mother classes (components) respond differently on a message from
the child (mediator).
• Every component object (father and mother) must have a reference to the mediator
(child).
• The constructor does a two-way connection:
- The component gets information about the mediator.
- The mediator gets information about the component.
• The SendMessage method calls the mediator's Send (or Invoke) method. The mediator
will, in turn, relay the message to other components.
• In this example, there are two overloaded GetMessage methods. Both messages are called
from the mediator. The first version identifies the sender (a component) and the second one
identifies the mediator.
//Constructor
public AParent(string name, IMediator child)
{
this.Name = name;
this.child = child; //Tell the component who the mediator is
child.AddParent(this); //Tell the mediator to add this component to its
list of components for which it must care
} //Constructor
//Comms out - send to mediator who will relay the message further
public void SendMessage(string message)
{
child.RelayMessage(this, message);
}
//Comms in
public void GetMessage(AParent sender, string message)
{
string sMsg = string.Format("\t" + child.Name + ": \"{0}, {1} says '{2}'\"",
this.Name, sender.Name, message);
Console.WriteLine(sMsg);
} //GetMessage
//Comms in
public void GetMessage(IMediator child, string message)
{
string sMsg = string.Format("\t" + child.Name + ": \"{0}, {1}\"",
this.Name, message);
Console.WriteLine(sMsg);
} //GetMessage
IMediator interface
• The essential member in this interface is the RelayMessage (aka Invoke or Notify) method.
It allows components to send a message to the mediator. The mediator will then relay the
message to other components.
• The first overloaded RelayMessage method is used to identify the sending component. The
second one can be used by the client to send a message.
• The AddParent method is used in the component's constructor to add the component to the
list of components for the mediator.
interface IMediator
{
string Name { get; }
void AddParent(AParent parent);
void RelayMessage (AParent sender, string msg);
void RelayMessage (string msg);
} //interface IMediator
Concrete Mediator
//Constructor
public Child(string name)
{ this.Name = name;
lstParents = new List<AParent>();
} //Constructor
//Mediator receives request from client and send message to all components
public void RelayMessage(string message)
{
foreach (AParent parent in lstParents)
{ parent.GetMessage(this, message);
parent.Responds();
} //foreach
} //Send
} //class Child
Client
class Client
{
static void Main(string[] args)
{
//Create mediator
IMediator child = new Child("Johnny");
Note: In this example, the client initiates the communication from the components, but
actually, the components should initiate the messages themselves.
Class diagram
Service class
1. Declare the delegates
2. Declare the events
3. Invoke the events
Client class
4. Subscribe an event handler to the event
5. Handle the event
Components
• Step 1: Add two delegates (method types) – one for a general message and one for a message
with identifying sender, receiver and mediator.
...
//Comms in
public void GetMessage(AParent sender, string message)
{
//Step 3: Invoke the event
OnComponentMessage?.Invoke(sender, this, child, message);
} //GetMessage
//Comms in
public void GetMessage(string message)
{
//Step 3: Invoke the event
OnComponentMessage?.Invoke(null, this, child, message);
} //GetMessage
IMediator interface
interface IMediator
{
string Name { get; }
void AddParent(AParent parent);
void RelayMsg(AParent sender, string msg);
void RelayMsg (string msg);
event delOnMediatorMessage OnMediatorMessage;
} //interface IMediator
Concrete mediator
...
...
} //class Child
Client
• Step 4: Subscribe
• Step 5: Handle. The text formatting is done in the event handler in the client class and
no longer in the service class.
• Note that we can do Step 4 and 5 together with an anonymous method if the handler is
short and simple.
class Client
{
static void Main(string[] args)
{
//Create mediator
IMediator child = new Child("Johnny");
child.OnMediatorMessage += OnMediatorMessage; //Step 4: Subscribe
//Step 5: Handle
private static void OnMediatorMessage(AParent sender, AParent receiver,
IMediator child, string message)
{
string msg = string.Format("\t" + sender.Name + ": \"{0}, tell {1}, '{2}'\"",
child.Name, receiver.Name, message);
Console.WriteLine(msg);
}
//Step 5: Handle
private static void OnComponentMessage(AParent sender, AParent receiver,
IMediator child, string message)
{
string msg = "";
if (sender != null)
msg = string.Format("\t" + child.Name + ": \"{0}, {1} says '{2}'\"",
receiver.Name, sender.Name, message);
else
msg = string.Format("\t" + child.Name + ": \"{0}, {1}\"",
receiver.Name, message);
Console.WriteLine(msg);
} //OnComponentMessage
} //class Client
• https://www.dotnettricks.com/learn/designpatterns/mediator-design-pattern-c-sharp
• https://www.dofactory.com/net/mediator-design-pattern
• https://refactoring.guru/design-patterns/mediator
• Exam 1, 2020, Question 6 (Chat room)
• Exam 2, 2021, Question 5 (ATC)
• Project 8, 2022 (Real estate agent)
• Project 7, 2023 (Maps)
Advantages
• Single Responsibility Principle. You can extract the communications between various
components into a single place, making it easier to comprehend and maintain.
• Open/Closed Principle. You can introduce new mediators without having to change the
actual components.
• You can reduce coupling between various components of a program.
• You can reuse individual components more easily.
Disadvantage
• Over time a mediator can evolve into a god object. That means that the mediator has too
much power and knows too much about the component classes.
• Use the Mediator pattern when it is not desirable that instances of a class are aware of each
other’s presence or communicate through references
• Use the Mediator pattern when it is hard to change some of the classes because they are
tightly coupled to a bunch of other classes.
Copyright: PJ Blignaut, 2022
32
• Use the pattern when you cannot reuse a component in a different program because it is too
dependent on other components.
• Use the Mediator when you find yourself creating tons of component subclasses just to
reuse some basic behaviour in various contexts.
7. Memento pattern
7.1 Introduction
Sometimes it is necessary to record the internal state of an object. This is required when
implementing checkpoints and undo mechanisms that let users back out of tentative operations
or recover from errors. You must save state information somewhere so that you can restore
objects to their previous states. But objects normally encapsulate some or all of their state,
making it inaccessible to other objects and impossible to save externally. Exposing this state
would violate encapsulation, which can compromise the application’s reliability and
extensibility.
We can solve this problem with the Memento pattern. A memento is an object that stores a
snapshot of the internal state of another object – the memento’s originator. The undo
mechanism will request a memento from the originator when it needs to checkpoint the
originator’s state. The originator initializes the memento with information that characterizes its
current state. Only the originator can store and retrieve information from the memento – the
memento is “opaque” to other objects.
The Memento pattern allows saving and restoring the previous state of an object without
revealing the details of its implementation.
The memento pattern is implemented with three objects, namely the originator, a caretaker,
and a memento.
• The Originator class is the class of objects that we want to keep history of. It has some
internal state fields, i.e. data fields and properties. It has methods to return its current state
as a Memento and update its state based on the state of a given memento.
• The Memento class has the same data fields and properties (state) as the Originator, but not
the behaviours (methods). The memento should be immutable – i.e. it should not be possible
to change its state after construction.
• The Caretaker is going to do something to the originator but wants to be able to undo the
change. The caretaker may be the client class. The caretaker contains a list or stack of
mementos. Before any operation, the caretaker gets a memento object and adds it to the list.
The caretaker can undo an operation or reverting the originator to a previous version by
retrieving a memento from the list.
• If the programming language does not allow nested classes, it is important to ensure that the
caretaker cannot access the memento's data fields. This is normally done by using an
explicitly declared intermediate interface.
• It is important to ensure that other classes cannot change state of the originator through the
memento. This can be done by adding an interface which the originator must implement.
• The Memento class can be nested inside the Originator class. This allows the originator to
access the fields inside the memento, even though they are declared private, but the caretaker
(client) would not be able to tamper with its state. Since C# allows nested classes, this is the
approach that we will follow.
1. The Originator class can produce snapshots of its own state (SaveToMemento), as well as
restore its state from snapshots when needed (RestoreFromMemento).
2. The Memento is a value object that acts as a snapshot of the originator’s state. It’s a common
practice to make the memento immutable and pass it the data only once, via the constructor.
3. A caretaker keeps track of the originator’s history by storing a stack of mementos. When the
originator has to travel back in history, the caretaker fetches the topmost memento from the
stack and passes it to the originator’s restoration method.
4. In this implementation, the memento class is nested inside the originator. The set part of all
properties is private so that the caretaker or any other class cannot change the data.
Consider the example of a Student class which keeps track of a student's student number, name
and modules for which he/she is registered.
Originator
There is nothing unfamiliar here. This is the class for which we want to keep track of its state
over its history of changes.
class Student
{
//State fields
public string StudentNumber { get; private set; }
public string Name { get; private set; }
public List<string> Modules { get; private set; }
//Constructor
public Student(string studentNumber, string name)
{
this.StudentNumber = studentNumber;
this.Name = name;
Modules = new List<string>();
} //Constructor
//Manage modules
public void AddModule(string moduleCode) { Modules.Add(moduleCode); }
public void RemoveModule(string moduleCode) { Modules.Remove(moduleCode); }
public List<string> GetModules() { return Modules; }
//ToString
public override string ToString()
{
return StudentNumber + ", " + Name + ", [" + string.Join(", ", Modules) + "]";
}
Memento
The Memento class is nested inside the Originator class in the region as indicated above.
• The data fields are the same as for the Originator class. Together, they are referred to as
the saved state.
• The data fields are immutable. They cannot be changed after construction of the object.
• The constructor assigns values to the state fields.
• Note that SaveToMemento and RestoreFromMemento are methods of the Originator class.
• Note that we should not make shallow copies of fields with reference types. That would
mean that the saved state is just an alias for the originator's fields and will change if the
originator's state is changed.
//Memento constructor
public Memento(string studentNumber, string name, List<string> modules)
{
this.StudentNumber = studentNumber;
this.Name = name;
//this.Modules = modules; Wrong!!!
this.Modules = new List<string>(modules); //Must be deep copy.
} //Constructor
} //class Memento
Client
If we have a single object to be taken care of, we can use the client as the caretaker.
• The caretaker has a list of saved states. The list contains objects of the Memento class which
is nested inside Student.
• Every operation (AddModule, RemoveModule) must be preceded with a call to
SaveToMemento.
• In this example, there is no safety net in case the client enters an invalid index when restoring
from the memento's. It is left to you as an exercise.
class Client
{
static void Main(string[] args)
{
//Caretaker maintains a list of mementos
List<Student.Memento> savedStates = new List<Student.Memento>();
• Output
When we have a list of objects, each one with its own list of saved states, things can become
messy in the Client class. In any case, we do not want the client to take care of safe restores.
If we adapt the above example to make provision for more than one student, it would be better
to declare a separate caretaker class.
Caretaker class
• The caretaker class encapsulates the actual object for which we have to keep track of state.
• We use a Stack<Memento> to keep track of the history.
• Note that the current state must be pushed on the stack before a new operation is executed.
• The Restore method has a step parameter to indicate how far we must go back in history.
We can only pop from the stack if it is not empty.
• It is left as an exercise to also implement a redo stack.
• The history can quickly become very large. It is recommended to use a sliding window of
history so that, for example, only the last 10 versions are kept on the stack. You can try to
do that.
class ctStudent
{
//Private fields for the Originator and stack of mementos
private Student student;
private Stack<Student.Memento> history = new Stack<Student.Memento>();
//Constructor
public ctStudent(Student student) { this.student = student; }
//ToString
public override string ToString() { return student.ToString(); }
//Operations
public void AddModule(string module)
{
history.Push(student.SaveToMemento()); //Save current state before operation
student.Modules.Add(module);
}
Client
• The client does not take responsibility for maintaining lists of saved states.
• The client works with a list of caretakers of students (List<ctStudent>), not a list of
students (List<Student>).
• It would actually be better to create a container class for Students.
class Client
{
static void Main(string[] args)
{
//List of students
List<ctStudent> Students = new List<ctStudent>();
//New student with state
Student student = new Student("2020123456", "Johnny Mickelson");
Students.Add(new ctStudent(student));
//Add modules
Students[0].AddModule("CSIS2614"); Students[0].AddModule("CSIS2634");
Students[0].AddModule("CSIS2624"); Students[0].AddModule("CSIS2664");
Console.WriteLine("\t" + student); //2614, 2634, 2624, 2664
Class diagram
Note that not all these examples follow the exact same pattern.
• https://www.c-sharpcorner.com/UploadFile/dacca2/design-pattern-for-beginner-part-8-
memento-design-patter/
• https://www.dofactory.com/net/memento-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/memento-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/memento
• Project 8, 2021 (Text editor, version control)
• Exam 2, 2021, Question 6 (Text editor, version control)
• Project 8, 2023 (Sorting)
Advantages
• You can produce snapshots of the object’s state without violating its encapsulation.
• Through a history of snapshots, it is possible to restore an object to an earlier state.
• You can simplify the originator’s code by letting the caretaker maintain the history of the
originator’s state.
Disadvantages
• The app might consume lots of RAM if clients create mementos too often.
• Caretakers should track the originator’s lifecycle to be able to destroy obsolete mementos.
• Most dynamic programming languages, such as PHP, Python and JavaScript, can’t guarantee
that the state within the memento stays untouched.
Copyright: PJ Blignaut, 2022
39
• Use the Memento pattern when you want to produce snapshots of the object’s state to be
able to restore a previous state of the object.
The Memento pattern lets you make full copies of an object’s state, including private
fields, and store them separately from the object.
• Use the pattern when direct access to the object’s fields/getters/ setters violates its
encapsulation.
The Memento makes the object itself responsible for creating a snapshot of its state. No
other object can read the snapshot, making the original object’s state data safe and secure.
• https://en.wikipedia.org/wiki/Memento_pattern
• Cooper, Chapter 26, 441
• Gamma, Helm, Johnson, Vlissides (1995, p 365)
• Nesteruk (2019, p 244)
• Shvets, p 326
• Banas, D. https://www.youtube.com/watch?v=jOnxYT8Iaoo
8.1 Introduction
A null value is an indication of nothingness. It is not the same as an empty value. It means that
the compiler is aware of the variable, but no memory cell is allocated. Of the following three
initialisations, the first two are equivalent, but they are fundamentally different from the third:
string s;
string s = null;
string s = "";
A null object is also known as a stub, an active nothing or an active null. It is not nothing, but
an instance that does nothing. Instead of using a null reference to convey the absence or non-
existence of an object, the Null Object pattern uses an object which implements the expected
interface, but whose method bodies are empty. The advantage of this approach is that a null
object is very predictable and has no side effects: it does nothing.
With the Null Object pattern, a null object replaces the need to check for null. In other words,
instead of using null to reflect the notion of nothing, an instance is created with default
behaviour that does nothing. Note that this does not eliminate the null error due to careless
programming. Instead, it allows a careful programmer with an alternative to represent non-
existence.
8.3 Example
Abstract class
• The abstraction of animals contains all the common attributes and methods.
Concrete animals
• The derived classes implement the abstract class with specific details in the overridden
methods.
• A NullAnimal class is included that inherits from AAnimal in the same way that concrete
animals do with the exception that the method implementations have empty bodies.
• Note that the NullAnimal class is sealed. There is no point in inheriting from an object that
deliberately has no behaviour.
Client
Suppose the client would, for some or other reason, like to refer to a non-existent animal.
Consider the following three ways of initialising such non-existence with the respective
consequences:
3. Null object assignment - no error. The MakeSound method does nothing but does also not
give an error.
Since C# allows nested classes, we can declare a private NullAnimal class inside the abstract
class.
• Instantiating a null animal from the client will then be done like this:
It is not necessary to have an abstract class with several possible descendants. We can also
provide for a Null object for a simple class.
• It is important that the class developer specifies the behaviour of a Null object. In the
example below, the ToString() method is overridden to return an empty string.
class Car
{
//Properties
public string make { get; private set; }
public string model { get; private set; }
public string registration { get; private set; }
//Constructors
public Car() { }
public Car(string make, string model, string registration)
{
this.make = make;
this.model = model;
this.registration = registration;
}
//To String
public override string ToString()
{
return string.Join(", ", new string[] { make, model, registration });
}
Advantages
• Null objects can be used in place of real objects when the object is expected to do nothing.
• The Null Object pattern makes the client code simple. Clients can treat real objects and null
objects in the same way. This simplifies client code, because it avoids having to write testing
code which handles null objects specially.
• The behaviour of a null object is very predictable and has no side effects: it does nothing.
• The null object pattern helps us to write a clean code and avoid null checks.
• Using the Null Object pattern, callers do not have to care whether they have a null object or
a real object.
Disadvantages
• The Null Object pattern can be difficult to implement if various clients do not agree on how
the null object should do nothing if the abstraction is not well defined.
• To be consistent, application of the Null Object pattern can necessitate creation of a null
object class for every abstract class in the program.
• It is not possible to implement the Null Object pattern in every scenario. Sometimes, it is
necessary to work with a null reference and perform some null checks.
• Use the Null Object pattern to avoid frequent checks for null.
• Use the Null Object pattern if you want to return an object of the expected type, but do
nothing.
• https://www.c-sharpcorner.com/article/null-object-design-pattern/
• https://www.geeksforgeeks.org/null-object-design-pattern/
• https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm
• https://en.wikipedia.org/wiki/Null_object_pattern
• Nesteruk (2019, p 250)
9. Observer pattern
9.1 Introduction
The Observer pattern allows an object to notify other objects that something has happened. This
happens often in the real world. If, for example, somebody has made a payment into your bank
account, you are notified with an SMS.
This approach uses a Push mechanism for communication. The service object pushes a message
with the necessary details to all its subscribers whenever something has happened to it. This is
in contrast with a Pull approach where other objects have to request a service object for
information every now and then. With a Pull approach the requester must determine how often
it should ask. If it asks infrequently, it is possible that happenings may have a nasty consequence
if the requester does not respond quickly enough. If it asks too often, lots of processing power
might be wasted with unnecessary operations.
Because the Observer pattern is popular and necessary, the designers of C# incorporated the
pattern into the language with the use of the event keyword. Events can be members of a class
and are decorated with the event keyword. Event handlers are methods that are called whenever
an event is raised. Refer to Chapter 19 of the previous semester to revise the principles of events
and event handlers in C#.
Since we try to teach you how to program and not to use C#, it is important that you understand
what is happening behind the scenes with the Observer pattern.
1. The Publisher issues events of interest to other objects. These events occur when the
publisher changes its state or executes some behaviours. Publishers contain a subscription
infrastructure that lets new subscribers join and current subscribers leave the list.
When a new event happens, the publisher goes over the subscription list and calls the
notification method declared in the subscriber interface on each subscriber object.
2. The ISubscriber interface declares the notification interface. In most cases, it consists of a
single Update method. The method may have several parameters that let the publisher pass
some event details along with the update.
ISubscriber may optionally also declare Subscribe and Unsubscribe to allow the
subscriber to initiate the subscriptions. These methods will then call the corresponding
methods of the publisher with itself (this) as parameter.
Usually, subscribers need some contextual information to handle the update correctly. For
this reason, publishers often pass some context data as arguments of the notification method.
The publisher can pass itself as an argument, to allow subscribers to fetch any required data
directly.
4. The Client creates publisher and subscriber objects separately and then registers subscribers
for publisher updates.
9.3 Example
Share prices of a specific commodity fluctuates up and down. Consider the example of brokers
who buy shares of a specific entity when the share price is low and sell shares when the price
is high. Since the brokers do not watch the movements all the time (they have to sleep
sometimes), they have a program that watches the price on their behalf and warns them when
the price reaches a predefined threshold on either end.
Publisher
• The class PriceWatch acts as publisher. It publishes relevant events whenever the share
price exceeds the set limits.
• An infinite loop is used to simulate the process of watching the stock market prices and
notifying subscribers of low or high prices.
• The infinite loop must run in a separate thread or else it will block execution. You can
comment out the thread and see what happens.
• A random number generator is used to simulate a share price.
• Don't use a foreach to iterate through the subscribers during the notification process. The
list of subscribers might change during that period which will cause an error (see below).
Use either a normal for loop or Parallel.ForEach which runs every loop traversal in its
own thread.
class PriceWatch
{
//Thresholds to demarcate when events must be raised
public decimal lowThreshold { get; private set; }
public decimal highThreshold { get; private set; }
//List of subscribers
private List<ISubscriber> lstSubscribers;
//Constructor
public PriceWatch(decimal lowThreshold, decimal highThreshold)
{
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
lstSubscribers = new List<ISubscriber>();
} //Constructor
ISubscriber interface
• This interface defines two event handlers which must be implemented by every subscriber.
Concrete subscribers
• The constructor subscribes a broker automatically upon instantiation. This is not necessary
and can also be done explicitly by the client.
class Broker : ISubscriber
{
//Subscriber data fields
public string Name { get; private set; }
//Constructor
public Broker(string name, PriceWatch pw)
{
this.Name = name;
pw.Subscribe(this);
} //Constructor
Client
• The client instantiates the publisher, PriceWatch, and all the subscribers.
• The client also subscribes and unsubscribes brokers periodically.
class Client
{
static void Main(string[] args)
{
//Create publisher
PriceWatch pw = new PriceWatch(100, 900);
//Run price watch in separate thread, else the execution will block.
new Thread(delegate () { pw.WatchPrice(1000); }).Start();
//Define brokers
Isubscriber broker1 = new Broker("John", pw);
Isubscriber broker2 = new Broker("Mike", pw);
Isubscriber broker3 = new Broker("James", pw);
Class diagram
• https://www.c-sharpcorner.com/UploadFile/dacca2/design-pattern-for-beginner-part-10-
observer-design-patter/
• https://www.dofactory.com/net/observer-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/observer-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/observer
• Project 8 & Exam 2, 2020 (Buttons on a form)
• Exam 1, 2021, Question 6 (XYZ)
Advantages
• Open/Closed Principle. You can introduce new subscriber classes without having to change
the publisher’s code (and vice-versa if there is a publisher interface).
• You can establish relations between objects at runtime.
Disadvantages
• Use the Observer pattern when changes to the state of one object may require changing other
objects, and the actual set of objects is unknown beforehand or changes dynamically.
• Use the pattern when some objects in your app must observe others, but only for a limited
time or in specific cases.
10.1 Introduction
The state of an object refers to its appearance at a specific moment in time and is represented
by the values of its data fields at that time. The state of an object may change over time. The
State pattern is applicable when there are certain rules about the state or sequence of possible
states. It can also be used when an object must behave differently depending on its current state.
The context (object) does not always know the successor of every given state. The individual
states know what follows them and informs the context.
Example applications
• An elevator serving Floors 0-10, cannot move up beyond Floor 10 and it can never skip a
floor.
• A traffic light can only change colours in the sequence Red, Green, Yellow, Red, …. The
period of a certain state depends on the state, for example the duration of Yellow is much
shorter than that of Green or Red.
• The credit level of a Gold bank account is higher than that of a Silver account.
• A database connection can be open or closed. You cannot open an open connection and you
cannot close a closed connection. No queries against the database are possible if the
connection is closed.
• An academic paper can be in draft format, submitted, accepted, or published. A paper cannot
be accepted before it has been submitted, and it cannot be published before it is accepted.
• The buttons and switches on your smartphone behave differently depending on the current
state of the device:
- When the phone is locked, pressing any button leads to the unlock screen.
- When the phone is unlocked, pressing buttons leads to executing various functions.
- When the battery is low, pressing any button shows the charging screen.
The Context class refers to the object with varying state. There are different classes for every
state and the state property of the Context is assigned an instance of one of these classes. An
abstract class represents the states of the object. This class declares an interface that is common
to all classes that represent different operational states. Subclasses implement state-specific
behaviour. See the Sta01 (conceptual) example.
1. The Context current state is indicated by a reference to one of the concrete state objects.
The context knows there is a next state, but it does not always know what it is and asks the
current state what its successor is (context.GetNextState). The current state then informs
the context (state.NextState) through a parameter in a call to context.SetState. Now,
the context can change its state to the correct successor of the current state.
2. The State interface or abstract class declares the state-specific methods. State objects may
store a back-reference to the context object. Through this reference, the state can fetch any
required information from the context object, as well as initiate state transitions (inside
NextState). In other words, the current state object requests the context to replace itself
with another.
3. Concrete States provide their own implementations for the state-specific methods. Both
context and concrete states can set the next state of the context and perform the actual state
transition by replacing the state object that is linked to the context.
10.3 Example
Consider the example of the traffic light. It can only change colours in the sequence Red, Green,
Yellow, Red, …. The interval between transitions depends on the current state, for example the
duration of Yellow is much shorter than that of Green or Red.
Context
//Property
public Color colour => currentColour.colour;
//Constructor
public TrafficLight()
{
currentColour = new Red(this);
}
//State property
public Color colour;
//Context
protected TrafficLight t;
//Constructor
public AColour(TrafficLight trafficLight)
{
this.t = trafficLight; //Set context
}
} //class AColour
Concrete states
• The context of every instance is set in the abstract class. So, the concrete states send the
context through to the base class constructor.
• The state-specific properties and behaviour are set per state. In this case, the delay is set
specific to the colour.
Client
• For this example, the client instantiates the context and starts the infinite cycle of colour
changes. (We assume no load shedding!!)
class Client
{
static void Main(string[] args)
{
//Instantiate context
TrafficLight trafficLight = new TrafficLight();
Console.WriteLine("Colour is now " + trafficLight.colour.Name);
//Infinite loop
while (true)
{
trafficLight.NextColour();
Console.WriteLine("Colour is now " + trafficLight.colour.Name);
}
} //Main
}
Output
The output is generated from the client after every call to the context’s NextColour method.
Class diagram
• I added some extra dotted arrows to indicate the sequence of method calls. For the sake of
clarity, I entered every method in a separate box. This is non-standard UML.
• https://www.dofactory.com/net/state-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/state-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/state
• Project 9, 2021 (Elevator)
• Project 9, 2022 (Golf course)
• Project 9, 2023 (Production line)
Advantages
• Single Responsibility Principle. Organize the code related to particular states into separate
classes.
• Open/Closed Principle. Introduce new states without changing existing state classes or the
context.
• Simplify the code of the context by eliminating bulky state machine conditionals.
Disadvantages
• Applying the pattern can be overkill if a state machine has only a few states or rarely changes.
• Use the State pattern when you have an object that behaves differently depending on its
current state, the number of states is enormous, and the state-specific code changes
frequently. The pattern suggests that you extract all state-specific code into a set of distinct
classes. As a result, you can add new states or change existing ones independently of each
other, reducing the maintenance cost.
• Use the pattern when you have a class polluted with massive conditionals (if and while
statements) that alter how the class behaves according to the current values of the class’s
fields.
• Use State when you have a lot of duplicate code across similar states and transitions of a
condition-based state machine. The State pattern lets you compose hierarchies of state
classes and reduce duplication by extracting common code into abstract base classes.
11.1 Introduction
Design patterns are about limiting the need for change (Okhravi, 2017). If something
somewhere changes, it should not enforce a series of cascading changes. The principle of
decoupling things makes that possible.
The Strategy pattern is one of the easiest patterns to understand. If something can be done in
many ways, the strategy pattern extracts all of these algorithms into separate classes called
strategies. The original class, called context (or driver), must have a field for storing a reference
to one of the strategies. The context delegates the work to a linked strategy object instead of
executing it on its own.
The Strategy pattern consists of the specification of a common interface that needs to be
implemented by all the classes that belong to the same family or have similar purposes. The
interface exposes a single method for triggering the algorithm encapsulated within the selected
strategy. This way, the context becomes independent of concrete strategies, so you can add new
algorithms or modify existing ones without changing the code of the context or other strategies.
The context is not responsible for selecting an appropriate algorithm for the job. Instead, the
client passes the desired strategy to the context. The Strategy pattern allows us to change the
way of doing something at run time. The client is responsible for deciding which concrete class
needs to be instantiated based on parameters, conditions or business or technical requirements.
Consider the scenario of collections or lists of objects. We can have different types of lists, for
example static lists (using arrays) and .Net lists (using System.Collections.Generic.List<T>).
Refer to Chapter 21 of Semester 1 again. All lists must implement an interface with the typical
list operations such as Add and Remove and be able to return a sorted list of objects. The Sort
method in the diagram below is private and is called by the GetList method.
Now, suppose the client wants to have a choice of how the list must be sorted, for example
with a bubble sort or insertion sort or selection sort or not sorted.
Doing it wrong
The class diagram below shows one possibility of approaching the challenge.
This is clearly wrong. We have multiple classes doing the exact same thing. If we want to
change something in, for example, the bubble sort algorithm, we will have to do it twice. If we
want to add another type of list, e.g. a dynamic list using linked lists, we will have to add another
four sorting classes. If we want to add another sorting algorithm, e.g. MergeSort, we will have
to add it for all types of lists. The number of classes will grow exponentially with every addition.
A better approach
We can decouple the sorting strategy from the list implementation. We can now add list types
and sort strategies at will with the one having no effect on the other.
Figure 20. Sorting different types of lists in different ways following the Strategy pattern
This approach follows the principle of favouring composition over inheritance. The sort
strategy is now also a member (component) of the list interface, and it shows clearly that
inheritance is not the best way to facilitate code reuse.
1. The Context maintains a reference to one of the concrete strategies and communicates with
this object only via the strategy interface. The context calls the execution method on the
linked strategy object each time it needs to run the algorithm. The context doesn’t know what
type of strategy it works with or how the algorithm is executed.
2. The Strategy interface is common to all concrete strategies. It declares a method that is used
by the context to execute a strategy.
4. The Client creates a specific strategy object and passes it to the context.
Strategy interface
• The interface takes an array (unsorted) parameter and returns an array (sorted). The context
has to abide with this interface.
interface ISortStrategy<T>
{
T[] Sort(T[] list);
}
Concrete strategies
using System;
return tmpList;
} //Sort
} //class BubbleSort
} //class InsertionSort
Context
• For the purposes of the example, we limit the context below to a .Net list implementation.
An array implementation is available in the Visual Studio code example.
• We use a generic interface so that the list can be used for various data types.
• The interface is very limited for the purposes of this discussion. Of course, it should also
contain members such as Contains, RemoveAt, an indexer, an enumerator, etc.
• The class is named MyList to distinguish from System.Collections.Generic.List
which is the private underlying data store type.
• It is important that MyList limits T to IComparable. Else, CompareTo will not be available
to do the sorting. Include the System namespace.
• Note that the sortStrategy reference is private, but there is a public interface method
SetSortStrategy.
• The Sort method is also private and used in GetList.
• The Sort method has to abide with the IStrategy interface. Therefore, the list of items is
cast to an array and the return value is cast back to List<T>.
using System;
using System.Collections.Generic;
interface IList<T>
{
void Add(T item);
void Remove(T item);
T[] GetList();
void SetSortStrategy(ISortStrategy<T> sortStrategy);
} //IList
Client
• Note that the end user can determine the sort strategy during runtime.
class Client
{
static void Main(string[] args)
{
//Original list of names
IList<string> lstNames = new MyList<string>();
lstNames.Add("Samual"); lstNames.Add("Jimmy"); lstNames.Add("Sandra");
lstNames.Add("Vivek"); lstNames.Add("Anna"); lstNames.Add("Chris");
//Loop
char strategy;
do
{
//- Select sort strategy
Console.Write("\tSelect sort strategy: [B], [I], [S], [U], [X]: ");
strategy = Console.ReadKey().KeyChar.ToString().ToUpper()[0];
Console.WriteLine();
} //Main
} //class Client
Output
• https://code-maze.com/strategy/
• https://www.c-sharpcorner.com/UploadFile/dacca2/design-pattern-for-beginner-part-9-
strategy-design-pattern/
• https://www.dofactory.com/net/strategy-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/strategy-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/strategy
• Test 2, 2021, Question 5
• Project 9, 2022 (File strategy)
• Bridge, State, Strategy (and to some degree Adapter) have very similar structures. All these
patterns are based on composition, which is delegating work to other objects. However, they
all solve different problems.
• Command and Strategy may look similar because you can use both to parameterize an object
with some action. However, they have very different intents.
- You can use Command to convert any operation into an object. The operation’s
parameters become fields of that object.
- Strategy usually describes different ways of doing the same thing.
• Decorator lets you change the skin (interface) of an object, while Strategy lets you change
the guts (details inside).
• Template Method is based on inheritance: it lets you alter parts of an algorithm by extending
those parts in subclasses. Strategy is based on composition: you can alter parts of the object’s
behaviour by supplying it with different strategies that correspond to that behaviour.
Template Method works at the class level, so it is static. Strategy works on the object level,
letting you switch behaviours at runtime.
Advantages
Disadvantages
• If you only have a couple of algorithms and they rarely change, there is no real reason to
overcomplicate the program with new classes and interfaces that come along with the
pattern.
• Clients must be aware of the differences between strategies to be able to select a proper one.
• A lot of modern programming languages have functional type support that lets you
implement different versions of an algorithm inside a set of anonymous functions. Then you
could use these functions exactly as you’d have used the strategy objects, but without
bloating your code with extra classes and interfaces.
• Use the Strategy pattern when you want to use different variants of an algorithm within an
object and be able to switch from one algorithm to another during runtime.
• Use the pattern when you have a lot of similar classes that only differ in the way they execute
some behaviour.
• Use the pattern to isolate the business logic of a class from the implementation details of
algorithms that may not be as important in the context of that logic.
• Use the pattern when your class has a massive conditional operator that switches between
different variants of the same algorithm.
• Banas: https://www.youtube.com/watch?v=-NCgRD9-C6o
• Cardoso, p 231
• Cooper, Chapter 29, p 489
• Gamma, Helm, Johnson, Vlissides (1995, p 403)
• Martin & Martin (2006), Chapter 22, p 407
• Metz, S. 2015. https://www.youtube.com/watch?v=OMPfEXIlTVE
• Nesteruk (2019, p 291)
• Okhravi, C. 2017. Strategy Pattern.
https://www.youtube.com/watch?v=v9ejT8FO-
7I&list=PLrhzvIcii6GNjpARdnO4ueTUAVR9eMBpc&index=1
• Shvets, 369
12.1 Introduction
An algorithm may be written on a high level or low level. High level shows the basic steps only.
Low level gives more details with the basic steps.
The Template Method pattern is used to specify the high-level steps in a template method inside
an abstract class. These steps are just method calls to where the details are specified. The details
may be different for different implementations of the abstract class. So, some of the detail
methods can be implemented inside the abstract class as well, but some of them may be
implemented in sub-classes.
In short, Template Method defines the skeleton of an algorithm in an abstract base class but
allows subclasses to override specific steps.
Both Template Method and Strategy work with a family of related algorithms. The algorithms
all have the same basic steps, but the details of their implementations vary. Template Method
uses inheritance to solve the problem, whereas Strategy uses delegation.
1. The Abstract Class declares methods that act as steps of an algorithm, as well as the actual
template method which calls these methods in a specific order. The steps may either be
declared abstract or virtual or private. The details of the abstract steps must be specified in
the concrete classes.
2. Concrete Classes can override the virtual steps and must override the abstract steps. The
concrete classes may not override the template method.
Consider an MS Access database with two tables, one for categories of products and one for
products.
In order to display data from the tables through a Visual Studio application, we have to follow
a few steps:
If we have to get data from different tables, we will have to repeat the above steps.
Abstract class
• The abstract class contains the common code as well as abstract method declarations for
code that will be different for different tables.
• The template method, Run:
- contains the steps that is necessary to manage the database connection and return the data;
- is the only public method in the class;
- is used by a client to return the data.
using System.Data;
Concrete classes
• The concrete classes provide the specific implementations of the Select and GetData
methods as specified in the abstract class.
• The Select method uses a specific SQL query to fill a data set with data from the database.
• The GetData method steps through the rows in the data and populates a string array with
records, and then returns the array.
• Note that we do not do formatting of output in service classes.
using System.Data;
using System.Data.OleDb;
} //Select
Client
• The client instantiates instances of the concrete classes but refers to them as an instance of
the abstract class. Therefore, the client can call the template method of the abstract class for
both the Category and Product classes.
class Client
{
static void Main(string[] args)
{
//List categories
Console.WriteLine("\tCategories");
ADataAccessObject daoCategories = new Categories();
foreach (string record in daoCategories.Run())
Console.WriteLine("\t" + record);
Console.WriteLine();
//List products
Console.WriteLine("\tProducts");
ADataAccessObject daoProducts = new Products();
foreach (string record in daoProducts.Run())
Console.WriteLine("\t" + record);
Console.WriteLine();
Class diagram
12.4 Example 2: Combination of the Strategy pattern and the Template Method pattern
Consider the example for the Strategy pattern again. You would have noticed that there was
some repetition in the concrete strategies. All but the NoSort strategy had to make a deep copy
of the original list before it could do the sorting. Both the BubbleSort and SelectionSort
algorithms had a fragment of code where two values had to be swapped in memory space.
We can insert an abstract class into the hierarchy that inherits from the ISortStrategy
interface.
Abstract class
• We consider the Sort method in the ISortStrategy interface as the template method. This
method contains the steps for the sorting.
1. Make a deep copy of the incoming array.
2. Do the actual sorting.
• The actual sorting is different for the various sort strategies, so the DoSort method is
abstract and must be overridden in the subclasses for the specific sorting strategy
• The only public member is the template method.
• The Swap method is a helper method and will be called from the subclasses when needed.
//Helper method
protected void Swap(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
} //Swap
} //class ASorting
Concrete classes
• Compare the classes below with what we had for the Strategy pattern. The repetition for the
deep copy is gone and the details for the Swap method is done in the abstract class. So, all
that we have in the Sort classes are the differences between the sorting algorithms.
return list;
} //Sort
} //class BubbleSort
We are combining the Strategy and the Template method pattern. The context class in the
Strategy pattern serves as the client of the Template method pattern. The Sort method in the
Interface strategy is the template method and is called from the context.
• https://www.dofactory.com/net/template-method-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/template-method-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/template-method
Advantages
• You can let clients override only certain parts of a large algorithm, making them less affected
by changes that happen to other parts of the algorithm.
• You can pull the duplicate code into a superclass.
Disadvantages
• Use the Template Method pattern when you want to let clients extend only particular steps
of an algorithm, but not the whole algorithm or its structure.
• Use the pattern when you have several classes that contain almost identical algorithms with
some minor differences. As a result, you might need to modify all classes when the algorithm
changes.
13.1 Introduction
The Visitor pattern allows separation of algorithms from the objects on which they operate. So, the
algorithm is external to the class and operates on it from the outside to change the object's appearance
and possibly its behaviour.
As an analogy, you can think of the Visitor as a doctor who gives you an injection against pain. The
doctor is the visitor, you are the object, the injection is the operation, the medicine is the parameter
and the decreased pain represents a change in appearance. The doctor can visit more than one patient
(object) with the same operation. You, the object, can decide to accept the visitor or not.
The Visitor pattern suggests that you place new behaviour into a separate Visitor class instead of
integrating it into an existing class.
When you need to add a new method to a class or hierarchy of classes, you will potentially break the
existing code. With the Visitor pattern, the new method is added as an extra class without touching
any of the existing service code. All that the client does is to accept the new visitor on a specific
instance of the Element class. It can accept the same visitor for different elements (unlike the
command class where the command is coupled with a specific receiver).
1. The IVisitor interface declares visiting methods that can take concrete elements of an
object structure as arguments. Several overloaded versions of the Visit method may be
declared – each one aimed at another Element subclass.
2. Each Concrete Visitor should implement all Visit methods in the IVisitor interface. Any
data that the visitor needs can be passed to it through a parameter of the constructor.
3. The IElement interface declares a method for accepting visitors. This method should have
one parameter declared with the type of the visitor interface.
4. Each Concrete Element must implement the acceptance method. The purpose of this method
is to redirect the call to the proper visitor’s method.
5. The Client has references to IElement and IVisitor objects. It facilitates external
operations on elements by accepting visitors.
13.3 Example
Consider the example of a document that can be edited, saved to or read from file. In terms of
the Visitor pattern, the document is the element with Edit, Save and Read being visitors. You
can surely argue that there are easier ways to do this but remember that the Visitor pattern
separates the object from the operations. This means that we can now easily add operations
without changing the Document class or any of the existing operations – so we comply with
OCP.
Element interface
• The Element interface declares a single Accept method which takes the visitor as parameter.
interface IDocument
{
void Accept(IVisitor visitor);
}
Element
• The Document class declares the attributes of the element and implements the IDocument
interface with the Accept method.
• Note that the set accessor is public in order to allow the visitors to access it from outside.
This is a disadvantage of the Visitor pattern.
Visitor interface
• The IVisitor interface declares a single Visit method which takes the element as
parameter.
interface IVisitor
{
void Visit(Document doc); //, params object[] values);
} //IVisitor
Concrete visitors
• The concrete visitors include all operations that must be done on the document. All concrete
visitors must implement the IVisitor interface with the details of the specific operation.
• Any data that the visitor needs can be passed to it through a parameter of the constructor.
class EditVisitor : IVisitor
{
private string text = "";
public EditVisitor(string text) { this.text = text; }
public void Visit(Document doc) { doc.text += text; }
} //class EditVisitor
Client
class Client
{
static void Main(string[] args)
{
//Element that can be visited
IDocument doc = new Document();
//Visitors
//- Edit document
doc.Accept(new EditVisitor("\nThis is example text."));
doc.Accept(new EditVisitor("\nSecond line of text."));
//Wait
Console.Write("\n\tPress any key to exit ...");
Console.ReadKey();
} //Main
} //class Client
• Check that the file Example.txt is created with the correct content.
Class diagram
• https://www.dofactory.com/net/visitor-design-pattern
• https://www.dotnettricks.com/learn/designpatterns/visitor-design-pattern-c-sharp
• https://refactoring.guru/design-patterns/visitor
• Project 10, 2021
• Test 3, 2021, Question 5
• Project 10, 2022
• Test 2, 2022, Question 7
• Project 10, 2023 (Queues)
Advantages
• Open/Closed Principle. You can introduce a new behaviour that can work with objects of
different classes without having to change these classes.
• Single Responsibility Principle. You can move multiple versions of the same behaviour into
the same class.
• A visitor object can accumulate some useful information while working with various objects.
This might be handy when you want to traverse some complex object structure, such as an
object tree, and apply the visitor to each object of this structure.
Disadvantages
• You need to update all visitors each time a class gets added to or removed from the element
hierarchy.
• Visitors need access to data fields in element classes and therefore these data fields have to
be declared as public.
• Visitors might lack the necessary access to the private fields and methods of the elements
that they are supposed to work with.
• Use the Visitor when you need to perform an operation on all elements of a complex object
structure (for example, an object tree). The Visitor pattern lets you execute an operation over
a set of objects with different classes by having a visitor object implement several variants
of the same operation, which correspond to all target classes. See example of calculating
area of different types of shapes.
• Use the Visitor to clean up the business logic of auxiliary behaviours. The pattern lets you
make the primary classes of your app more focused on their main jobs by extracting all other
behaviours into a set of visitor classes.
• Use the pattern when a behaviour makes sense only in some classes of a class hierarchy, but
not in others. You can extract this behaviour into a separate visitor class and implement only
those visiting methods that accept objects of relevant classes, leaving the rest empty.
• Chain of Responsibility allows passing of requests along a chain of handlers. Upon receiving
a request, each handler decides either to process the request or to pass it to the next handler in
the chain.
• The Command pattern turns a request into a stand-alone object that contains all information
about the request. This transformation allows parameterization of methods.
• The Iterator traverses elements of a collection without exposing its underlying representation.
• The Mediator reduces chaotic dependencies between objects. The pattern restricts direct
communications between the objects and forces them to collaborate only via a mediator
object.
• The Memento saves and restores the previous state of an object without revealing the details
of its implementation.
• The Null Object pattern uses an object which implements the expected interface, but whose
method bodies are empty.
• The Observer facilitates a subscription mechanism to notify multiple objects about any events
that happen to the object that they are observing.
• The State pattern allows an object to alter its behaviour when its internal state changes. It
appears as though the object changed its class.
• The Strategy pattern puts each one of a family of algorithms into a separate class and make
their objects interchangeable.
• The Template method pattern defines the skeleton of an algorithm in the superclass but let
subclasses override specific steps of the algorithm without changing its structure.
• The Visitor pattern separates algorithms from the objects on which they operate.
Compare:
- Visitor: Every visitor does something differently.
Overloaded Visit methods are possible with different types of elements.
Client: element.Accept(visitor)
- Strategy: Every class does the same thing but in a different way.
• Cardoso, A. 2019. Implementing Design Patterns in C# and .NET 5: Build Scalable, Fast,
and Reliable .NET Applications Using the Most Common Design Patterns. English Edition.
BPB Publications. Kindle Edition.
• Cooper, J.W. 2002. C# Design Patterns: A Tutorial, Addison Wesley
• Freeman, E., Freeman, E., Bates, B., Sierra, K. 2004. Head First Design Patterns. O'Reilly.
• Gamma, E., Helm, R., Johnson, R., Vlissides, J. 1994. Design Patterns: Elements of
Reusable Object-Oriented Software. Addison- Wesley.
• Martin, R.C. 2000. Design Principles and Patterns.
https://fi.ort.edu.uy/innovaportal/file/2032/1/design_principles.pdf
• Martin, R.C., Martin, M. 2006. Agile Principles, Patterns, and Practices in C#. Prentice Hall.
• Nesteruk, D. 2019. Design Patterns in .NET. Apress. Kindle Edition.
• Ohkravi, C. 2017. Design patterns.
https://www.youtube.com/playlist?list=PLrhzvIcii6GNjpARdnO4ueTUAVR9eMBpc
• Shvets, A. 2020. Dive into Design Patterns. https://refactoring.guru/design-patterns/book.
Iterator Mediator
Observer State
Visitor