[go: up one dir, main page]

0% found this document useful (0 votes)
60 views106 pages

SWD392 - Design Patterns - ManhNC5

alo

Uploaded by

xinchao2342008
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views106 pages

SWD392 - Design Patterns - ManhNC5

alo

Uploaded by

xinchao2342008
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 106

Design Patterns: A Comprehensive Overview

Design Patterns are reusable solutions to commonly occurring problems in software


design. They are blueprints that provide a general approach to solving specific design problems,
promoting code reusability, maintainability, and flexibility.

I. Design Patterns: A Categorized Definition Guide


1.1. Creational Patterns: These patterns deal with object creation mechanisms,
trying to create objects in a manner suitable to the situation.
1. Abstract Factory: Provides an interface for creating families of related or dependent
objects without specifying their concrete classes. This pattern lets you produce families of
products without having to specify their concrete classes, promoting flexibility and
maintainability.

2. Builder: Separates the construction of a complex object from its representation so that
the same construction process can create different representations. This pattern enables
the step-by-step creation of complex objects, offering flexibility in constructing various
representations.

3. Factory Method: Defines an interface for creating objects, but lets subclasses decide
which class to instantiate. This pattern delegates object creation to subclasses, providing
flexibility in choosing the type of object to be created.

4. Prototype: Specifies the kinds of objects to create using a prototypical instance, which is
cloned to produce new objects. This pattern allows for the creation of new objects by
copying existing objects, avoiding expensive initialization or complex setup.

5. Singleton: Ensures that a class has only one instance and provides a global point of
access to it. This pattern ensures the existence of only one instance of a class and
provides a global access point to that instance.
1.2. Structural Patterns: These patterns are concerned with how classes and
objects are composed to form larger structures.
6. Adapter: Converts the interface of a class into another interface clients expect. This
pattern lets classes with incompatible interfaces work together, allowing for code reuse
without modifying existing code.

7. Bridge: Decouples an abstraction from its implementation so that the two can vary
independently. This pattern separates the abstraction from its implementation, allowing
for independent variations and flexibility.

8. Composite: Composes objects into tree structures to represent part-whole hierarchies.


This pattern enables the uniform treatment of individual objects and compositions of
objects, simplifying client code interaction.

9. Decorator: Attaches additional responsibilities to an object dynamically. This pattern


allows for dynamic addition of responsibilities to an object at runtime, enhancing
flexibility and avoiding subclass explosion.

10. Facade: Provides a simplified interface to a complex subsystem. This pattern hides the
complexities of a subsystem behind a simpler interface, making it easier to use and
understand.

11. Flyweight: Shares objects to support large numbers of fine-grained objects efficiently.
This pattern minimizes memory usage by sharing common state among multiple objects,
especially beneficial for large numbers of similar objects.

12. Proxy: Provides a surrogate or placeholder for another object to control access to it. This
pattern controls access to an object, offering benefits like lazy initialization, caching, or
security checks.

1.3. Behavioral Patterns: These patterns are concerned with the assignment of
responsibilities between objects and how they communicate.
13. Chain of Responsibility: Avoids coupling the sender of a request to its receiver by
giving multiple objects a chance to handle the request. This pattern decoupled the sender
and receiver of a request, enabling multiple objects to potentially handle it.
14. Command: Encapsulates a request as an object, thereby letting you parameterize clients
with different requests, queue or log requests, and support undoable operations. This
pattern represents a request as an object, facilitating parameterization, queuing, logging,
and undo/redo functionalities.

15. Interpreter: Given a language, defines a representation for its grammar along with an
interpreter that uses the representation to interpret sentences in the language. This pattern
defines a grammatical representation for a language and provides an interpreter to process
it, commonly used for parsing expressions and queries.

16. Iterator: Provides a way to access the elements of an aggregate object sequentially
without exposing its underlying representation. This pattern allows for sequential access
to elements of an aggregate object without exposing its internal structure, offering a
consistent iteration method.

17. Mediator: Defines an object that encapsulates how a set of objects interact. This pattern
reduces dependencies between objects by introducing a mediator to manage their
communication, promoting loose coupling.

18. Memento: Without violating encapsulation, captures and externalizes an object's internal
state so that the object can be restored to this state later. This pattern saves and restores an
object's internal state without exposing its structure, facilitating undo/redo functionalities
and state persistence.

19. Observer: Defines a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated automatically. This pattern
notifies multiple objects (observers) about changes in a subject object, promoting loose
coupling and flexible event handling.

20. State: Allows an object to alter its behavior when its internal state changes. This pattern
encapsulates different states of an object as separate classes, enabling smooth transitions
between states.

21. Strategy: Defines a family of algorithms, encapsulates each one, and makes them
interchangeable. This pattern allows for dynamic selection and use of different algorithms
at runtime, promoting flexibility and extensibility.
22. Template Method: Defines the skeleton of an algorithm in an operation, deferring some
steps to subclasses. This pattern defines the overall structure of an algorithm in a base
class, permitting subclasses to override specific steps, promoting code reuse and
consistency.

23. Visitor: Represents an operation to be performed on the elements of an object structure.


This pattern adds new operations to a class hierarchy without modifying the classes
themselves, enhancing flexibility by separating the operation from the object structure.

II. Design Patterns: Explained by Category


II.1. Creational Patterns
These patterns focus on flexible object creation mechanisms, avoiding hardcoding instantiation and
allowing for better control over how objects are created.

1. Abstract Factory
 Purpose: Provides an interface for creating families of related or dependent objects without
specifying their concrete classes.

 What it is: A creational pattern that allows you to produce families of products (objects) without
having to specify their concrete classes, promoting flexibility and maintainability.

 Benefits:

o Improved flexibility and maintainability by abstracting the object creation process.

o Promotes consistency within families of objects.

o Hides complex creation logic from the client.

 When to use it:

o When you need to create families of related objects without knowing their concrete
classes in advance.

o When you want to enforce consistency in object creation across the application.

 How to use it:

1. Define an abstract factory interface with methods for creating each product type.

2. Create concrete factory classes that implement the abstract factory interface, each responsible for
creating a specific family of products.
3. The client code uses the abstract factory interface to create objects, unaware of the concrete
factory being used.

 Real-world example: Creating different UI elements (buttons, text boxes, etc.) for different
operating systems (Windows, macOS, Linux) using a single abstract factory.

 C# Code Example:

// Abstract Factory interface

public interface IGUIFactory

IButton CreateButton();

ITextBox CreateTextBox();

// Concrete Factory for Windows

public class WindowsGUIFactory : IGUIFactory

public IButton CreateButton()

return new WindowsButton();

public ITextBox CreateTextBox()

return new WindowsTextBox();

}
// Concrete Factory for macOS

public class MacGUIFactory : IGUIFactory

public IButton CreateButton()

return new MacButton();

public ITextBox CreateTextBox()

return new MacTextBox();

// Product interfaces

public interface IButton

void Render();

public interface ITextBox

void Render();

}
// Concrete product classes (Windows)

public class WindowsButton : IButton

public void Render()

Console.WriteLine("Rendering a Windows button.");

public class WindowsTextBox : ITextBox

public void Render()

Console.WriteLine("Rendering a Windows text box.");

// Concrete product classes (macOS)

public class MacButton : IButton

public void Render()

Console.WriteLine("Rendering a macOS button.");

}
public class MacTextBox : ITextBox

public void Render()

Console.WriteLine("Rendering a macOS text box.");

// Client code

public class Client

public static void Main(string[] args)

// Get the appropriate factory based on the current OS

IGUIFactory factory = GetFactoryForOS();

// Create UI elements using the factory

IButton button = factory.CreateButton();

ITextBox textBox = factory.CreateTextBox();

button.Render();

textBox.Render();

}
private static IGUIFactory GetFactoryForOS()

// Logic to determine the current operating system

// and return the appropriate factory

if (Environment.OSVersion.Platform == PlatformID.Win32NT)

return new WindowsGUIFactory();

else if (Environment.OSVersion.Platform == PlatformID.MacOSX)

return new MacGUIFactory();

else

throw new Exception("Unsupported operating system.");

Explanation: This example demonstrates how an Abstract Factory (IGUIFactory) is used to create
different UI elements based on the operating system. The client code only interacts with the abstract
factory and product interfaces, making it independent of the concrete classes.

2. Builder
 Purpose: Separates the construction of a complex object from its representation so that the same
construction process can create different representations.

 What it is: A creational pattern that enables the step-by-step creation of complex objects, offering
flexibility in constructing various representations.
 Benefits:

o Improved code readability and maintainability by encapsulating the object creation


process.

o Offers fine-grained control over object construction.

