Structural design pattern

 1. Decorator Design Pattern

Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.

Decorator Pattern is basically used to add new functionality to an existing class without changing the original class [ following the open — close principle]

“Wrapper” is the alternative nickname for the Decorator pattern that clearly expresses the main idea of the pattern. A wrapper is an object that can be linked with some target object. The wrapper contains the same set of methods as the target and delegates to it all requests it receives. However, the wrapper may alter the result by doing something either before or after it passes the request to the target.

Example:

Initial Setup of our class [ say for ex we have a Car Class ]

one abstract function which we will implement later on which gives us the price of the car we want to purchase.

  public abstract class Car
    {
        public string carName;
        public string carColor;

        public virtual string GetCarName()
        {
            return carName;
        }

        public string GetCarColor()
        {
            return carColor;
        }

        public abstract int GetCarPrice();
    }

BMW which will extend our class CAR. now in BMW class we will give the name , colour and will also implement our abstract function to give the initial price of the car without any add ons

 public class Bmw : Car
    {
        public Bmw()
        {
            carName = "BMW";
            carColor = "RED";
        }

        public override int GetCarPrice()
        {
            return 200;
        }
    }

let’s create one more car say -> AUDI

 public class Audi : Car
    {
        public Audi()
        {
            carName = "Audi";
            carColor = "Blue";
        }

        public override int GetCarPrice()
        {
            return 100;
        }
    }

We want to have some add on features with our car, say for ex -> Music System , AIR Bags etc ;

now one way is to add them directly to each car but what if there are 10,000 cars then it would not make any sense.so we will first create a decorator class which we will use whenever we want to add new feature to our car class

 public abstract class CarDecorator : Car
    {
        protected Car car;
        protected CarDecorator(Car car)
        {
            this.car = car;
        }
    }

Now for every feature we want to add we can create a class that extends this decorator class and then modify our price accordingly so that we can get the exact price for the car after adding new items

Music-System class :

 public class MusicSystemFeature : CarDecorator
    {
        public MusicSystemFeature(Car car):base(car)
        {
        }

        public override string GetCarName()
        {
            return car.GetCarName() + " with music system";
        }

        public override int GetCarPrice()
        {
            return car.GetCarPrice() + 30;
        }
    }

let’s create our air bag class as well :

 public class AirBagFeature : CarDecorator
    {
        public AirBagFeature(Car car):base(car)
        {
        }

        public override string GetCarName()
        {
            return car.GetCarName() + " with AirBag";
        }

        public override int GetCarPrice()
        {
            return car.GetCarPrice() + 50;
        }
    }

Now for every feature we want to add we can create a class that extends this decorator class and then modify our price accordingly so that we can get the exact price for the car after adding new items. Now here we use on instance of our car class and now we can update the name of the car as well as add the price of the feature. lets see how our decorators help us to add feature to our own class without changing the base class

 class Program
    {
        static void Main(string[] args)
        {
            Car car = new Bmw();
            Console.WriteLine("Car Name " + car.GetCarName());
            Console.WriteLine("Car Price " + car.GetCarPrice());

            Console.WriteLine("Adding Music system with Car");
            car = new MusicSystemFeature(car);
            Console.WriteLine("Car Name " + car.GetCarName());
            Console.WriteLine("Car Price " + car.GetCarPrice());

            Console.WriteLine("Adding Music system with AirBag");
            car = new AirBagFeature(car);
            Console.WriteLine("Car Name " + car.GetCarName());
            Console.WriteLine("Car Price " + car.GetCarPrice());
        }
    }

Output:

Car Name BMW
Car Price 200
Adding Music system with Car
Car Name BMW with music system
Car Price 230
Adding Music system with AirBag
Car Name BMW with music system with AirBag
Car Price 280

