ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OOP Design Pattern - Structural Pattern
    프로그래밍 언어 2024. 1. 18. 21:33

    Adapter

    Adapter Pattern

    • Allow the interface of an existing class to be used as another interface
    • Allow two incompatible interfaces to work together
      1. Interfaces may be imcompatible, but the inner functionality should suit the need
      2. By converting the interface of one class into an interface
    • Often used to make existing classes work with others without modifying their source code

     

    Overview

    • Solves problem like:
      1. How can a class be reused that does not have an interface that a client requires?
      2. How can classes that have incompatible interfaces work together?
      3. How can an alternative interface be provided for a class?
    • Describes how to solve such problems:
      1. Define a separate adapter class that converts the (incompatible) interface of a class (adaptee) into another interface (target) clients require.
      2. Work through an adapter to work with (reuse) classes that do not have the required interface.
    • Clients do not know whether they work with a target class directly or through an adapter

    Class UML of adapter pattern

     

    Usage

    • Used when the wrapper must respect a particular interface and must support polymorphic behavior

     

    Structure

    • The client works through an adapter class that implements the target interface in terms of adaptee
    • The object adapter way implements the target interface by delegating to an adaptee object at run-time
    • The class adapter way implements the target interface by ineriting from an adaptee class at compile-time

    Example

    public interface ILightningPhone
    {
        void ConnectLightning();
        void Recharge();
    }
    
    public interface IUsbPhone
    {
        void ConnectUsb();
        void Recharge();
    }
    
    public sealed class AndroidPhone : IUsbPhone
    {
        private bool isConnected;
    		
        public void ConnectUsb() { this.isConnected = true;	}
        public void Recharge()
        {
            if(this.isConnected) Console.WriteLine("Android recharging");
            else Console.WriteLine("Connect the USB");
        }
    }
    
    public sealed class ApplePhone : ILightningPhone
    {
        private bool isConnected;
    
        public void ConnectLightning() { this.isConnected = true; }
    
        public void Recharge()
        {
            if(this.isConnected) Console.WriteLine("Apple rechargine");
            else Console.WriteLine("Connect the Lightning");
        }
    }
    
    public sealed class LightningToUsbAdapter : IUsbPhone
    {
        private readonly ILightningPhone lightningPhone;
    
        private bool isConnected;
    
        public LightningToUsbAdapter(ILightningPhone p_lightningPhone)
        {
    	    this.lightningPhone = p_lightningPhone;
    	    this.lightningPhone.ConnectLightning();
        }
    
        public void ConnectUsb() { this.isConnected = true; }
    
        public void Recharge()
        {
            if(this.isConnected) this.lightningPhone.Recharge();
            else Console.WriteLine("Connect the USB");
        }
    }
    
    public void Main()
    {
        ILightningPhone applePhone = new ApplePhone();
        IUsbPhone adapterCable = new LightningToUsbAdapter(applePhone);
        adapterCable.ConnectUsb();
        adapterCable.Recharge();
    }

     

    Bridge

    Bridge Pattern

    • Decouple an abstraction from its implementation so that the two can vary independently
    • The bridge uses encapsulation, aggregation, and inheritance to separate responsibilities into different classes
    • Useful when both the class and what it does vary often

     

    Overview

    • Solves problem like:
      1. An abstraction and its implementation should be defined and extended independently from each other
      2. A compile-time binding between an abstraction and its implementation should be avoided so that an implementation can be selected at run-time
    • Describes how to solve such problems:
      1. Separate an abstraction from its implementation by putting them in separate class hierarchies
      2. Implement the abstraction in terms of (by delegating to) an implementor object, enable to configure an abstraction with an implementor object at run-time

    Class UML of bridge pattern

     

    Example

    public interface IBridge // Helps in providing truly decoupled architecture
    {
        void Function1();
        void Function2();
    }
    
    public class Bridge1 : IBridge
    {
        public void Function1() { Console.WriteLine("Bridge1.Function1"); }
        public void Function2() { Console.WriteLine("Bridge1.Function2"); }
    }
    
    public class Bridge2 : IBridge
    {
        public void Function1() { Console.WriteLine("Bridge2.Function1"); }
        public void Function2() { Console.WriteLine("Bridge2.Function2"); }
    }
    
    public interface IAbstractBridge
    {
        void CallMethod1();
        void CallMethod2();
    }
    
    public class AbstractBridge : IAbstractBridge
    {
        public IBridge bridge;
    
        public AbstractBridge(IBridge p_bridge) { this.bridge = p_bridge; }
    
        public void CallMethod1() { this.bridge.Function1(); }
        public void CallMethod2() { this.bridge.Function2(); }
    }

     

    Composite

    Composite Pattern

    • A group of objects that are treated the same way as a single instance of the same type of object
    • Compose objects into tree structures to represent part-whole hierarchies
    • Allow clients to treat individual objects and compositions uniformly

     

    Overview

    • Solves problem like:
      1. A part-whole hierarchy should be represented so that clients can treat part and whole objects uniformly
      2. A part-whole hierarchy should be represented as tree structure
    • Describes how to solve such problems:
      1. Define a unified component interface for both part (leaf) objects and whole (composite) objects
      2. Individual leaf objects implement the component interface directly
      3. Composite objects forward requests to their child components

    Class UML of composite pattern

     

    Motivation

    • Problem : have to discriminate between a leaf-node and a branch dealing with tree-structured data
    • Solution : an interface that allows treating complex and primitive objects uniformly
    • Manipulate a single instance of the object just as you would manipulate a group of them

     

    When to use

    • Should be used when clients ignore the difference between compositions of objects and individual objects

     

    Structure

    • Design for uniformity : child-related operations are defined in the component interface
    • Design for type safety : child-related operations are defined only in the composite class
    • Component
      • The abstract for all components, including composite ones
      • Declares the interface for objects in the composition
      • (Optional) Defines an interface for accessing a component's parent in the recursive structure
    • Leaf
      • Leaf objects in the composition
      • Implements all component methods
    • Composite
      • A composite component (component having children)
      • Implements methods to manipulate children
      • Implements all component methods, generally by delegating them to its children

     

    Example

    public interface IComponent // Creating an interface
    {
        void DisplayPrice();
    }
    
    public class Leaf : IComponent // Creating a leaf class
    {
        private int price;
        private string name;
    		
        public Leaf(string p_name, int p_price)
        {
            this.price = p_price;
            this.name = p_name;
        }
    
        public void DisplayPrice() { Console.WriteLine(name + " : " + price); }
    }
    
    public class Composite : IComponent // Creating a composite class
    {
        private string name;
        private List<IComponent> components = new List<IComponent>();
    
        public Composite(string p_name) { this.name = p_name; }
    
        public void AddComponent(IComponent p_component) { components.Add(p_component); }
    		
        public void DisplayPricee()
        {
            Console.WirteLine(name);
            foreach(var t_item in components)
                t_item.DisplayPrice();
        }
    }

     

    Decorator

    Decorator Pattern

    • Allow behavior to be added to an individual object, dynamically, without affecting the behaviour of other objects from the same class
    • Useful for adhering to the Single Responsibility Principle, allowing functionality to be devided between classes with unique areas of concern
    • Useful for adhering to the Open-Closed Principle, allowing the functionality of a class to be extended without being modified
    • Can be more efficient than subclassing because an object's behavior can be augmented without defining an entirely new object

     

    Overview

    • Solve problem like:
      1. Responsibilities should be added to (and removed from) an object dynamically at run-time
      2. A flexible alternative to subclassing for extending functionality should be provided
    • Describe how to solve such problem:
      1. Implement the interface of the extended (decorated) object (Component) transparently by forwording all requests to it
      2. Perform additional functionality before / after forwarding a request

    Class UML of decorator pattern

     

    Intent

    • Sequence of steps of designing a new Decorator class that wraps the original class
      1. Subclass the original Component class into a Decorator class
      2. In the Decorator class, add a Component pointer as a field
      3. In the Decorator class, pass a Component to the Decorator constructor to initialize the Component pointer;
      4. In the Decorator class, forward all Component methods to the Component pointer
      5. In the ConcreteDecorator class, override any Component method(s) whose behavior need to be modified
    • Decorators and the original class object share a common set of features
    • The decoration features are usually defined by an interface, mixin, or class inheritance

     

    Example

    public interface IBike
    {
        string GetDetails();
        double GetPrice();
    }
    
    public class AluminiumBike : IBike
    {
        public double GetPrice() => 100.0;
        public string GetDetails() => "Aluminium Bike";
    }
    
    public class CarbonBike : IBike
    {
        public double GetPrice() => 1000.0;
        public string GetDetails() => "Carbon Bike";
    }
    
    public abstract class BikeAccessories : IBike
    {
        private readonly IBike _bike;
        public BikeAccessories(IBike bike) { _bike = bike; }
        public virtual double GetPrice() => _bike.GetPrice();
        public virtual string GetDetails() => _bike.GetDetails();
    }
    
    public class SecurityPackage : BikeAccessories
    {
        public SecurityPackage(IBike bike) : base(bike) {}
        public override double GetPrice() => base.GetPrice() + 1;
        public override string GetDetails() => base.GetDetails() + " + Security Pakage";
    }
    
    public class SportPackage: BikeAccessories
    {
        public SportPackage(IBike bike) : base(bike) {}
        public override double GetPrice() => base.GetPrice() + 10;
        public override string GetDetails() => base.GetDetails() + " + Sport Pakage";
    }
    
    public class BikeShop
    {
        public static void UpgradeBike()
        {
            var basicBike = new AluminiumBike();
            BikeAccessories upgraded = new SportPackage(basicBike);
            upgraded = new SecurityPackage(upgraded);
    
            Console.WriteLine($"Bike: '{upgraded.GetDetails()}' Cost : {upgraded.GetPrice()}"):
        }
    }

     

    Facade

    Facade Pattern

    • An object that serves as a front-facing interface masking more complex underlying or structural code
    • Improve the readability and usability of a software library by masking interaction with more complex components behind a single
    • Provide a context-specific interface to more generic functionality, involving a single wrapper class that contains a set of members required by the client
    • Serve as a launching point for a broader refactor of monolithic or tightly-coupled systems in favor of more loosely-coupled code

     

    Overview

    • Solves problem like:
      1. To make a complex subsystem easier to use, a simple interface should be provided for a set of interfaces in the subsystem
      2. The dependencies on a subsystem should be minimized
    • Describes how to solve such problem:
      1. Implements a simple interface in terms of (by delegating to) the interfaces in the subsystem
      2. May perform additional functionality before / after forwarding a request

     

    Usage

    • Often used to hide the complexities of the larger system and provide a simpler interface to the client when a system is very complex or difficult to understand because the system has many interdependent classes or because its source code is unavailable
    • Used when an easier or simpler interface to an underlying object is desired
    • Used when an entry point is needed to each level of layered software
    • Used when the abstractions and implementations of a subsystem are tightly coupled

     

    Example

    struct CPU
    {
        void Freeze();
        void Jump(long position);
        void Execute();
    };
    
    struct Memory
    {
        void Load(long position, char* data);
    };
    
    struct HardDrive
    {
        char* Read(long lba, int size);
    };
    
    class ComputerFacade
    {
    private:
        CPU cpu_;
        Memory memory_;
        HardDrive hard_drive_;
    
    public:
        void Start()
        {
            cpu_Freeze();
            memory_Load(kBootAddress, hard_drive_.Read(kBootSector, kSectorSize));
            cpu_.Jump(kBootAddress);
            cpu_.Execute();
        }
    };
    
    int main()
    {
        ComputerFacade computer;
        computer.Start();
    }

     

    Flyweight

    Flyweight Pattern

    • Refers to an object that minimizes memory usage by sharing some of its data with other similar objects

     

    Overview

    • Useful when dealing with large numbers of objects with simple repeated elements that would use a large amount of memory if individually stored
    • Store intrinsic state that is invariant, context-independent and shareable
    • Provide an interface for passing in extrinsic state that is variant, context-dependent and can't be shared

    Class UML of flyweight pattern

     

    Structure

    • Client : a class which uses the flyweight pattern
    • Flyweight Factory : a class which creates and shares Flyweight objects
    • Flyweight interface : an interface which takes in extrinsic state and performs an operation
    • Flyweight class : a class which implements Flyweight and stores intrinsic state
    • The sequence of interactions at run-time
      1. The Client object calls getFlyweight(key) on the Flyweight Factory, which returns a Flyweight1 object
      2. After calling operation(extrinsicState) on the returned Flywegith1 object, the Client again calls getFlyweight(key) on the Flyweight Factory
      3. The Flyweight Factory returns the already-existing Flyweight1 object

     

    Example

    public class Flyweight // Defines Flyweight object that repeats itself
    {
        public string Name { get; set; }
        public string Location { get; set; }
        public string Website { get; set; }
        public byte[] Logo { get; set; }
    }
    
    public static class Pointer
    {
        public static readonly Flyweight Company 
            = new Flyweight { "Abc", "XYZ", "www.example.com" };
    }
    
    public class MyObject
    {
        public string Name { get; set; }
        public string Company => Pointer.Company.Name;
    }

     

    Proxy

    Proxy Pattern

    • A wrapper or agent object that is being called by the client to access the real serving object behind the scenes
    • For client, usage of a proxy object is similar to using the real object 

     

    Overview

    • Solves problem like:
      1. The access to an object should be controlled
      2. Additional functionality should be provided when accessing an object
    • Describes how to solve such problems:
      1. Can be used as substitute for another object (Subject)
      2. Implements additional functionality to control the access to the Subject
    • To act as substitute for a subject, a proxy must implement the Subject interface

     

    Possible Usage Scenarios

    • Remote proxy : The local object is a proxy for the remote object
    • Virtual proxy : In place of a complex or heavy object, a skeleton representation may be advantageous in some cases
    • Protection proxy : A protection proxy might be used to control access to a resource based on access rights

     

    Example

    interface ICar { void DriveCar(); }
    
    public class Car : ICar // Real Object
    {
        public void DriveCar() { Console.WriteLine("Car has been driven!"; }
    }
    
    public class ProxyCar : ICar // Proxy Object
    {
        private Driver driver;
        private ICar realCar;
    
        public ProxyCar(Driver driver)
        {
            this.driver = driver;
            this.realCar = new Car();
        }
    
        public void DriveCar()
        {
            if(driver.Age < 16) Console.WriteLine("Sorry, the driver is too young to drive");
            else this.realCar.DriveCar();
        }
    }
    
    public class Driver
    {
        public int Age { get; set; }
        public Driver(int age) { this.Age = age; }
    }
    
    private void btnProxy_Click(object sender, EventArgs e) // How to use proxy class?
    {
        ICar car = new ProxyCar(new Driver(15));
        car.DriveCar();
    		
        car = new ProxyCar(new Driver(25));
        car.DriveCar();
    }

    '프로그래밍 언어' 카테고리의 다른 글

    [C#] Public Field vs Auto-Implemented Property  (0) 2024.04.06
    OOP Design Pattern - Creational Pattern  (0) 2024.01.12
    [C++] 이동 의미론 Move Semantics  (0) 2023.11.20
    입실론 테스트  (0) 2023.11.20
    객체 지향 설계 SOLID  (0) 2023.11.16
Designed by Tistory.