o Allows for the creation of different variations of an object using the same builder.

 When to use it:

o When the construction of an object involves multiple steps and potential variations.

o When you want to hide the internal representation of the object from the client.

 How to use it:

1. Define a builder interface with methods for each construction step.

2. Create concrete builder classes that implement the builder interface, each responsible for building
a specific variation of the object.

3. Create a director class that coordinates the building process by calling the builder methods in a
specific order.

4. The client code uses the director and a concrete builder to construct the desired object.

 Real-world example: Building a house with various configurations (number of floors, types of
rooms, materials, etc.) using a single builder.

 C# Code Example:

// Product class

public class House

public int Floors { get; set; }

public string Walls { get; set; }

public string Roof { get; set; }

public override string ToString()


{

return $"House: Floors - {Floors}, Walls - {Walls}, Roof - {Roof}";

// Builder interface

public interface IHouseBuilder

void Reset();

void BuildFloors(int floors);

void BuildWalls(string material);

void BuildRoof(string type);

House GetHouse();

// Concrete Builder for a Wooden House

public class WoodenHouseBuilder : IHouseBuilder

private House _house = new House();

public void Reset()

_house = new House();

}
public void BuildFloors(int floors)

_house.Floors = floors;

public void BuildWalls(string material)

_house.Walls = $"Wooden {material}";

public void BuildRoof(string type)

_house.Roof = $"Wooden {type}";

public House GetHouse()

return _house;

// Concrete Builder for a Brick House

public class BrickHouseBuilder : IHouseBuilder

private House _house = new House();


public void Reset()

_house = new House();

public void BuildFloors(int floors)

_house.Floors = floors;

public void BuildWalls(string material)

_house.Walls = $"Brick {material}";

public void BuildRoof(string type)

_house.Roof = $"Brick {type}";

public House GetHouse()

return _house;

}
}

// Director class

public class HouseConstructionDirector

public void Construct(IHouseBuilder builder, int floors, string walls, string roof)

builder.Reset();

builder.BuildFloors(floors);

builder.BuildWalls(walls);

builder.BuildRoof(roof);

// Client code

public class Client

public static void Main(string[] args)

// Create a director and builders

HouseConstructionDirector director = new HouseConstructionDirector();

IHouseBuilder woodenBuilder = new WoodenHouseBuilder();

IHouseBuilder brickBuilder = new BrickHouseBuilder();

// Construct a wooden house


director.Construct(woodenBuilder, 2, "Panel", "Gable");

House woodenHouse = woodenBuilder.GetHouse();

Console.WriteLine(woodenHouse);

// Construct a brick house

director.Construct(brickBuilder, 3, "Clay", "Flat");

House brickHouse = brickBuilder.GetHouse();

Console.WriteLine(brickHouse);

Explanation: This example demonstrates how the Builder pattern allows for different variations of a
House to be built using the same director and different builders. The director orchestrates the construction
process, and the concrete builders define the specific details of each variation.

3. Factory Method
 Purpose: Defines an interface for creating objects, but lets subclasses decide which class to
instantiate.

 What it is: A creational pattern that delegates object creation to subclasses, providing flexibility
in choosing the type of object to be created.

 Benefits:

o Decouples the client code from concrete product classes, allowing for new product types
to be added without modifying client code.

o Provides a hook for subclasses to customize the object creation process.

 When to use it:

o When you want to allow subclasses to control the type of objects created.

o When you want to decouple the client code from concrete product classes, making it
easier to add new product types later.
 How to use it:

1. Define a creator class with an abstract factory method that returns an object of a product type.

2. Create concrete creator subclasses that implement the factory method, each returning a specific
type of product.

3. The client code uses the creator class to create objects, unaware of the concrete creator being
used.

 Real-world example: Creating different types of documents (PDF, Word, Text) using a factory
method that delegates the creation to specific document creator classes.

 C# Code Example:

// Product interface

public interface IDocument

void Open();

void Save();

// Concrete product classes

public class PDFDocument : IDocument

public void Open()

Console.WriteLine("Opening PDF document...");

public void Save()

{
Console.WriteLine("Saving PDF document...");

public class WordDocument : IDocument

public void Open()

Console.WriteLine("Opening Word document...");

public void Save()

Console.WriteLine("Saving Word document...");

// Creator class with abstract factory method

public abstract class DocumentCreator

public abstract IDocument CreateDocument();

public void ManageDocument()

// Create the document


IDocument document = CreateDocument();

// Perform common operations on the document

document.Open();

document.Save();

// Concrete creator classes

public class PDFCreator : DocumentCreator

public override IDocument CreateDocument()

return new PDFDocument();

public class WordCreator : DocumentCreator

public override IDocument CreateDocument()

return new WordDocument();

}
// Client code

public class Client

public static void Main(string[] args)

// Create a PDF creator

DocumentCreator pdfCreator = new PDFCreator();

pdfCreator.ManageDocument(); // Creates and manages a PDF document

// Create a Word creator

DocumentCreator wordCreator = new WordCreator();

wordCreator.ManageDocument(); // Creates and manages a Word document

Explanation: This example shows how the DocumentCreator class defines an abstract CreateDocument
method, which is implemented by concrete creator subclasses (PDFCreator and WordCreator) to return
specific document types.

4. Prototype
 Purpose: Specifies the kinds of objects to create using a prototypical instance, which is cloned to
produce new objects.

 What it is: A creational pattern that allows for the creation of new objects by copying existing
objects (prototypes), avoiding expensive initialization or complex setup.

 Benefits:

o Improved performance when creating new objects is expensive or requires specific


configurations.

o Provides a way to create new objects without knowing their concrete classes.

o Flexibility in adding new object types without modifying existing code.


 When to use it:

o When the cost of creating a new object is high and you need multiple instances with
similar configurations.

o When the type of objects to be created is determined at runtime.

 How to use it:

1. Define a prototype interface with a Clone method.

2. Create concrete prototype classes that implement the prototype interface and override the Clone
method to create a copy of themselves.

3. The client code uses the prototype object to create new objects by calling the Clone method.

 Real-world example: Creating new game characters by cloning a prototype character object,
allowing for different attributes and abilities.

 C# Code Example:

// Prototype interface

public interface IPrototype

IPrototype Clone();

// Concrete prototype class

public class Monster : IPrototype

public string Name { get; set; }

public int Health { get; set; }

public int Attack { get; set; }

public Monster(string name, int health, int attack)