Class Diagram


    2. Adapter Design Pattern

    Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.

    Problem
    Imagine that you’re creating a stock market monitoring app. The app downloads the stock data from multiple sources in XML format and then displays nice-looking charts and diagrams for the user.

    At some point, you decide to improve the app by integrating a smart 3rd-party analytics library. But there’s a catch: the analytics library only works with data in JSON format.

    You could change the library to work with XML. However, this might break some existing code that relies on the library. And worse, you might not have access to the library’s source code in the first place, making this approach impossible.

    Solution
    You can create an adapter. This is a special object that converts the interface of one object so that another object can understand it. An adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes. The wrapped object isn’t even aware of the adapter.

    Adapters can not only convert data into various formats but can also help objects with different interfaces collaborate. Here’s how it works:
    1. The adapter gets an interface, compatible with one of the existing objects.
    2. Using this interface, the existing object can safely call the adapter’s methods.
    3. Upon receiving a call, the adapter passes the request to the second object, but in a format and order that the second object expects.
    Sometimes it’s even possible to create a two-way adapter that can convert the calls in both directions.


    Class Diagram



    Sample Code

    using System;
    
    namespace Adapter.Structural
    {
        /// <summary>
        /// The 'Target' class
        /// </summary>
    
        public class Target
        {
            public virtual void Request()
            {
                Console.WriteLine("Called Target Request()");
            }
        }
    
        /// <summary>
        /// The 'Adapter' class
        /// </summary>
    
        public class Adapter : Target
        {
            private Adaptee adaptee = new Adaptee();
    
            public override void Request()
            {
                // Possibly do some other work
                // and then call SpecificRequest
    
                adaptee.SpecificRequest();
            }
        }
    
        /// <summary>
        /// The 'Adaptee' class
        /// </summary>
    
        public class Adaptee
        {
            public void SpecificRequest()
            {
                Console.WriteLine("Called SpecificRequest()");
            }
        }
    	
         /// <summary>
        /// Adapter Design Pattern
        /// </summary>
    
        public class Program
        {
            public static void Main(string[] args)
            {
                // Create adapter and place a request
    
                Target target = new Adapter();
                target.Request();
    
                // Wait for user
    
                Console.ReadKey();
            }
        }
    }

    3. Bridge Design Pattern


    Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other.
    Problem

    Say you have a geometric Shape class with a pair of subclasses: Circle and Square. You want to extend this class hierarchy to incorporate colors, so you plan to create Red and Blue shape subclasses. However, since you already have two subclasses, you’ll need to create four class combinations such as BlueCircle and RedSquare.



    Adding new shape types and colors to the hierarchy will grow it exponentially. For example, to add a triangle shape you’d need to introduce two subclasses, one for each color. And after that, adding a new color would require creating three subclasses, one for each shape type. The further we go, the worse it becomes.

    Solution
    This problem occurs because we’re trying to extend the shape classes in two independent dimensions: by form and by color. That’s a very common issue with class inheritance.

    The Bridge pattern attempts to solve this problem by switching from inheritance to the object composition. What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class.



    The Shape class then gets a reference field pointing to one of the color objects. Now the shape can delegate any color-related work to the linked color object. That reference will act as a bridge between the Shape and Color classes. From now on, adding new colors won’t require changing the shape hierarchy, and vice versa.

    Class Diagram


    Code

    First, we define an interface for the abstraction:
    public interface IShape
    {
        void Draw();
    }


    Next, we define an abstract class for the implementation:

    public abstract class Shape
    {
        protected IShape _shape;
    
        public Shape(IShape shape)
        {
            _shape = shape;
        }
    
        public abstract void Draw();
    }
    In this class, we have a protected field for the abstraction (_shape) and a constructor that takes an instance of the abstraction. We also have an abstract Draw method that will be implemented by subclasses.

    Next, we define some concrete implementations of the abstraction:

    public class Circle : IShape
    {
        public void Draw()
        {
            Console.WriteLine("Drawing Circle");
        }
    }
    
    public class Square : IShape
    {
        public void Draw()
        {
            Console.WriteLine("Drawing Square");
        }
    }

    Finally, we define some subclasses of the Shape abstract class that implement the Draw method using the abstraction:


    public class RedShape : Shape
    {
        public RedShape(IShape shape) : base(shape)
        {
        }
    
        public override void Draw()
        {
            Console.ForegroundColor = ConsoleColor.Red;
            _shape.Draw();
            Console.ResetColor();
        }
    }
    
    public class BlueShape : Shape
    {
        public BlueShape(IShape shape) : base(shape)
        {
        }
    
        public override void Draw()
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            _shape.Draw();
            Console.ResetColor();
        }
    }
    In these subclasses, we override the Draw method and use the abstraction (_shape) to draw the shape. We also add some extra color to the output.

    To use this pattern, we can create instances of the concrete implementations of the abstraction and pass them to instances of the subclasses of the Shape abstract class:


    var circle = new Circle();
    var square = new Square();
    
    var redCircle = new RedShape(circle);
    var blueSquare = new BlueShape(square);
    
    redCircle.Draw();   // Output: Drawing Circle in red
    blueSquare.Draw();  // Output: Drawing Square in blue


    4. Facade Design Pattern

    The Facade pattern is a structural design pattern that provides a unified interface to a set of interfaces in a subsystem. It is used to simplify a complex system by providing a simple interface to the client code.

    Problem
    Imagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, you’d need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on.

    As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain.

    Solution
    A facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. A facade might provide limited functionality in comparison to working with the subsystem directly. However, it includes only those features that clients really care about.

    Having a facade is handy when you need to integrate your app with a sophisticated library that has dozens of features, but you just need a tiny bit of its functionality.

    For instance, an app that uploads short funny videos with cats to social media could potentially use a professional video conversion library. However, all that it really needs is a class with the single method encode(filename, format). After creating such a class and connecting it with the video conversion library, you’ll have your first facade.

    Example
    When you call a shop to place a phone order, an operator is your facade to all services and departments of the shop. The operator provides you with a simple voice interface to the ordering system, payment gateways, and various delivery services.

    Code :
    In C#, the Factory Design Pattern can be implemented as follows:
    1. Define an interface or an abstract class that will serve as the product of the factory:
    public interface ISubSystemA
    {
        void OperationA();
    }
    
    public interface ISubSystemB
    {
        void OperationB();
    }
    
    public interface ISubSystemC
    {
        void OperationC();
    }

    2. Implement the subsystem classes: These are the classes that implement the subsystem interfaces.

    public class SubSystemA : ISubSystemA
    {
        public void OperationA()
        {
            Console.WriteLine("SubSystemA OperationA");
        }
    }
    
    public class SubSystemB : ISubSystemB
    {
        public void OperationB()
        {
            Console.WriteLine("SubSystemB OperationB");
        }
    }
    
    public class SubSystemC : ISubSystemC
    {
        public void OperationC()
        {
            Console.WriteLine("SubSystemC OperationC");
        }
    }

    3. Define the facade class: This class provides a simple interface to the client code and delegates the calls to the appropriate subsystem classes.


    public class Facade
    {
        private readonly ISubSystemA _subSystemA;
        private readonly ISubSystemB _subSystemB;
        private readonly ISubSystemC _subSystemC;
    
        public Facade(ISubSystemA subSystemA, ISubSystemB subSystemB, ISubSystemC subSystemC)
        {
            _subSystemA = subSystemA;
            _subSystemB = subSystemB;
            _subSystemC = subSystemC;
        }
    
        public void Operation()
        {
            _subSystemA.OperationA();
            _subSystemB.OperationB();
            _subSystemC.OperationC();
        }
    }


    4. Use the facade class to simplify the client code:

    static void Main(string[] args)
    {
        var subSystemA = new SubSystemA();
        var subSystemB = new SubSystemB();
        var subSystemC = new SubSystemC();
    
        var facade = new Facade(subSystemA, subSystemB, subSystemC);
        facade.Operation();
    }

    In the example above, the client code only needs to create an instance of the Facade class and call its Operation method. The Facade class then delegates the calls to the appropriate subsystem classes.


    Class Diagram

    5. Proxy Design Pattern

    Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.

    Problem
    Why would you want to control access to an object? Here is an example: you have a massive object that consumes a vast amount of system resources. You need it from time to time, but not always.



    You could implement lazy initialization: create this object only when it’s actually needed. All of the object’s clients would need to execute some deferred initialization code. Unfortunately, this would probably cause a lot of code duplication.

    In an ideal world, we’d want to put this code directly into our object’s class, but that isn’t always possible. For instance, the class may be part of a closed 3rd-party library.

    Solution
    The Proxy pattern suggests that you create a new proxy class with the same interface as an original service object. Then you update your app so that it passes the proxy object to all of the original object’s clients. Upon receiving a request from a client, the proxy creates a real service object and delegates all the work to it.


    But what’s the benefit? If you need to execute something either before or after the primary logic of the class, the proxy lets you do this without changing that class. Since the proxy implements the same interface as the original class, it can be passed to any client that expects a real service object.

    Real-World Analogy

    A credit card is a proxy for a bank account, which is a proxy for a bundle of cash. Both implement the same interface: they can be used for making a payment. A consumer feels great because there’s no need to carry loads of cash around. A shop owner is also happy since the income from a transaction gets added electronically to the shop’s bank account without the risk of losing the deposit or getting robbed on the way to the bank.

    Code:


    using System;
    
    // Interface for the subject and the proxy
    public interface ISubject
    {
        void Request();
    }
    
    // Real subject class
    public class RealSubject : ISubject
    {
        public void Request()
        {
            Console.WriteLine("RealSubject: Handling request.");
        }
    }
    
    // Proxy class
    public class Proxy : ISubject
    {
        private RealSubject _realSubject;
    
        public void Request()
        {
            if (_realSubject == null)
            {
                _realSubject = new RealSubject();
            }
    
            Console.WriteLine("Proxy: Handling request.");
            _realSubject.Request();
        }
    }
    
    // Client code
    class Program
    {
        static void Main(string[] args)
        {
            // Creating a proxy object
            Proxy proxy = new Proxy();
    
            // Client interacts with the proxy object, not the real subject object
            proxy.Request();
        }
    }

    In the example above, we have an interface ISubject that both the RealSubject and Proxy classes implement. The RealSubject class is the object that the client wants to interact with, but instead of directly accessing the RealSubject object, the client interacts with the Proxy object.

    The Proxy class contains a reference to the RealSubject object and forwards the client's requests to the RealSubject object. If the RealSubject object doesn't exist yet, the Proxy creates it.

    When the client calls the Request() method on the Proxy object, the Proxy first handles the request and then forwards it to the RealSubject object.

    This allows the Proxy object to control access to the RealSubject object, for example, by adding additional security checks or caching results.


    Class diagram:



    No comments:

    Post a Comment