Introduction ๐
Design Pattern ๐งฑ
A design pattern is a solution that can be repeated/use every time that a specific problem/scenario occurs in the software design. The concept behind these solutions is independent of the programming language.
If we make an analogy with food, there are lots of different ways (recipes) to make French macarons, each one of them will create a different product with a different quality, so in this case, why not get the knowledge of the most experienced chef and follow his/her recipe and steps every time we need to make a French macaron?
This is the idea behind design patterns, to follow the best practices used by โchefsโ (Gang of Four (GoF)) to tackle a specific problem.
Gang of Four (GoF) ๐
Gang of Four is the term that represents the four authors (Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides) of the well-known book in the software industry: โDesign Patterns: Elements of Reusable Object-Oriented Softwareโ. This book was originally published in 1994 and introduced 23 solutions for commonly occurring design problems.
Gang of Four is the best book ever written on object-oriented design - possibly of any style of design. This book has been enormously influential on the software industry - just look at the Java and .NET libraries which are crawling with GOF patterns. Martin Fowler (software developer, author and international public speaker on software development)
Design Categories โ๏ธ
The 23 GoF Design Patterns are broken into three main categories:
โ๏ธBehavioral: Describes how objects interact/communicate between themselves. โ๏ธCreational: Describes how to instantiate an object without large and complex. โ๏ธStructural: Describes how objects/classes are composed to form larger structures.
In this article, letโs analyze the design patterns that are part of the structural category.
Structural Patterns
- Adapter: Match interfaces of different classes
- Bridge: Separates an objectโs interface from its implementation
- Composite: A tree structure of simple and composite objects
- Decorator: Add responsibilities to objects dynamically
- Facade: A single class that represents an entire subsystem
- Flyweight: A fine-grained instance used for efficient sharing
- Proxy: An object representing another object
Source Code ๐ฒ
github.com/VictorLins/DesignPatterns
Structural Patterns
1. Adapter
Objective ๐ฏ
Allow objects with different interfaces to communicate between themselves. It works as a bridge between two incompatible objects/interfaces.
UML ๐
Participants ๐
โข Target:
- Defines the specific interface that the Client is expecting
โข Adapter:
- Adapts the external class (Adaptee) to meet the requirements expected by the Target class
โข Adaptee:
- Any existing interface/code that needs to be adapted.
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class AdapterStructural
{
public static void Execute()
{
Target lTarget = new Adapter();
lTarget.Request();
}
}
public class Target
{
public virtual void Request()
{
Console.WriteLine("Target - Executing Default Request");
}
}
public class Adapter : Target
{
private Adaptee _Adaptee = new Adaptee();
public override void Request()
{
// Possibly execute some other code
_Adaptee.SpecificRequest();
}
}
public class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Adaptee - Executing Specific Requirement");
}
}
Output
Real-world Example ๐ฅ
public static class AdapterPractical
{
public static void Execute()
{
MediaPlayer lMediaPlayer = new MediaPlayer();
lMediaPlayer.Play("C:\\FolderA\\FileA.mp4");
lMediaPlayer = new VideoPlayerAdapter();
lMediaPlayer.Play("C:\\FolderA\\FileA.mp4");
lMediaPlayer.Play("C:\\FolderA\\FileB.flv");
lMediaPlayer = new AudioPlayerAdapter();
lMediaPlayer.Play("C:\\FolderA\\FileC.mp3");
lMediaPlayer.Play("C:\\FolderA\\FileD.aac");
lMediaPlayer.Play("C:\\FolderA\\FileE.oog");
}
}
public class MediaPlayer
{
public virtual void Play(String prFile)
{
Console.WriteLine("MediaPlayer - Media Player Unknown...");
}
}
public class VideoPlayerAdapter : MediaPlayer
{
private VideoPlayerLibrary _VideoPlayerLibrary = new VideoPlayerLibrary();
public override void Play(String prFile)
{
string lExtension = prFile.Substring(prFile.Length - 3);
switch (lExtension.ToUpper())
{
case "AVI":
_VideoPlayerLibrary.PlayAVIFile(prFile);
break;
case "MP4":
_VideoPlayerLibrary.PlayMP4File(prFile);
break;
case "FLV":
_VideoPlayerLibrary.PlayFLVFile(prFile);
break;
default:
Console.WriteLine("VideoPlayerAdapter - Video Format Not Supported...");
break;
}
}
}
public class AudioPlayerAdapter : MediaPlayer
{
private AudioPlayerLibrary _AudioPlayerLibrary = new AudioPlayerLibrary();
public override void Play(String prFile)
{
string lExtension = prFile.Substring(prFile.Length - 3);
switch (lExtension.ToUpper())
{
case "MP3":
_AudioPlayerLibrary.PlayMP3File(prFile);
break;
case "AAC":
_AudioPlayerLibrary.PlayAACFile(prFile);
break;
default:
Console.WriteLine("AudioPlayerAdapter - Audio Format Not Supported...");
break;
}
}
}
public class VideoPlayerLibrary
{
public void PlayAVIFile(String prFile) { Console.WriteLine("VideoPlayerLibrary - Playing AVI File..."); }
public void PlayMP4File(String prFile) { Console.WriteLine("VideoPlayerLibrary - Playing MP4 File..."); }
public void PlayFLVFile(String prFile) { Console.WriteLine("VideoPlayerLibrary - Playing FLV File..."); }
}
public class AudioPlayerLibrary
{
public void PlayMP3File(String prFile) { Console.WriteLine("AudioPlayerLibrary - Playing MP3 File..."); }
public void PlayAACFile(String prFile) { Console.WriteLine("AudioPlayerLibrary - Playing AAC File..."); }
}
Output
2. Bridge
Objective ๐ฏ
Allow to decouple one abstraction from its implementation so that both can vary independently, in other words, it allows to split a large class into two separate hierarchies (abstraction and implementation).
Notes ๐
โข Bridge pattern comes as an option to avoid an inheritance problem known as โproliferation of classesโ or โexplosion of subclassesโ
UML ๐
Participants ๐
โข Abstraction:
- Defines the abstractionโs interface
- Maintains a reference to an object of type Implementor
RefinedAbstraction:
- Extends the interface defined by Abstraction
โข Implementor:
- Defines the interface for implementation classes.
โข ConcreteImplementor:
- Implements the Implementor interface and defines its concrete implementation.
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class BridgeStructural
{
public static void Execute()
{
Abstraction lAbstraction = new RefinedAbstraction();
// Set Implementor to A
lAbstraction._Implementor = new ConcreteImplementorA();
lAbstraction.Operation();
// Set Implementor to B
lAbstraction._Implementor = new ConcreteImplementorB();
lAbstraction.Operation();
}
}
public class Abstraction
{
public Implementor _Implementor { get; set; }
public virtual void Operation()
{
_Implementor.Operation();
}
}
public abstract class Implementor
{
public abstract void Operation();
}
public class RefinedAbstraction : Abstraction
{
public override void Operation()
{
_Implementor.Operation();
}
}
public class ConcreteImplementorA : Implementor
{
public override void Operation()
{
Console.WriteLine("Concrete Implementor A - Executing Operation...");
}
}
public class ConcreteImplementorB : Implementor
{
public override void Operation()
{
Console.WriteLine("Concrete Implementor B - Executing Operation...");
}
}
Output
Real-world Example ๐ฅ
public static class BridgePractical
{
public static void Execute()
{
Shape lShape = new Square();
// Set Implementor to Blue
lShape._ColorImplementor = new BlueImplementor();
lShape.Draw();
// Set Implementor to Red
lShape._ColorImplementor = new RedImplementor();
lShape.Draw();
}
}
public class Shape
{
public ColorImplementor _ColorImplementor { get; set; }
public virtual void Draw()
{
_ColorImplementor.Draw();
}
}
public class Square : Shape
{
public override void Draw()
{
_ColorImplementor.Draw();
}
}
public class Circle : Shape
{
public override void Draw()
{
_ColorImplementor.Draw();
}
}
public abstract class ColorImplementor
{
public abstract void Draw();
}
public class BlueImplementor : ColorImplementor
{
public override void Draw()
{
Console.WriteLine("Blue Implementor - Drawing a blue component...");
}
}
public class RedImplementor : ColorImplementor
{
public override void Draw()
{
Console.WriteLine("Red Implementor - Drawing a red component...");
}
}
Output
3. Composite
Objective ๐ฏ
Provide a way to compose objects into tree structures, also known as part-whole hierarchy, allowing the client to treat/work with each object of the structure individually.
Notes ๐
โข Composite pattern makes sense just in scenarios where the model can be represented as a tree.
UML ๐
Participants ๐
โข Component:
- Declares the interface for objects in the composition with the default behaviour common to all classes
โข Leaf:
- Implements Component interface
- Represents leaf objects (components with no children)
โข Composite:
- Implements Component interface
- Declares an interface for accessing and managing its child components
- Store children components
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class CompositeStructural
{
public static void Execute()
{
Composite lRoot = new Composite("Root");
lRoot.Add(new Leaf("Leaf A"));
lRoot.Add(new Leaf("Leaf B"));
Composite lComposite = new Composite("Composite X");
lComposite.Add(new Leaf("Leaf XA"));
lComposite.Add(new Leaf("Leaf XB"));
lRoot.Add(lComposite);
lRoot.Add(new Leaf("Leaf C"));
Leaf lLeaf = new Leaf("Leaf D");
lRoot.Add(lLeaf);
lRoot.Remove(lLeaf);
// Recursively display tree
lRoot.Display(1);
}
}
public abstract class Component
{
protected string _Name;
public Component(string prName)
{
_Name = prName;
}
public abstract void Display(int prDepth);
}
public class Composite : Component
{
private List<Component> _Children = new List<Component>();
public Composite(string prName)
: base(prName)
{
}
public void Add(Component prComponent)
{
_Children.Add(prComponent);
}
public void Remove(Component prComponent)
{
_Children.Remove(prComponent);
}
public override void Display(int prDepth)
{
Console.WriteLine(new String('-', prDepth) + _Name);
// Recursively display child nodes
foreach (Component lComponentCurrent in _Children)
{
lComponentCurrent.Display(prDepth + 2);
}
}
}
public class Leaf : Component
{
// Constructor
public Leaf(string prName)
: base(prName)
{
}
public override void Display(int prDepth)
{
Console.WriteLine(new String('-', prDepth) + _Name);
}
}
Output
Real-world Example ๐ฅ
public static class CompositePractical
{
public static void Execute()
{
ManagementMember lDirector = new ManagementMember("Director");
ManagementMember lManagerDeptA = new ManagementMember("Manager Department A");
lManagerDeptA.Add(new StaffMember("Staff 1 Department A"));
lManagerDeptA.Add(new StaffMember("Staff 2 Department A"));
ManagementMember lSupervisorDeptA = new ManagementMember("Supervisor Department A");
lSupervisorDeptA.Add(new StaffMember("Staff 3 Department A"));
lSupervisorDeptA.Add(new StaffMember("Staff 4 Department A"));
lManagerDeptA.Add(lSupervisorDeptA);
ManagementMember lManagerDeptB = new ManagementMember("Manager Department B");
lManagerDeptB.Add(new StaffMember("Staff 1 Department B"));
lManagerDeptB.Add(new StaffMember("Staff 2 Department B"));
lManagerDeptB.Add(new StaffMember("Staff 3 Department B"));
lManagerDeptB.Add(new StaffMember("Staff 4 Department B"));
lDirector.Add(lManagerDeptA);
lDirector.Add(lManagerDeptB);
lDirector.Display(1);
}
}
public abstract class Employee
{
protected string _Name;
public Employee(string prName)
{
_Name = prName;
}
public abstract void Display(int prDepth);
}
public class ManagementMember : Employee
{
private List<Employee> _Employees = new List<Employee>();
public ManagementMember(string prName)
: base(prName)
{
}
public void Add(Employee prEmployee)
{
_Employees.Add(prEmployee);
}
public void Remove(Employee prEmployee)
{
_Employees.Remove(prEmployee);
}
public override void Display(int prDepth)
{
Console.WriteLine(new String('-', prDepth) + _Name);
// Recursively display child nodes
foreach (Employee lEmployeeCurrent in _Employees)
{
lEmployeeCurrent.Display(prDepth + 2);
}
}
}
public class StaffMember : Employee
{
// Constructor
public StaffMember(string prName)
: base(prName)
{
}
public override void Display(int prDepth)
{
Console.WriteLine(new String('-', prDepth) + _Name);
}
}
Output
4. Decorator
Objective ๐ฏ
Allows to add/attach new features/behaviours to an object dynamically.
Notes ๐
This design pattern is also known as โWrapperโ.
UML ๐
Participants ๐
โข Component:
- Defines the properties and methods for objects that can have responsibilities/features added to them dynamically
โข Concrete Component:
- Implements Component interface
โข Decorator:
- Maintains reference to a Component object
- Defines an interface that conforms to Componentโs interface
โข Concrete Decorator:
- Adds responsibilities to the component
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class DecoratorStructural
{
public static void Execute()
{
ConcreteComponent lConcreteComponent = new ConcreteComponent();
ConcreteDecoratorA lConcreteDecoratorA = new ConcreteDecoratorA();
ConcreteDecoratorB lConcreteDecoratorB = new ConcreteDecoratorB();
lConcreteDecoratorA.SetComponent(lConcreteComponent);
lConcreteDecoratorB.SetComponent(lConcreteDecoratorA);
lConcreteDecoratorB.Operation();
}
}
public abstract class Component
{
public abstract void Operation();
}
public class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("ConcreteComponent - Executing Operation");
}
}
public abstract class Decorator : Component
{
protected Component _Component;
public void SetComponent(Component prComponent)
{
_Component = prComponent;
}
public override void Operation()
{
if (_Component != null)
_Component.Operation();
}
}
public class ConcreteDecoratorA : Decorator
{
public override void Operation()
{
base.Operation();
Console.WriteLine("ConcreteDecoratorA - Executing Operation");
}
}
public class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
Console.WriteLine("ConcreteDecoratorB - Executing Operation");
}
}
Output
Real-world Example ๐ฅ
public static class DecoratorPractical
{
public static void Execute()
{
Margherita lMargherita = new Margherita();
BarbequeDecorator lBarbequeDecorator = new BarbequeDecorator(lMargherita);
JalapenoDecorator lJalapenoDecorator = new JalapenoDecorator(lBarbequeDecorator);
Console.WriteLine(lJalapenoDecorator.GetDescription() + " | Cost: " + lJalapenoDecorator.GetCost());
Pepperoni lPepperoni = new Pepperoni();
BarbequeDecorator lBarbequeDecoratorPepperoni = new BarbequeDecorator(lPepperoni);
JalapenoDecorator lJalapenoDecoratorPepperoni = new JalapenoDecorator(lBarbequeDecoratorPepperoni);
OnionDecorator lOnionDecorator = new OnionDecorator(lJalapenoDecoratorPepperoni);
Console.WriteLine(lOnionDecorator.GetDescription() + " | Cost: " + lOnionDecorator.GetCost());
}
}
public abstract class PizzaItem
{
public string Name = "Unknown Pizza";
public string Description = "";
public abstract string GetDescription();
public abstract int GetCost();
}
public class Margherita : PizzaItem
{
public override string GetDescription() { return "Marguerita"; }
public override int GetCost() { return 20; }
}
public class Pepperoni : PizzaItem
{
public override string GetDescription() { return "Pepperoni"; }
public override int GetCost() { return 30; }
}
public abstract class ToppingDecorator : PizzaItem
{
protected PizzaItem _PizzaItem;
public ToppingDecorator(PizzaItem prPizzaItem)
{
_PizzaItem = prPizzaItem;
}
}
public class BarbequeDecorator : ToppingDecorator
{
public BarbequeDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }
public override string GetDescription() { return _PizzaItem.GetDescription() + ", Barbeque"; }
public override int GetCost() { return _PizzaItem.GetCost() + 5; }
}
public class JalapenoDecorator : ToppingDecorator
{
public JalapenoDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }
public override string GetDescription() { return _PizzaItem.GetDescription() + ", Jalapeno"; }
public override int GetCost() { return _PizzaItem.GetCost() + 5; }
}
public class OnionDecorator : ToppingDecorator
{
public OnionDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }
public override string GetDescription() { return _PizzaItem.GetDescription() + ", Onion"; }
public override int GetCost() { return _PizzaItem.GetCost() + 5; }
}
Output
This is what happens when we decorate the Pepperoni Pizza:
5. Facade
Objective ๐ฏ
Hide the complexity of the subsystems by providing a simplified interface to the client.
UML ๐
Participants ๐
โข Facade:
- Provides simplified methods to be executed by subsystem objects
- Knows which subsystem classes are responsible for a request
โข Subsystem Classes:
- Execute some specific funcionality
- Have no knowledge of the facade and keep no reference to it
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class FacadeStructural
{
public static void Execute()
{
Facade lFacade = new Facade();
lFacade.MethodA();
lFacade.MethodB();
}
}
public class Facade
{
private SubSystemOne _SubSystemOne;
private SubSystemTwo _SubSystemTwo;
private SubSystemThree _SubSystemThree;
private SubSystemFour _SubSystemFour;
public Facade()
{
_SubSystemOne = new SubSystemOne();
_SubSystemTwo = new SubSystemTwo();
_SubSystemThree = new SubSystemThree();
_SubSystemFour = new SubSystemFour();
}
public void MethodA()
{
Console.WriteLine("Facade - Executing Method A");
_SubSystemOne.MethodOne();
_SubSystemFour.MethodFour();
}
public void MethodB()
{
Console.WriteLine("Facade - Executing Method B");
_SubSystemTwo.MethodTwo();
_SubSystemThree.MethodThree();
}
}
public class SubSystemOne
{
public void MethodOne()
{
Console.WriteLine("SubSystemOne - Executing Method One");
}
}
public class SubSystemTwo
{
public void MethodTwo()
{
Console.WriteLine("SubSystemTwo - Executing Method Two");
}
}
public class SubSystemThree
{
public void MethodThree()
{
Console.WriteLine("SubSystemThree - Executing Method Three");
}
}
public class SubSystemFour
{
public void MethodFour()
{
Console.WriteLine("SubSystemFour - Executing Method Four");
}
}
Output
Real-world Example ๐ฅ
public static class FacadePractical
{
public static void Execute()
{
OrderFacade lOrderFacade = new OrderFacade();
lOrderFacade.OrderFood();
}
}
public class OrderFacade
{
private Waiter _Waiter;
private Kitchen _Kitchen;
public OrderFacade()
{
_Waiter = new Waiter();
_Kitchen = new Kitchen();
}
public void OrderFood()
{
_Waiter.WriteOrder();
_Waiter.SendToKitchen();
_Kitchen.PrepareFood();
_Kitchen.CallWaiter();
_Waiter.ServerCustomer();
_Kitchen.WashDishes();
}
}
public class Waiter
{
public void WriteOrder()
{
Console.WriteLine("Waiter - Writting Order...");
}
public void SendToKitchen()
{
Console.WriteLine("Waiter - Sending To Kitchen...");
}
public void ServerCustomer()
{
Console.WriteLine("Waiter - Serving Customer...");
}
}
public class Kitchen
{
public void PrepareFood()
{
Console.WriteLine("Kitchen - Preparing Food...");
}
public void CallWaiter()
{
Console.WriteLine("Kitchen - Calling Waiter...");
}
public void WashDishes()
{
Console.WriteLine("Kitchen - Washing Dishes...");
}
}
Output
6. Flyweight
Objective ๐ฏ
Allow to put more objects into the available memory RAM by sharing common parts between multiple objects.
Notes ๐
โข Flyweight object has a single-instance (singleton) and can be reused by other objects.
โข Intrinsic state means that the object can be shared as there is not a โspecificโ property in the object that makes it unique. We can consider it also as contextless.
โข Extrinsic state is the opposite of intrinsic, it means that the object has one or more properties that are exclusive of a single instance (context), what prevents the object from being shared.
โข To use a web browser analogy, a single cached image can be used in different places just varying its size and position, in this scenario the URL never changes (intrinsic property) while the size and position change (extrinsic property).
UML ๐
Participants ๐
โข Flyweight:
- Declares an interface to allow flyweights objects to receive extrinsic state (property) and act on it.
โข ConcreteFlyweight:
- Implements the Flyweight interface
- Represents a shareable object
โข UnsharedFlyweight:
- Implements the Flyweight interface
- Represents a unshareable object
โข FlyweightFactory:
- Creates and manage flyweight objects
- Makes sure flyweight objects are created and shared properly
โข Client:
- Maintains a reference to flyweight(s) object(s)
- Calls the FlyweightFactory to get shared objects
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class FlyweightStructural
{
public static void Execute()
{
int lContextVariable = 22;
FlyweightFactory lFlyweightFactory = new FlyweightFactory();
Flyweight lFlyweightX = lFlyweightFactory.GetFlyweight("X");
Flyweight lFlyweightY = lFlyweightFactory.GetFlyweight("Y");
Flyweight lFlyweightZ = lFlyweightFactory.GetFlyweight("Z");
Flyweight lFlyweightXAgain = lFlyweightFactory.GetFlyweight("X");
lFlyweightX.Operation(--lContextVariable);
lFlyweightY.Operation(--lContextVariable);
lFlyweightZ.Operation(--lContextVariable);
lFlyweightZ.Operation(--lContextVariable);
UnsharedConcreteFlyweight lUnsharedConcreteFlyweight = new UnsharedConcreteFlyweight();
lUnsharedConcreteFlyweight.Operation(--lContextVariable);
}
}
public class FlyweightFactory
{
private Hashtable _Flyweights = new Hashtable();
public FlyweightFactory()
{
_Flyweights.Add("X", new ConcreteFlyweight());
_Flyweights.Add("Y", new ConcreteFlyweight());
_Flyweights.Add("Z", new ConcreteFlyweight());
}
public Flyweight GetFlyweight(string prKey)
{
return ((Flyweight)_Flyweights[prKey]);
}
}
public abstract class Flyweight
{
public abstract void Operation(int prUniqueState);
}
public class ConcreteFlyweight : Flyweight
{
public override void Operation(int prUniqueState)
{
Console.WriteLine("ConcreteFlyweight - Context Property: " + prUniqueState);
}
}
public class UnsharedConcreteFlyweight : Flyweight
{
public override void Operation(int prUniqueState)
{
Console.WriteLine("UnsharedConcreteFlyweight - Context Property: " + prUniqueState);
}
}
Output
Real-world Example ๐ฅ
public static class FlyweightPractical
{
public static void Execute()
{
ShapeFactory lShapeFactory = new ShapeFactory();
Circle lCircle1 = (Circle)lShapeFactory.GetShape("Circle", "Red");
lCircle1.Draw(40, 65);
Circle lCircle2 = (Circle)lShapeFactory.GetShape("Circle", "Blue");
lCircle2.Draw(78,32);
Circle lCircle3 = (Circle)lShapeFactory.GetShape("Circle", "Blue");
lCircle3.Draw(15,67);
Square lSquare1 = (Square)lShapeFactory.GetShape("Square", "Red");
lSquare1.Draw(99,44);
Square lSquare2 = (Square)lShapeFactory.GetShape("Square", "Red");
lSquare2.Draw(88,42);
Triangle lTriangle1 = (Triangle)lShapeFactory.GetShape("Triangle", "Yellow");
lTriangle1.Draw(89,34);
Triangle lTriangle2 = (Triangle)lShapeFactory.GetShape("Triangle", "White");
lTriangle2.Draw(64,24);
}
}
public class ShapeFactory
{
private Hashtable _Shapes = new Hashtable();
public Shape GetShape(string prShape, string prColor)
{
string lUniqueKey = prShape + prColor;
Shape lResult = ((Shape)_Shapes[lUniqueKey]);
if (lResult == null)
{
switch (prShape)
{
case ("Circle"): _Shapes.Add(lUniqueKey, new Circle()); Console.WriteLine($"Creating Shape \"{prColor} {prShape}\""); break;
case ("Square"): _Shapes.Add(lUniqueKey, new Square()); Console.WriteLine($"Creating Shape \"{prColor} {prShape}\""); break;
case ("Triangle"): _Shapes.Add(lUniqueKey, new Triangle()); Console.WriteLine($"Creating Shape \"{prColor} {prShape}\""); break;
default: break;
}
return ((Shape)_Shapes[lUniqueKey]);
}
else
{
Console.WriteLine($"Shape \"{prColor} {prShape}\" already created, retrieving from cache");
return lResult;
}
}
}
public abstract class Shape
{
public string _Color { get; set; }
public abstract void Draw(int prX, int prY);
}
public class Circle : Shape
{
public override void Draw(int prX, int prY)
{
Console.WriteLine($"---Drawing {this._Color} Circle in position: X = {prX}, Y = {prY}");
}
}
public class Square : Shape
{
public override void Draw(int prX, int prY)
{
Console.WriteLine($"---Drawing {this._Color} Square in position: X = {prX}, Y = {prY}");
}
}
public class Triangle : Shape
{
public override void Draw(int prX, int prY)
{
Console.WriteLine($"---Drawing {this._Color} Triangle in position: X = {prX}, Y = {prY}");
}
}
Output
7. Proxy
Objective ๐ฏ
Provide a substitute for another object to control access to it.
UML ๐
Participants ๐
โข Subject:
- Defines an interface common for both Real and Proxy subject so that Proxy can be used anywhere a Real object is expected.
โข RealSubject:
- Implements the real operations that proxy will represent.
โข Proxy:
- Maintains a reference to RealSubject
- Forward method calls to RealSubject
Sample Code ๐ฎ
Structural Example ๐๏ธ
public static class ProxyStructural
{
public static void Execute()
{
Proxy lProxy = new Proxy();
lProxy.Request();
}
}
internal abstract class Subject
{
public abstract void Request();
}
internal class RealSubject : Subject
{
public override void Request()
{
Console.WriteLine("Called RealSubject.Request()");
}
}
internal class Proxy : Subject
{
private RealSubject _RealSubject;
public override void Request()
{
if (_RealSubject == null)
_RealSubject = new RealSubject();
_RealSubject.Request();
}
}
Output
Real-world Example ๐ฅ
namespace Main.Proxy
{
public static class ProxyPractical
{
public static void Execute()
{
Calculator lCalculator = new ProxyCalculator();
Console.WriteLine($"Proxy Calculator - Calling method \"Add\" in the Real Calculator... Result: 5 + 7 = {lCalculator.Add(5,7)}");
Console.WriteLine($"Proxy Calculator - Calling method \"Add\" in the Real Calculator... Result: 5 - 7 = {lCalculator.Subtract(5, 7)}");
Console.WriteLine($"Proxy Calculator - Calling method \"Add\" in the Real Calculator... Result: 5 * 7 = {lCalculator.Multiply(5, 7)}");
Console.WriteLine($"Proxy Calculator - Calling method \"Add\" in the Real Calculator... Result: 5 / 7 = {lCalculator.Divide(5, 7)}");
}
}
public abstract class Calculator
{
public abstract float Add(float prNumber1, float prNumber2);
public abstract float Subtract(float prNumber1, float prNumber2);
public abstract float Multiply(float prNumber1, float prNumber2);
public abstract float Divide(float prNumber1, float prNumber2);
}
public class RealCalculator : Calculator
{
public override float Add(float prNumber1, float prNumber2)
{
return prNumber1 + prNumber2;
}
public override float Divide(float prNumber1, float prNumber2)
{
return prNumber1 / prNumber2;
}
public override float Multiply(float prNumber1, float prNumber2)
{
return prNumber1 * prNumber2;
}
public override float Subtract(float prNumber1, float prNumber2)
{
return prNumber1 - prNumber2;
}
}
public class ProxyCalculator : Calculator
{
private RealCalculator _RealCalculator;
public ProxyCalculator()
{
_RealCalculator = new RealCalculator();
}
public override float Add(float prNumber1, float prNumber2)
{
return _RealCalculator.Add(prNumber1, prNumber2);
}
public override float Divide(float prNumber1, float prNumber2)
{
return _RealCalculator.Divide(prNumber1, prNumber2);
}
public override float Multiply(float prNumber1, float prNumber2)
{
return _RealCalculator.Multiply(prNumber1, prNumber2);
}
public override float Subtract(float prNumber1, float prNumber2)
{
return _RealCalculator.Subtract(prNumber1, prNumber2);
}
}
}
Output