{

Name = name;

Health = health;

Attack = attack;

public IPrototype Clone()

// Shallow copy

return (IPrototype)this.MemberwiseClone();

// Client code

public class Client

public static void Main(string[] args)

// Create a prototype monster

Monster prototypeMonster = new Monster("Goblin", 100, 10);

// Clone the prototype to create new monsters

Monster monster1 = (Monster)prototypeMonster.Clone();

Monster monster2 = (Monster)prototypeMonster.Clone();


// Modify the cloned monsters

monster1.Name = "Orc";

monster2.Health = 150;

// Print the monster information

Console.WriteLine($"Prototype Monster: {prototypeMonster.Name}, {prototypeMonster.Health},


{prototypeMonster.Attack}");

Console.WriteLine($"Monster 1: {monster1.Name}, {monster1.Health}, {monster1.Attack}");

Console.WriteLine($"Monster 2: {monster2.Name}, {monster2.Health}, {monster2.Attack}");

Explanation: This example shows how a Monster prototype can be cloned to create new monsters with
similar initial characteristics. The client code can then modify the properties of the cloned monsters as
needed.

5. Singleton
 Purpose: Ensures that a class has only one instance and provides a global point of access to it.

 What it is: A creational pattern that restricts the instantiation of a class to a single instance and
provides a global access point to that instance.

 Benefits:

o Provides a centralized point of access to a shared resource, simplifying resource


management and ensuring consistency.

o Avoids unnecessary object creation, saving memory and resources.

o Allows for controlled access to the single instance.

 When to use it:

o When you need a single, globally accessible instance of a class (e.g., a logger, database
connection, configuration manager).
o When you need to control access to a shared resource.

 How to use it:

1. Make the constructor of the class private to prevent direct instantiation.

2. Create a static instance of the class within the class itself.

3. Provide a static method (usually called GetInstance) that returns the single instance. This method
should create the instance if it doesn't exist and return the existing instance if it does.

 Real-world example: Creating a logger class that has only one instance, allowing all parts of the
application to log messages to the same file.

 C# Code Example:

public sealed class Logger

private static Logger _instance = null;

private static readonly object padlock = new object();

// Private constructor to prevent direct instantiation

private Logger()

// Static method to get the single instance

public static Logger GetInstance()

// Use double-checked locking for thread safety

if (_instance == null)

lock (padlock)
{

if (_instance == null)

_instance = new Logger();

return _instance;

// Log message method

public void Log(string message)

Console.WriteLine($"Log: {message}");

// Write the message to a file or other logging destination

// Client code

public class Client

public static void Main(string[] args)

// Get the single instance of the logger

Logger logger = Logger.GetInstance();


// Log messages

logger.Log("This is the first message.");

logger.Log("This is the second message.");

content_copy Use code -----------.C#

Explanation: The Logger class has a private constructor and a static GetInstance method that returns the
single instance of the logger. The lock statement ensures that only one instance is created even in multi-
threaded environments.

Excellent, let's continue our journey through Design Patterns!

II.2. Structural Patterns


These patterns focus on how to assemble classes and objects into larger structures, promoting flexibility
and efficiency in composing systems.

6. Adapter
 Purpose: Converts the interface of a class into another interface clients expect.

 What it is: A structural pattern that acts as a bridge between two incompatible interfaces. It
allows classes with incompatible interfaces to work together by wrapping the "adaptee" and
providing a target interface.

 Benefits:

o Promotes code reusability by enabling the use of existing classes with incompatible
interfaces.

o Improves flexibility by decoupling the client code from the concrete implementation of
the adaptee.

 When to use it:

o When you want to use an existing class, but its interface doesn't match the interface
required by the client.
o When you want to create a reusable component that can be adapted to work with different
classes.

 How to use it:

1. Define a target interface that the client expects.

2. Create an adapter class that implements the target interface and contains an instance of the
adaptee.

3. Implement the target interface methods in the adapter, delegating the calls to the appropriate
methods of the adaptee.

 Real-world example: Imagine you have a legacy system that uses an old data format. You want
to integrate it with a new system that uses a different format. An adapter can be used to bridge the
gap between the two systems, allowing them to exchange data seamlessly.

 C# Code Example:

// Target interface (expected by the client)

public interface IModernPaymentGateway

void ProcessPayment(decimal amount);

// Adaptee (legacy payment system)

public class LegacyPaymentSystem

public void Pay(int amountInCents)

Console.WriteLine($"Processing payment of {amountInCents} cents using Legacy System.");

}
// Adapter

public class LegacyPaymentAdapter : IModernPaymentGateway

private LegacyPaymentSystem _legacySystem;

public LegacyPaymentAdapter(LegacyPaymentSystem legacySystem)

_legacySystem = legacySystem;

public void ProcessPayment(decimal amount)

// Convert decimal amount to cents for the legacy system

int amountInCents = (int)(amount * 100);

_legacySystem.Pay(amountInCents);

// Client code

public class Client

public static void Main(string[] args)

// Using the legacy payment system through the adapter

LegacyPaymentSystem legacySystem = new LegacyPaymentSystem();


IModernPaymentGateway adapter = new LegacyPaymentAdapter(legacySystem);

adapter.ProcessPayment(12.50m); // Output: Processing payment of 1250 cents using Legacy


System.

Explanation: This example shows how the LegacyPaymentAdapter class adapts the
LegacyPaymentSystem (adaptee) to the IModernPaymentGateway (target) interface, enabling the client to
use the legacy system seamlessly.

7. Bridge
 Purpose: Decouples an abstraction from its implementation so that the two can vary
independently.

 What it is: A structural pattern that separates an abstraction from its implementation, allowing
them to vary independently. This promotes flexibility by providing a bridge between the
abstraction and the implementation.

 Benefits:

o Increased flexibility by allowing both the abstraction and implementation to change


independently.

o Improved code organization and maintainability.

o Reduces complexity by separating concerns.

 When to use it:

o When you want to avoid a permanent binding between an abstraction and its
implementation.

o When both the abstraction and implementation should be extensible by subclassing.

o When you want to share an implementation among multiple objects.

 How to use it:

1. Define an abstraction interface and a separate implementation interface.


2. Create concrete abstraction classes that implement the abstraction interface and use an instance of
the implementation interface.

3. Create concrete implementation classes that implement the implementation interface.

 Real-world example: Imagine you have a remote control (abstraction) that can control different
devices (implementations) like TV, DVD player, and Music System. The bridge pattern allows
you to add new devices without modifying the remote control code.

 C# Code Example:

// Abstraction

public interface IRemoteControl

void TurnOn();

void TurnOff();

void SetChannel(int channel);

// Refined Abstraction

public class AdvancedRemoteControl : IRemoteControl

private IDevice _device;

public AdvancedRemoteControl(IDevice device)

_device = device;

public void TurnOn()


{

_device.TurnOn();

public void TurnOff()

_device.TurnOff();

public void SetChannel(int channel)

_device.SetChannel(channel);

// Additional methods for the advanced remote

public void Mute()

_device.Mute();

// Implementor

public interface IDevice

void TurnOn();
void TurnOff();

void SetChannel(int channel);

void Mute();

// Concrete Implementors

public class TV : IDevice

public void TurnOn()

Console.WriteLine("Turning on the TV.");

public void TurnOff()

Console.WriteLine("Turning off the TV.");

public void SetChannel(int channel)

Console.WriteLine($"Setting TV channel to {channel}.");

public void Mute()

{
Console.WriteLine("Muting the TV.");

public class DVDPlayer : IDevice

public void TurnOn()

Console.WriteLine("Turning on the DVD Player.");

public void TurnOff()

Console.WriteLine("Turning off the DVD Player.");

public void SetChannel(int channel)

Console.WriteLine("DVD Player does not have channels.");

public void Mute()

Console.WriteLine("Muting the DVD Player.");

}
}

// Client code

public class Client

public static void Main(string[] args)

// Create a TV and a remote control

IDevice tv = new TV();

IRemoteControl remote = new AdvancedRemoteControl(tv);

remote.TurnOn();

remote.SetChannel(10);

((AdvancedRemoteControl)remote).Mute();

// Create a DVD player and a remote control

IDevice dvdPlayer = new DVDPlayer();

remote = new AdvancedRemoteControl(dvdPlayer);

remote.TurnOn();

remote.SetChannel(5); // Output: DVD Player does not have channels.

Explanation: This example shows how different types of devices (TV and DVDPlayer) can be controlled
using a RemoteControl abstraction, and how new devices can be added without modifying the remote
control code.
8. Composite
 Purpose: Composes objects into tree structures to represent part-whole hierarchies.

 What it is: A structural pattern that lets you treat individual objects and compositions of objects
uniformly, allowing you to build complex hierarchies where both individual objects (leaves) and
groups of objects (composites) can be treated the same.

 Benefits:

o Simplifies the client code by providing a unified way to interact with both individual
objects and groups of objects.

o Makes it easy to add or remove components in the hierarchy without affecting the client
code.

o Promotes flexibility and extensibility by allowing for dynamic changes in the structure.

 When to use it:

o When you want to represent part-whole hierarchies of objects.

o When you want to treat individual objects and compositions of objects uniformly.

 How to use it:

1. Define a component interface that declares common operations for both leaf objects and
composite objects.

2. Create concrete leaf classes that implement the component interface for individual objects.

3. Create a composite class that also implements the component interface and contains a collection
of child components.

 Real-world example: Think of a file system structure: directories (composites) can contain files
(leaves) and other directories, allowing you to navigate and manage the entire structure as a single
entity.

 C# Code Example:

// Component interface

public interface IFileComponent

string Name { get; set; }


void Display();

// Leaf class (representing a file)

public class File : IFileComponent

public string Name { get; set; }

public File(string name)

Name = name;

public void Display()

Console.WriteLine($"File: {Name}");

// Composite class (representing a directory)

public class Directory : IFileComponent

public string Name { get; set; }

private List<IFileComponent> _children = new List<IFileComponent>();

public Directory(string name)


{

Name = name;

public void Add(IFileComponent component)

_children.Add(component);

public void Remove(IFileComponent component)

_children.Remove(component);

public void Display()

Console.WriteLine($"Directory: {Name}");

foreach (IFileComponent component in _children)

component.Display();

// Client code
public class Client

public static void Main(string[] args)

// Create a file system structure

Directory root = new Directory("Root");

Directory documents = new Directory("Documents");

Directory pictures = new Directory("Pictures");

File file1 = new File("resume.doc");

File file2 = new File("report.pdf");

File file3 = new File("vacation.jpg");

File file4 = new File("portrait.png");

root.Add(documents);

root.Add(pictures);

documents.Add(file1);

documents.Add(file2);

pictures.Add(file3);

pictures.Add(file4);

// Display the file system structure

root.Display();

}
Explanation: This example demonstrates how a file system can be represented using the Composite
pattern, allowing for the uniform treatment of files and directories. The Display method recursively
traverses the hierarchy and displays the contents.

9. Decorator
 Purpose: Attaches additional responsibilities to an object dynamically.

 What it is: A structural pattern that lets you add new functionalities to an object dynamically
without altering its structure. Decorators provide a flexible alternative to subclassing for
extending functionality.

 Benefits:

o Provides flexibility in adding and removing responsibilities at runtime.

o Avoids subclass explosion by allowing for dynamic composition of functionalities.

o Promotes code reusability by allowing decorators to be applied to different objects.

 When to use it:

o When you want to add responsibilities to individual objects dynamically, without


affecting other objects of the same class.

o When you want to extend functionality without creating a large number of subclasses.

 How to use it:

1. Define a component interface that declares common operations for both the concrete component
and decorators.

2. Create a concrete component class that implements the component interface and provides the
basic functionality.

3. Create decorator classes that implement the component interface and contain a reference to a
component object. Decorators add their own behavior before or after delegating to the component.

 Real-world example: Imagine a coffee shop where you can add various toppings (decorators) to
a basic coffee (component) like whipped cream, chocolate sprinkles, or caramel. Each topping
adds its cost and description to the coffee.

 C# Code Example:
// Component interface

public interface ICoffee

string GetDescription();

decimal GetCost();

// Concrete component (basic coffee)

public class SimpleCoffee : ICoffee

public string GetDescription()

return "Simple Coffee";

public decimal GetCost()

return 2.0m;

// Decorator abstract class

public abstract class CoffeeDecorator : ICoffee

protected ICoffee _coffee;


public CoffeeDecorator(ICoffee coffee)

_coffee = coffee;

public abstract string GetDescription();

public abstract decimal GetCost();

// Concrete decorators (toppings)

public class WhippedCream : CoffeeDecorator

public WhippedCream(ICoffee coffee) : base(coffee)

public override string GetDescription()

return _coffee.GetDescription() + ", Whipped Cream";

public override decimal GetCost()

return _coffee.GetCost() + 0.5m;


}

public class ChocolateSprinkles : CoffeeDecorator

public ChocolateSprinkles(ICoffee coffee) : base(coffee)

public override string GetDescription()

return _coffee.GetDescription() + ", Chocolate Sprinkles";

public override decimal GetCost()

return _coffee.GetCost() + 0.2m;

// Client code

public class Client

public static void Main(string[] args)

{
// Order a simple coffee

ICoffee coffee = new SimpleCoffee();

Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost()}"); // Output: Simple Coffee:


$2.00

// Add whipped cream

coffee = new WhippedCream(coffee);

Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost()}"); // Output: Simple Coffee,


