GoF Structural Design Patterns

GoF Structural Design Patterns

Software Architecture Simplified

ยท

16 min read

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?

image.png

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)

image.png

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.

image.png

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.

image.png

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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 image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

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โ€

image.png

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

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 ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

4. Decorator

Objective ๐ŸŽฏ

Allows to add/attach new features/behaviours to an object dynamically.

Notes ๐Ÿ“

This design pattern is also known as โ€œWrapperโ€.

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

This is what happens when we decorate the Pepperoni Pizza:

image.png

5. Facade

Objective ๐ŸŽฏ

Hide the complexity of the subsystems by providing a simplified interface to the client.

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

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.

image.png

โ€ข 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).

image.png

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

7. Proxy

Objective ๐ŸŽฏ

Provide a substitute for another object to control access to it.

image.png

UML ๐Ÿ“

image.png

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 ๐Ÿ›๏ธ

image.png

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

image.png

Real-world Example ๐Ÿ”ฅ

image.png

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

image.png

Did you find this article valuable?

Support Victor Lins by becoming a sponsor. Any amount is appreciated!

ย