Whipped Cream: $2.50

// Add chocolate sprinkles

coffee = new ChocolateSprinkles(coffee);

Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost()}"); // Output: Simple Coffee,


Whipped Cream, Chocolate Sprinkles: $2.70

Explanation: This example demonstrates how decorators can be used to add toppings to a basic coffee,
dynamically altering its description and cost.

10. Facade
 Purpose: Provides a simplified interface to a complex subsystem.

 What it is: A structural pattern that hides the complexity of a subsystem behind a simpler
interface, making it easier to use and understand. The facade acts as a single point of entry to the
subsystem, providing a high-level interface to the client.

 Benefits:

o Reduces complexity by hiding the details of the subsystem.

o Makes the subsystem easier to use and understand by providing a simplified interface.
o Decouples the client code from the subsystem, making it easier to modify the subsystem
without affecting the client.

 When to use it:

o When you need to simplify the interface of a complex subsystem.

o When you want to decouple the client code from the subsystem.

 How to use it:

1. Identify the subsystem classes that the client needs to interact with.

2. Create a facade class that provides a simplified interface to the subsystem.

3. Implement the facade methods by delegating to the appropriate methods of the subsystem classes.

 Real-world example: Think of a home theater system with multiple components (DVD player,
amplifier, projector, screen). A facade can provide a simplified interface with methods like
WatchMovie() or ListenToMusic(), hiding the complexity of managing each component
individually.

 C# Code Example:

// Subsystem classes

public class Amplifier

public void On()

Console.WriteLine("Amplifier is on.");

public void Off()

Console.WriteLine("Amplifier is off.");

}
public void SetVolume(int level)

Console.WriteLine($"Amplifier volume set to {level}.");

public class DVDPlayer

public void On()

Console.WriteLine("DVD Player is on.");

public void Off()

Console.WriteLine("DVD Player is off.");

public void Play(string movie)

Console.WriteLine($"Playing DVD: {movie}");

}
public class Projector

public void On()

Console.WriteLine("Projector is on.");

public void Off()

Console.WriteLine("Projector is off.");

public void WideScreenMode()

Console.WriteLine("Projector in widescreen mode.");

// Facade class

public class HomeTheaterFacade

private Amplifier _amplifier;

private DVDPlayer _dvdPlayer;

private Projector _projector;


public HomeTheaterFacade(Amplifier amplifier, DVDPlayer dvdPlayer, Projector projector)

_amplifier = amplifier;

_dvdPlayer = dvdPlayer;

_projector = projector;

public void WatchMovie(string movie)

Console.WriteLine("Getting ready to watch a movie...");

_amplifier.On();

_dvdPlayer.On();

_projector.On();

_projector.WideScreenMode();

_amplifier.SetVolume(5);

_dvdPlayer.Play(movie);

public void EndMovie()

Console.WriteLine("Shutting down the home theater...");

_amplifier.Off();

_dvdPlayer.Off();

_projector.Off();

}
}

// Client code

public class Client

public static void Main(string[] args)

// Create the home theater components

Amplifier amplifier = new Amplifier();

DVDPlayer dvdPlayer = new DVDPlayer();

Projector projector = new Projector();

// Create the facade

HomeTheaterFacade homeTheater = new HomeTheaterFacade(amplifier, dvdPlayer, projector);

// Use the facade to watch a movie

homeTheater.WatchMovie("Inception");

// End the movie

homeTheater.EndMovie();

Explanation: The HomeTheaterFacade class provides a simplified interface for controlling the home
theater components, hiding the complexities of managing each component individually.
11. Flyweight
 Purpose: Shares objects to support large numbers of fine-grained objects efficiently.

 What it is: A structural pattern that minimizes memory usage by sharing common state among
multiple objects. Flyweights store intrinsic state (shared) and extrinsic state (unique) separately,
allowing for significant memory savings when dealing with large numbers of similar objects.

 Benefits:

o Reduces memory usage by sharing common state.

o Improves performance by reducing object creation overhead.

 When to use it:

o When you need to create a large number of similar objects.

o When the objects have intrinsic state that can be shared, and extrinsic state that can be
passed in as parameters.

 How to use it:

1. Define a flyweight interface that declares methods for accessing and manipulating the flyweight's
intrinsic state.

2. Create concrete flyweight classes that implement the flyweight interface and store the intrinsic
state.

3. Create a flyweight factory that manages the flyweight objects, ensuring that only one instance of
a flyweight with a specific intrinsic state is created.

 Real-world example: Imagine a word processor where each character is an object. Using
flyweights, you can share the font and size data (intrinsic state) among all instances of the same
character, reducing memory usage significantly.

 C# Code Example:

// Flyweight interface

public interface ICharacter

void Display(int pointSize, string font, string color);

}
// Concrete Flyweight class

public class CharacterA : ICharacter

private char _symbol = 'A';

public void Display(int pointSize, string font, string color)

Console.WriteLine($"Character: {_symbol} ({pointSize}pt, {font}, {color})");

public class CharacterB : ICharacter

private char _symbol = 'B';

public void Display(int pointSize, string font, string color)

Console.WriteLine($"Character: {_symbol} ({pointSize}pt, {font}, {color})");

// Flyweight Factory

public class CharacterFactory

{
private Dictionary<char, ICharacter> _characters = new Dictionary<char, ICharacter>();

public ICharacter GetCharacter(char key)

if (!_characters.ContainsKey(key))

switch (key)

case 'A':

_characters[key] = new CharacterA();

break;

case 'B':

_characters[key] = new CharacterB();

break;

// Add more characters as needed

default:

throw new Exception("Character not found.");

return _characters[key];

// Client code

public class Client


{

public static void Main(string[] args)

CharacterFactory factory = new CharacterFactory();

// Create Flyweight objects

ICharacter charA1 = factory.GetCharacter('A');

ICharacter charA2 = factory.GetCharacter('A');

ICharacter charB = factory.GetCharacter('B');

// Display characters with different extrinsic states

charA1.Display(12, "Arial", "Red");

charA2.Display(14, "Times New Roman", "Blue");

charB.Display(10, "Courier", "Green");

// Check if the flyweight objects are the same

Console.WriteLine(charA1 == charA2); // Output: True

Explanation: In this example, the flyweight objects (CharacterA, CharacterB) store the intrinsic state (the
character symbol). The extrinsic state (point size, font, and color) is passed in as parameters to the
Display method. The CharacterFactory ensures that only one instance of each character is created.

12. Proxy
 Purpose: Provides a surrogate or placeholder for another object to control access to it.
 What it is: A structural pattern that provides a substitute or placeholder for a real object. The
proxy controls access to the real object, allowing you to perform operations like access control,
caching, logging, or lazy initialization.

 Benefits:

o Controls access to the real object.

o Provides additional functionality without modifying the real object.

o Can improve performance by using caching or lazy initialization.

 When to use it:

o When you need to control access to an object (e.g., for security reasons).

o When you want to add additional functionality to an object without modifying it directly.

o When you want to delay the creation of an object until it's actually needed.

 How to use it:

1. Define a subject interface that is implemented by both the real subject and the proxy.

2. Create a real subject class that provides the actual functionality.

3. Create a proxy class that implements the subject interface and contains a reference to the real
subject. The proxy controls access to the real subject.

 Real-world example: Imagine an image gallery application that displays thumbnails of large
images. You can use a proxy to represent the image and only load the full-size image when the
user clicks on the thumbnail.

 C# Code Example:

// Subject interface

public interface IImage

void Display();

// Real Subject
public class RealImage : IImage

private string _fileName;

public RealImage(string fileName)

_fileName = fileName;

LoadFromDisk();

private void LoadFromDisk()

Console.WriteLine($"Loading image from disk: {_fileName}");

public void Display()

Console.WriteLine($"Displaying image: {_fileName}");

// Proxy

public class ImageProxy : IImage

private string _fileName;


private RealImage _realImage;

public ImageProxy(string fileName)

_fileName = fileName;

public void Display()

if (_realImage == null)

_realImage = new RealImage(_fileName);

_realImage.Display();

// Client code

public class Client

public static void Main(string[] args)

// Create image proxies

IImage image1 = new ImageProxy("image1.jpg");

IImage image2 = new ImageProxy("image2.png");


// Display images (loads the images from disk only when Display() is called)

image1.Display();

image2.Display();

image1.Display(); // Image is already loaded, so it's not loaded again

Explanation: This example shows how the ImageProxy class acts as a proxy for the RealImage class,
loading the image from disk only when the Display method is called.

This covers the Structural Patterns in our Design Pattern journey. Next, we'll explore the Behavioral
Patterns! Let me know if you have any questions so far.

II.3. Behavioral Patterns


These patterns focus on the assignment of responsibilities between objects and how they interact and
communicate, leading to more flexible and maintainable systems.

13. Chain of Responsibility


 Purpose: Avoids coupling the sender of a request to its receiver by giving multiple objects a
chance to handle the request.

 What it is: A behavioral pattern that creates a chain of handler objects. Each handler has the
option to process a request or pass it on to the next handler in the chain.

 Benefits:

o Decouples the sender and receiver of a request.

o Increases flexibility in handling requests by allowing for dynamic addition or removal of


handlers.

o Avoids complex conditional logic by distributing responsibility.

 When to use it:

o When multiple objects can handle a request, and the handler should be determined at
runtime.
o When you want to avoid coupling the sender of a request to its receiver.

o When the set of handlers and their order may change dynamically.

 How to use it:

1. Define a handler interface with a HandleRequest method.

2. Create concrete handler classes that implement the handler interface, each responsible for
handling a specific type of request.

3. Chain the handlers together, typically by setting the NextHandler property of each handler to the
next handler in the chain.

4. The client sends the request to the first handler in the chain. Each handler checks if it can handle
the request; if not, it passes the request to the next handler.

 Real-world example: Imagine a customer support system where different levels of support
agents handle different types of issues. A basic issue can be resolved by a level 1 agent, while a
more complex issue might be escalated to a level 2 agent.

 C# Code Example:

// Handler interface

public interface IHandler

IHandler SetNext(IHandler handler);

object Handle(object request);

// Abstract handler class

public abstract class AbstractHandler : IHandler

private IHandler _nextHandler;

public IHandler SetNext(IHandler handler)


{

this._nextHandler = handler;

return handler;

public virtual object Handle(object request)

if (this._nextHandler != null)

return this._nextHandler.Handle(request);

else

return null;

// Concrete handler for handling basic issues

public class BasicSupportHandler : AbstractHandler

public override object Handle(object request)

if ((request as Issue).Severity == "Basic")

{
return $"BasicSupportHandler: I can handle this. Issue resolved.";

else

return base.Handle(request);

// Concrete handler for handling advanced issues

public class AdvancedSupportHandler : AbstractHandler

public override object Handle(object request)

if ((request as Issue).Severity == "Advanced")

return $"AdvancedSupportHandler: I can handle this. Issue escalated.";

else

return base.Handle(request);

}
// Issue class representing a support issue

public class Issue

public string Severity { get; set; }

public string Description { get; set; }

// Client code

public class Client

public static void Main(string[] args)

// Create the chain of handlers

var basicHandler = new BasicSupportHandler();

var advancedHandler = new AdvancedSupportHandler();

basicHandler.SetNext(advancedHandler);

// Create some issues

var issue1 = new Issue { Severity = "Basic", Description = "Password reset" };

var issue2 = new Issue { Severity = "Advanced", Description = "Server outage" };

var issue3 = new Issue { Severity = "Critical", Description = "Data breach" };

// Handle the issues

Console.WriteLine(basicHandler.Handle(issue1)); // Output: BasicSupportHandler: I can handle this.


Issue resolved.
Console.WriteLine(basicHandler.Handle(issue2)); // Output: AdvancedSupportHandler: I can handle
this. Issue escalated.

Console.WriteLine(basicHandler.Handle(issue3)); // Output: null (no handler found)

Explanation: In this example, the BasicSupportHandler handles basic issues, while the
AdvancedSupportHandler handles advanced issues. If an issue is not handled by either handler, it falls
through the chain and returns null.

14. Command
 Purpose: Encapsulates a request as an object, thereby letting you parameterize clients with
different requests, queue or log requests, and support undoable operations.

 What it is: A behavioral pattern that encapsulates a request as an object, allowing for
parameterization, queuing, logging, and undo/redo functionalities.

 Benefits:

o Decouples the object that invokes the request from the object that knows how to perform
it.

o Allows for queuing or logging of requests.

o Enables easy implementation of undo/redo functionality.

 When to use it:

o When you want to parameterize objects with different requests.

o When you need to queue or log requests.

o When you want to support undo/redo operations.

 How to use it:

1. Define a command interface with an Execute method.

2. Create concrete command classes that implement the command interface, each encapsulating a
specific request.
3. Create an invoker object that holds and invokes the commands.

4. The client creates command objects and sets them on the invoker. The invoker then executes the
commands.

 Real-world example: Think of a text editor where each editing action (typing, deleting,
formatting) is a command. These commands can be queued, logged, and undone/redone,
providing a flexible and powerful editing experience.

 C# Code Example:

// Command interface

public interface ICommand

void Execute();

void Undo();

// Concrete command for turning a light on

public class TurnOnLightCommand : ICommand

private Light _light;

public TurnOnLightCommand(Light light)

_light = light;

public void Execute()

{
_light.TurnOn();

public void Undo()

_light.TurnOff();

// Concrete command for turning a light off

public class TurnOffLightCommand : ICommand

private Light _light;

public TurnOffLightCommand(Light light)

_light = light;

public void Execute()

_light.TurnOff();

public void Undo()


{

_light.TurnOn();

// Receiver class

public class Light

public void TurnOn()

Console.WriteLine("Light is ON");

public void TurnOff()

Console.WriteLine("Light is OFF");

// Invoker class

public class RemoteControl

private ICommand _command;

public void SetCommand(ICommand command)


{

_command = command;

public void PressButton()

_command.Execute();

public void PressUndo()

_command.Undo();

// Client code

public class Client

public static void Main(string[] args)

// Create a light and a remote

Light light = new Light();

RemoteControl remote = new RemoteControl();

// Set the command to turn on the light


remote.SetCommand(new TurnOnLightCommand(light));

remote.PressButton(); // Output: Light is ON

// Set the command to turn off the light

remote.SetCommand(new TurnOffLightCommand(light));

remote.PressButton(); // Output: Light is OFF

// Undo the last command

remote.PressUndo(); // Output: Light is ON

Explanation: This example shows how a RemoteControl (Invoker) can be used to execute and undo
Light (Receiver) commands (TurnOnLightCommand, TurnOffLightCommand).

15. Interpreter
 Purpose: Given a language, defines a representation for its grammar along with an interpreter
that uses the representation to interpret sentences in the language.

 What it is: A behavioral pattern that defines a grammatical representation for a language and
provides an interpreter to deal with this grammar.

 Benefits:

o Provides a flexible and extensible way to process structured data.

o Allows for easy addition of new language features.

o Can be used to implement domain-specific languages (DSLs).

 When to use it:

o When you need to interpret a language with a well-defined grammar.

o When the grammar is relatively simple and can be represented using a recursive structure.
 How to use it:

1. Define a grammar for the language using a context-free grammar or similar formalism.

2. Create an abstract expression class for each rule in the grammar.

3. Create concrete expression classes that implement the abstract expression classes, each
representing a specific terminal or non-terminal symbol in the grammar.

4. Create an interpreter class that traverses the abstract syntax tree (AST) generated from the input
string, interpreting each expression according to the grammar rules.

 Real-world example: Consider a system that evaluates mathematical expressions. You can use
the Interpreter pattern to define the grammar for mathematical expressions and create an
interpreter that evaluates them.

 C# Code Example:

// Abstract expression interface

public interface IExpression

int Interpret();

// Terminal expression for numbers

public class NumberExpression : IExpression

private int _number;

public NumberExpression(int number)

_number = number;

}
public int Interpret()

return _number;

// Non-terminal expression for addition

public class AddExpression : IExpression

private IExpression _leftExpression;

private IExpression _rightExpression;

public AddExpression(IExpression leftExpression, IExpression rightExpression)

_leftExpression = leftExpression;

_rightExpression = rightExpression;

public int Interpret()

return _leftExpression.Interpret() + _rightExpression.Interpret();

}
// Non-terminal expression for subtraction

public class SubtractExpression : IExpression

private IExpression _leftExpression;

private IExpression _rightExpression;

public SubtractExpression(IExpression leftExpression, IExpression rightExpression)

_leftExpression = leftExpression;

_rightExpression = rightExpression;

public int Interpret()

return _leftExpression.Interpret() - _rightExpression.Interpret();

// Client code

public class Client

public static void Main(string[] args)

// Create the expression tree: 1 + (2 - 3)

IExpression expression = new AddExpression(


new NumberExpression(1),

new SubtractExpression(

new NumberExpression(2),

new NumberExpression(3)

);

// Interpret the expression

int result = expression.Interpret();

Console.WriteLine($"Result: {result}"); // Output: Result: 0

Explanation: This example defines a simple grammar for mathematical expressions with addition and
subtraction. The Interpreter pattern is used to build an expression tree from the input and then interpret the
tree to calculate the result.

16. Iterator
 Purpose: Provides a way to access the elements of an aggregate object sequentially without
exposing its underlying representation.

 What it is: A behavioral pattern that provides a way to traverse the elements of a collection
without exposing its underlying structure.

 Benefits:

o Provides a consistent way to iterate over different types of collections.

o Simplifies the client code by hiding the details of the iteration process.

o Allows for multiple iterations over the same collection without interfering with each
other.
 When to use it:

o When you want to provide a way to access the elements of a collection without exposing
its internal structure.

o When you want to support different types of iterations (e.g., forward, backward, filtered).

 How to use it:

1. Define an iterator interface that declares methods for traversing the collection (e.g., HasNext,
Next, Current).

2. Create concrete iterator classes that implement the iterator interface, each specific to a type of
collection.

3. Implement an iterator() method in the aggregate class that returns an instance of the appropriate
iterator.

 Real-world example: Think of a music playlist. You can use an iterator to go through each song
in the playlist without knowing how the songs are stored internally (e.g., array, linked list, etc.).

 C# Code Example:

// Iterator interface

public interface IIterator<T>

bool HasNext();

T Next();

// Aggregate interface

public interface IAggregate<T>

IIterator<T> CreateIterator();

}
// Concrete aggregate (a collection of names)

public class NameRepository : IAggregate<string>

private string[] _names = { "Robert", "John", "Julie", "Lora" };

public IIterator<string> CreateIterator()

return new NameIterator(_names);

// Concrete iterator

public class NameIterator : IIterator<string>

private string[] _names;

private int _index = 0;

public NameIterator(string[] names)

_names = names;

public bool HasNext()

return _index < _names.Length;


}

public string Next()

return _names[_index++];

// Client code

public class Client

public static void Main(string[] args)

NameRepository namesRepository = new NameRepository();

// Get an iterator

IIterator<string> iterator = namesRepository.CreateIterator();

// Iterate over the names

while (iterator.HasNext())

Console.WriteLine($"Name : {iterator.Next()}");

}
Explanation: This example shows how an iterator (NameIterator) can be used to traverse the elements of
a NameRepository without exposing its internal structure.

17. Mediator
 Purpose: Defines an object that encapsulates how a set of objects interact.

 What it is: A behavioral pattern that promotes loose coupling between objects by introducing a
mediator object. The mediator acts as a central hub for communication between objects, reducing
direct dependencies between them.

 Benefits:

o Simplifies object communication by centralizing the logic.

o Reduces coupling between objects, making the system more flexible and maintainable.

o Improves code organization by encapsulating interaction logic.

 When to use it:

o When a set of objects communicate in a complex or tightly coupled manner.

o When you want to reuse objects in different contexts but their interactions are context-
dependent.

o When you want to centralize control logic for a group of objects.

 How to use it:

1. Define a mediator interface that declares methods for communication between colleagues.

2. Create concrete mediator classes that implement the mediator interface and coordinate
communication between colleagues.

3. Create colleague classes that hold a reference to the mediator and communicate through it.

 Real-world example: Imagine an air traffic control tower. The tower (mediator) coordinates
communication between airplanes (colleagues), ensuring safe and efficient air traffic.

 C# Code Example:

// Mediator interface

public interface IChatroomMediator


{

void SendMessage(string message, User user);

// Concrete Mediator

public class Chatroom : IChatroomMediator

private List<User> _users = new List<User>();

public void RegisterUser(User user)

_users.Add(user);

public void SendMessage(string message, User user)

foreach (User u in _users)

// Message should not be received by the sender

if (u != user)

u.Receive(message);

}
}

// Colleague

public class User

private string _name;

private IChatroomMediator _chatroom;

public User(string name, IChatroomMediator chatroom)

_name = name;

_chatroom = chatroom;

public void Send(string message)

Console.WriteLine($"{_name}: Sending Message: {message}");

_chatroom.SendMessage(message, this);

public void Receive(string message)

Console.WriteLine($"{_name}: Received Message: {message}");

}
// Client code

public class Client

public static void Main(string[] args)

// Create a chatroom (mediator)

IChatroomMediator chatroom = new Chatroom();

// Create users (colleagues)

User user1 = new User("John", chatroom);

User user2 = new User("Jane", chatroom);

User user3 = new User("David", chatroom);

// Register users with the chatroom

chatroom.RegisterUser(user1);

chatroom.RegisterUser(user2);

chatroom.RegisterUser(user3);

// Send messages

user1.Send("Hi everyone!"); // Output: John: Sending Message: Hi everyone!

// Jane: Received Message: Hi everyone!

// David: Received Message: Hi everyone!

user2.Send("Hello John!"); // Output: Jane: Sending Message: Hello John!

// John: Received Message: Hello John!


// David: Received Message: Hello John!

Explanation: This example shows how a Chatroom (mediator) facilitates communication between User
(colleague) objects, ensuring that messages are delivered to the correct recipients.

18. Memento
 Purpose: Without violating encapsulation, captures and externalizes an object's internal state so
that the object can be restored to this state later.

 What it is: A behavioral pattern that allows you to save and restore an object's internal state
without exposing its internal structure.

 Benefits:

o Provides undo/redo functionality.

o Allows for object state persistence.

o Preserves encapsulation by hiding the object's internal state from other objects.

 When to use it:

o When you need to save and restore the state of an object.

o When you want to provide undo/redo functionality.

 How to use it:

1. Create a memento class that stores the internal state of the originator object.

2. Create an originator class that has methods for creating a memento and restoring its state from a
memento.

3. Create a caretaker class that holds and manages the memento objects.

 Real-world example: Consider a text editor. The Memento pattern can be used to implement the
undo/redo functionality, allowing the user to revert the text editor to a previous state.

 C# Code Example:

// Memento class
public class EditorMemento

private string _content;

public EditorMemento(string content)

_content = content;

public string GetContent()

return _content;

// Originator class

public class TextEditor

private string _content;

public void SetContent(string content)

_content = content;

}
public string GetContent()

return _content;

public EditorMemento CreateMemento()

return new EditorMemento(_content);

public void RestoreFromMemento(EditorMemento memento)

_content = memento.GetContent();

// Caretaker class

public class History

private Stack<EditorMemento> _mementos = new Stack<EditorMemento>();

public void Push(EditorMemento memento)

_mementos.Push(memento);

}
public EditorMemento Pop()

return _mementos.Pop();

// Client code

public class Client

public static void Main(string[] args)

TextEditor editor = new TextEditor();

History history = new History();

// Initial content

editor.SetContent("Hello World");

history.Push(editor.CreateMemento());

// Change content

editor.SetContent("Hello Universe");

history.Push(editor.CreateMemento());

// Change content again

editor.SetContent("Hello Galaxy");
// Restore to the previous state

editor.RestoreFromMemento(history.Pop());

Console.WriteLine(editor.GetContent()); // Output: Hello Universe

// Restore to the original state

editor.RestoreFromMemento(history.Pop());

Console.WriteLine(editor.GetContent()); // Output: Hello World

Explanation: This example demonstrates how a History (caretaker) object can store EditorMemento
objects, which represent the state of a TextEditor (originator) at different points in time, allowing for
undo/redo functionality.

19. Observer
 Purpose: Defines a one-to-many dependency between objects so that when one object changes
state, all its dependents are notified and updated automatically.

 What it is: A behavioral pattern where a "subject" object maintains a list of its dependents, called
"observers," and notifies them automatically of any state changes, usually by calling one of their
methods.

 Benefits:

o Establishes a loose coupling between the subject and its observers, making the system
more flexible and maintainable.

o Allows for dynamic addition or removal of observers at runtime.

o Provides an efficient way for the subject to notify its dependents without having to know
about their specific implementations.

 When to use it:


o When a change in one object should trigger changes in other objects, and the number of
dependent objects may vary.

o When you want to decouple the subject from its observers, allowing them to change
independently.

 How to use it:

1. Define a subject interface with methods for attaching and detaching observers and notifying them.

2. Create a concrete subject class that implements the subject interface and maintains a list of
observers.

3. Define an observer interface with an Update method that will be called by the subject when its
state changes.

4. Create concrete observer classes that implement the observer interface and define the actions to
be performed when the subject's state changes.

 Real-world example: A news agency (subject) can notify its subscribers (observers) when new
news articles are published.

 C# Code Example:

// Subject interface

public interface ISubject

void Attach(IObserver observer);

void Detach(IObserver observer);

void Notify();

// Concrete Subject

public class NewsAgency : ISubject

private List<IObserver> _observers = new List<IObserver>();


private string _latestNews;

public string LatestNews

get { return _latestNews; }

set

_latestNews = value;

Notify();

public void Attach(IObserver observer)

_observers.Add(observer);

public void Detach(IObserver observer)

_observers.Remove(observer);

public void Notify()

foreach (IObserver observer in _observers)


{

observer.Update(this);

// Observer interface

public interface IObserver

void Update(ISubject subject);

// Concrete Observer

public class NewsSubscriber : IObserver

private string _name;

public NewsSubscriber(string name)

_name = name;

public void Update(ISubject subject)

if (subject is NewsAgency newsAgency)


{

Console.WriteLine($"{_name} received news: {newsAgency.LatestNews}");

// Client code

public class Client

public static void Main(string[] args)

// Create a news agency

NewsAgency newsAgency = new NewsAgency();

// Create subscribers

NewsSubscriber subscriber1 = new NewsSubscriber("John Doe");

NewsSubscriber subscriber2 = new NewsSubscriber("Jane Doe");

// Attach subscribers to the news agency

newsAgency.Attach(subscriber1);

newsAgency.Attach(subscriber2);

// Publish new news

newsAgency.LatestNews = "Breaking News: Major Earthquake Strikes!"; // Output: John Doe


received news: Breaking News: Major Earthquake Strikes!

// Jane Doe received news: Breaking News: Major Earthquake Strikes!


// Detach a subscriber

newsAgency.Detach(subscriber1);

// Publish more news

newsAgency.LatestNews = "Stock Market Plummets After Tech Giant Announces Layoffs"; //


Output: Jane Doe received news: Stock Market Plummets After Tech Giant Announces Layoffs

Explanation: This example demonstrates how the NewsAgency (Subject) notifies its NewsSubscriber
(Observer) objects when there's an update to the LatestNews.

20. State
 Purpose: Allows an object to alter its behavior when its internal state changes.

 What it is: A behavioral pattern that allows an object to change its behavior based on its internal
state. The object delegates its behavior to a state object, which represents a particular state.

 Benefits:

o Simplifies complex conditional logic for state-dependent behavior.

o Makes it easier to add or modify states without affecting the context object.

o Provides a clean separation of state-specific logic.

 When to use it:

o When an object's behavior changes based on its internal state.

o When you have a lot of conditional statements to handle different states.

o When you want to make it easier to add or modify states.

 How to use it:

1. Define a state interface that declares methods for the state-specific behavior.

2. Create concrete state classes that implement the state interface, each representing a different state.
3. Create a context class that contains a reference to the current state object and delegates requests to
it.

 Real-world example: Consider a vending machine. The vending machine's behavior changes
based on its state (e.g., has money, no money, dispensing item).

 C# Code Example:

// State interface

public interface IState

void InsertQuarter(VendingMachine machine);

void EjectQuarter(VendingMachine machine);

void TurnCrank(VendingMachine machine);

void Dispense(VendingMachine machine);

// Concrete state: No Quarter

public class NoQuarterState : IState

public void InsertQuarter(VendingMachine machine)

Console.WriteLine("You inserted a quarter.");

machine.SetState(machine.HasQuarterState);

public void EjectQuarter(VendingMachine machine)

Console.WriteLine("You haven't inserted a quarter.");


}

public void TurnCrank(VendingMachine machine)

Console.WriteLine("You turned, but there's no quarter.");

public void Dispense(VendingMachine machine)

Console.WriteLine("You need to pay first.");

// Concrete state: Has Quarter

public class HasQuarterState : IState

public void InsertQuarter(VendingMachine machine)

Console.WriteLine("You can't insert another quarter.");

public void EjectQuarter(VendingMachine machine)

Console.WriteLine("Quarter returned.");

machine.SetState(machine.NoQuarterState);
}

public void TurnCrank(VendingMachine machine)

Console.WriteLine("You turned...");

machine.SetState(machine.SoldState);

public void Dispense(VendingMachine machine)

Console.WriteLine("No gumball dispensed.");

// Concrete state: Sold

public class SoldState : IState

public void InsertQuarter(VendingMachine machine)

Console.WriteLine("Please wait, we're already giving you a gumball.");

public void EjectQuarter(VendingMachine machine)

Console.WriteLine("Sorry, you already turned the crank.");


}

public void TurnCrank(VendingMachine machine)

Console.WriteLine("Turning twice doesn't get you another gumball!");

public void Dispense(VendingMachine machine)

Console.WriteLine("A gumball comes rolling out the slot.");

if (machine.Count > 0)

machine.SetState(machine.NoQuarterState);

else

Console.WriteLine("Oops, out of gumballs!");

machine.SetState(machine.SoldOutState);

// Concrete state: Sold Out

public class SoldOutState : IState

{
public void InsertQuarter(VendingMachine machine)

Console.WriteLine("You can't insert a quarter, the machine is sold out.");

public void EjectQuarter(VendingMachine machine)

Console.WriteLine("You can't eject, you haven't inserted a quarter yet.");

public void TurnCrank(VendingMachine machine)

Console.WriteLine("You turned, but there are no gumballs.");

public void Dispense(VendingMachine machine)

Console.WriteLine("No gumball dispensed.");

// Context

public class VendingMachine

private IState _state;

public int Count { get; set; }


public NoQuarterState NoQuarterState = new NoQuarterState();

public HasQuarterState HasQuarterState = new HasQuarterState();

public SoldState SoldState = new SoldState();

public SoldOutState SoldOutState = new SoldOutState();

public VendingMachine(int count)

Count = count;

if (count > 0)

_state = NoQuarterState;

else

_state = SoldOutState;

public void SetState(IState state)

_state = state;

public void InsertQuarter()


{

_state.InsertQuarter(this);

public void EjectQuarter()

_state.EjectQuarter(this);

public void TurnCrank()

_state.TurnCrank(this);

_state.Dispense(this);

// Client code

public class Client

public static void Main(string[] args)

VendingMachine machine = new VendingMachine(5);

machine.InsertQuarter();

machine.TurnCrank();
machine.InsertQuarter();

machine.EjectQuarter();

machine.TurnCrank();

for (int i = 0; i < 5; i++)

machine.InsertQuarter();

machine.TurnCrank();

Explanation: The VendingMachine (Context) class holds a reference to the current state (_state) and
delegates the behavior to the corresponding state object.

21. Strategy
 Purpose: Defines a family of algorithms, encapsulates each one, and makes them
interchangeable.

 What it is: A behavioral pattern that lets you define a family of algorithms, encapsulate each one
into a separate class, and make them interchangeable. This allows the algorithm used by a client
to be selected at runtime.

 Benefits:

o Allows for dynamic switching of algorithms at runtime.

o Encapsulates the implementation details of algorithms, promoting clean code and


separation of concerns.

o Makes it easy to add or remove algorithms without modifying the client code.

 When to use it:


o When you have multiple algorithms for performing a specific task, and you want to be
able to choose the algorithm at runtime.

o When the behavior of an object needs to vary depending on the context.

o When you want to avoid using conditional statements to choose between different
algorithms.

 How to use it:

1. Define a strategy interface that declares the common methods for all algorithms.

2. Create concrete strategy classes that implement the strategy interface, each representing a
different algorithm.

3. Create a context class that uses a strategy object to perform the operation.

4. The client chooses the strategy to use and sets it on the context object.

 Real-world example: Imagine a navigation app that provides different routing algorithms (fastest
route, shortest route, most scenic route). The user can choose the routing algorithm based on their
preferences.

 C# Code Example:

// Strategy interface

public interface IRouteStrategy

void CalculateRoute(string origin, string destination);

// Concrete strategy: Fastest Route

public class FastestRouteStrategy : IRouteStrategy

public void CalculateRoute(string origin, string destination)

Console.WriteLine($"Calculating the fastest route from {origin} to {destination}...");


// Logic to calculate the fastest route

// Concrete strategy: Shortest Route

public class ShortestRouteStrategy : IRouteStrategy

public void CalculateRoute(string origin, string destination)

Console.WriteLine($"Calculating the shortest route from {origin} to {destination}...");

// Logic to calculate the shortest route

// Context class

public class Navigator

private IRouteStrategy _routeStrategy;

public Navigator(IRouteStrategy routeStrategy)

_routeStrategy = routeStrategy;

public void SetStrategy(IRouteStrategy routeStrategy)


{

_routeStrategy = routeStrategy;

public void Navigate(string origin, string destination)

_routeStrategy.CalculateRoute(origin, destination);

// Client code

public class Client

public static void Main(string[] args)

// Create a navigator with the fastest route strategy

Navigator navigator = new Navigator(new FastestRouteStrategy());

navigator.Navigate("New York", "Los Angeles");

// Change the strategy to shortest route

navigator.SetStrategy(new ShortestRouteStrategy());

navigator.Navigate("London", "Paris");

}
Explanation: This example shows how the Navigator (Context) uses different RouteStrategy (Concrete
Strategies) to calculate routes. The client can dynamically change the strategy at runtime, allowing for
flexibility in choosing the routing algorithm.

22. Template Method


 Purpose: Defines the skeleton of an algorithm in an operation, deferring some steps to
subclasses.

 What it is: A behavioral pattern that defines the overall structure of an algorithm in a base class,
allowing subclasses to override specific steps without changing the algorithm's structure.

 Benefits:

o Promotes code reuse by defining a common algorithm structure in the base class.

o Ensures consistent algorithm execution by providing a template that subclasses must


follow.

o Allows for flexibility in customizing specific steps of the algorithm.

 When to use it:

o When you want to define an algorithm's invariant parts in a base class and allow
subclasses to define variant parts.

o When you want to control the points of extension in an algorithm.

 How to use it:

1. Define a template method in the base class that outlines the algorithm's steps.

2. Declare abstract methods (or hook methods with default implementations) in the base class for
the steps that can be overridden by subclasses.

3. Create concrete subclasses that override the abstract methods to provide specific implementations
for those steps.

 Real-world example: Think of a process for making different types of beverages (coffee, tea, hot
chocolate). The Template Method pattern can define the common steps (boil water, add
ingredients, steep/brew, serve), while subclasses override specific steps like the type of
ingredients or brewing method.

 C# Code Example:

// Abstract base class with template method


public abstract class Beverage

// Template method outlining the algorithm

public void PrepareRecipe()

BoilWater();

Brew();

PourInCup();

AddCondiments();

// Common operations

protected void BoilWater()

Console.WriteLine("Boiling water...");

protected void PourInCup()

Console.WriteLine("Pouring into cup...");

// Abstract methods to be overridden by subclasses

protected abstract void Brew();

protected abstract void AddCondiments();


}

// Concrete class for Coffee

public class Coffee : Beverage

protected override void Brew()

Console.WriteLine("Dripping Coffee through filter...");

protected override void AddCondiments()

Console.WriteLine("Adding Sugar and Milk...");

// Concrete class for Tea

public class Tea : Beverage

protected override void Brew()

Console.WriteLine("Steeping tea bag...");

protected override void AddCondiments()


{

Console.WriteLine("Adding Lemon...");

// Client code

public class Client

public static void Main(string[] args)

// Prepare coffee

Beverage coffee = new Coffee();

coffee.PrepareRecipe();

Console.WriteLine();

// Prepare tea

Beverage tea = new Tea();

tea.PrepareRecipe();

Explanation: This example shows how the Beverage class defines the template method PrepareRecipe(),
outlining the common steps for making a beverage. The subclasses Coffee and Tea provide concrete
implementations for the Brew and AddCondiments methods, customizing the steps specific to each
beverage.

23. Visitor
 Purpose: Represents an operation to be performed on the elements of an object structure.

 What it is: A behavioral pattern that lets you define new operations to be performed on elements
of an object structure (like a tree or a list) without modifying the structure itself. The visitor
object "visits" each element and performs the specific operation on it.

 Benefits:

o Adds new operations to a class hierarchy without modifying the classes themselves.

o Separates the algorithm from the object structure.

o Can gather related operations into a single visitor class.

 When to use it:

o When you need to perform operations on objects in a heterogeneous collection.

o When you want to add operations to a class hierarchy without modifying the classes.

o When the operations you want to perform vary depending on the concrete type of the
object.

 How to use it:

1. Define a visitor interface with Visit methods for each concrete element type in the object
structure.

2. Create concrete visitor classes that implement the visitor interface, each providing the specific
implementation for the operation on the corresponding element type.

3. Add an Accept method to the element interface that takes a visitor object as a parameter.

4. Implement the Accept method in each concrete element class to call the appropriate Visit method
on the visitor, passing itself as an argument.

 Real-world example: Imagine a compiler that needs to perform different operations on different
parts of the code (e.g., checking variable declarations, analyzing expressions, generating code).
The Visitor pattern can be used to define visitors for each of these operations, allowing new
operations to be added without modifying the code structure.
 C# Code Example:

// Element interface

public interface IElement

void Accept(IVisitor visitor);

// Concrete Element classes

public class ConcreteElementA : IElement

public void OperationA()

Console.WriteLine("ConcreteElementA: OperationA");

public void Accept(IVisitor visitor)

visitor.VisitConcreteElementA(this);

public class ConcreteElementB : IElement

public void OperationB()

{
Console.WriteLine("ConcreteElementB: OperationB");

public void Accept(IVisitor visitor)

visitor.VisitConcreteElementB(this);

// Visitor interface

public interface IVisitor

void VisitConcreteElementA(ConcreteElementA element);

void VisitConcreteElementB(ConcreteElementB element);

// Concrete Visitor class

public class ConcreteVisitor1 : IVisitor

public void VisitConcreteElementA(ConcreteElementA element)

Console.WriteLine("ConcreteVisitor1 visited ConcreteElementA.");

element.OperationA();

}
public void VisitConcreteElementB(ConcreteElementB element)

Console.WriteLine("ConcreteVisitor1 visited ConcreteElementB.");

element.OperationB();

// Client code

public class Client

public static void Main(string[] args)

// Create objects and structure

List<IElement> elements = new List<IElement>

new ConcreteElementA(),

new ConcreteElementB()

};

// Create visitor

ConcreteVisitor1 visitor1 = new ConcreteVisitor1();

// Use visitor to perform operations on elements

foreach (IElement element in elements)

{
element.Accept(visitor1);

Explanation: This example shows how the ConcreteVisitor1 performs specific operations on each
element in the list, demonstrating the core concept of the Visitor pattern in separating operations from the
object structure.

These comprehensive explanations should give you a solid understanding of both the Structural and
Behavioral Design Patterns. Remember, choosing the right pattern depends on the specific problem you're
trying to solve and the context of your application. Good Luck!

Rgds/ManhNC5

You might also like