1ae1de6d0d2a87e6834da409bfd891b2 Few Structural Design patterns like Adapter ,Composite ,Bridge and Decorator design pattern with python implementation

Few Structural Design patterns like Adapter ,Composite ,Bridge and Decorator design pattern with python implementation

 








📘 Definition of Structural Design Pattern

Structural Design Patterns are design patterns that deal with object composition—that is, how classes and objects are composed to form larger structures. They help ensure that if one part of a system changes, the entire system doesn't need to change.

🔧 Definition:
Structural design patterns define the composition of classes or objects to form larger structures while keeping the system flexible and efficient.


🧱 Common Structural Design Patterns

Here are the 7 main structural design patterns:

  1. Adapter – Allows incompatible interfaces to work together.

  2. Bridge – Separates abstraction from implementation.

  3. Composite – Treats individual objects and compositions uniformly.

  4. Decorator – Adds behavior to objects dynamically.

  5. Facade – Provides a simplified interface to a complex system.

  6. Flyweight – Shares common parts of state between multiple objects.

  7. Proxy – Acts as a placeholder or surrogate for another object.


How to Check or Identify a Structural Design Pattern

To recognize or use a structural design pattern in a codebase, ask the following:

QuestionIf Yes, Then...
Does this part of the system wrap an object to modify or simplify its behavior?Decorator or Proxy
Are different interfaces being made compatible?Adapter
Is an abstraction separated from its implementation?Bridge
Are objects being organized into a tree structure?Composite
Is a simplified interface provided over a complex system?Facade
Is memory being saved by sharing data between objects?Flyweight
Is a stand-in object controlling access to a real one?Proxy

🔍 How to "Check Out" a Structural Pattern in Code

If you're exploring existing code and want to identify structural design patterns:

  1. Look for class hierarchies that wrap other classes or delegate behavior.

  2. Check for methods that reference or instantiate other classes in a modular way.

  3. Identify interfaces or base classes with multiple implementations.

  4. Search for pattern names in documentation or comments (Adapter, Decorator, etc.).

  5. Use UML diagrams or visual tools to see class relationships clearly.









The Adapter Design Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces.


🔧 Definition

The Adapter pattern converts the interface of a class into another interface that a client expects. It allows classes to work together that couldn’t otherwise because of incompatible interfaces.


🧱 Components

  1. Target Interface: The interface the client expects.

  2. Adaptee: The existing class with a different interface.

  3. Adapter: The class that implements the target interface and translates the requests to the adaptee.


🔄 Real-world Analogy

Imagine a power adapter that allows a US plug (adaptee) to fit into a European socket (target). The adapter converts the plug shape and voltage as needed.


When to Use

  • You want to reuse an existing class, but its interface does not match the one you need.

  • You want to create a reusable class that cooperates with classes that don’t necessarily have compatible interfaces.


👨‍💻 Example in Python


# Target Interface class MediaPlayer: def play(self, audio_type: str, file_name: str): pass # Adaptee (Incompatible interface) class AdvancedMediaPlayer: def play_mp4(self, file_name: str): print(f"Playing mp4 file: {file_name}") def play_vlc(self, file_name: str): print(f"Playing vlc file: {file_name}") # Adapter class MediaAdapter(MediaPlayer): def __init__(self, audio_type: str): self.advanced_player = AdvancedMediaPlayer() self.audio_type = audio_type def play(self, audio_type: str, file_name: str): if audio_type == "mp4": self.advanced_player.play_mp4(file_name) elif audio_type == "vlc": self.advanced_player.play_vlc(file_name) else: print(f"Unsupported format: {audio_type}") # Client class AudioPlayer(MediaPlayer): def play(self, audio_type: str, file_name: str): if audio_type == "mp3": print(f"Playing mp3 file: {file_name}") elif audio_type in ["mp4", "vlc"]: adapter = MediaAdapter(audio_type) adapter.play(audio_type, file_name) else: print(f"Invalid media format: {audio_type}") # Example usage player = AudioPlayer() player.play("mp3", "song.mp3") player.play("mp4", "video.mp4") player.play("vlc", "movie.vlc") player.play("avi", "clip.avi")




Output


Playing mp3 file: song.mp3 Playing mp4 file: video.mp4 Playing vlc file: movie.vlc Invalid media format: avi




Composite Design Pattern




The Composite Design Pattern is a structural pattern that lets you compose objects into tree structures to represent part-whole hierarchies.

It allows clients to treat individual objects and compositions uniformly.


🔧 Definition

The Composite pattern lets clients treat individual objects and compositions of objects uniformly.


📦 Key Concepts

  • Useful when you have a tree-like structure (e.g., file system, UI components).

  • Common in scenarios where you need to treat individual objects and groups of objects in the same way.


🧱 Components

  1. Component: Base interface for all objects in the composition.

  2. Leaf: Represents individual objects (end nodes).

  3. Composite: Represents a group of Component objects (can contain Leaf or other Composite).


🌳 Real-world Analogy

Think of a file system:

  • A file is a Leaf.

  • A folder is a Composite that can contain files and other folders.


🐍 Python Implementation


from abc import ABC, abstractmethod # Component class FileSystemComponent(ABC): @abstractmethod def display(self, indent=0): pass # Leaf class File(FileSystemComponent): def __init__(self, name): self.name = name def display(self, indent=0): print(" " * indent + f"- File: {self.name}") # Composite class Directory(FileSystemComponent): def __init__(self, name): self.name = name self.children = [] def add(self, component: FileSystemComponent): self.children.append(component) def remove(self, component: FileSystemComponent): self.children.remove(component) def display(self, indent=0): print(" " * indent + f"+ Directory: {self.name}") for child in self.children: child.display(indent + 2) # Example usage if __name__ == "__main__": # Creating files file1 = File("file1.txt") file2 = File("file2.txt") file3 = File("file3.txt") # Creating directories root = Directory("root") sub_dir1 = Directory("sub_dir1") sub_dir2 = Directory("sub_dir2") # Building the tree sub_dir1.add(file1) sub_dir1.add(file2) sub_dir2.add(file3) root.add(sub_dir1) root.add(sub_dir2) # Display the file structure root.display()

Output


+ Directory: root + Directory: sub_dir1 - File: file1.txt - File: file2.txt + Directory: sub_dir2 - File: file3.txt

🧠 When to Use

  • You want to represent part-whole hierarchies.

  • You want clients to ignore the difference between compositions and individual objects.

Here’s a clear explanation and Python implementation of both the Bridge and Decorator design patterns — these are often compared because both deal with abstraction and flexibility but in different ways.


🧱 1. Bridge Design Pattern

🔧 Definition

Bridge pattern decouples abstraction from its implementation, so the two can vary independently.


📦 Key Idea

  • Split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently.


🧩 Use Case Example: Different devices (TV, Radio) controlled by different remote controls.


🐍 Python Example (Bridge)

# Implementation hierarchy
class Device:
    def turn_on(self):
        pass

    def turn_off(self):
        pass

class TV(Device):
    def turn_on(self):
        print("Turning on TV")

    def turn_off(self):
        print("Turning off TV")

class Radio(Device):
    def turn_on(self):
        print("Turning on Radio")

    def turn_off(self):
        print("Turning off Radio")


# Abstraction hierarchy
class RemoteControl:
    def __init__(self, device: Device):
        self.device = device

    def turn_on_device(self):
        self.device.turn_on()

    def turn_off_device(self):
        self.device.turn_off()


# Example usage
tv_remote = RemoteControl(TV())
tv_remote.turn_on_device()
tv_remote.turn_off_device()

radio_remote = RemoteControl(Radio())
radio_remote.turn_on_device()
radio_remote.turn_off_device()

Output

Turning on TV
Turning off TV
Turning on Radio
Turning off Radio

🎨 2. Decorator Design Pattern

🔧 Definition

Decorator pattern adds new functionality to an object dynamically without altering its structure.


📦 Key Idea

  • Attach additional responsibilities to objects at runtime.

  • A flexible alternative to subclassing for extending functionality.


🧩 Use Case Example: Text with different formatting (bold, italic, underline).


🐍 Python Example (Decorator)

# Component
class Text:
    def render(self):
        return "Hello"

# Base Decorator
class TextDecorator(Text):
    def __init__(self, wrapped: Text):
        self.wrapped = wrapped

    def render(self):
        return self.wrapped.render()

# Concrete Decorators
class BoldDecorator(TextDecorator):
    def render(self):
        return f"<b>{super().render()}</b>"

class ItalicDecorator(TextDecorator):
    def render(self):
        return f"<i>{super().render()}</i>"

class UnderlineDecorator(TextDecorator):
    def render(self):
        return f"<u>{super().render()}</u>"

# Example usage
simple_text = Text()
decorated_text = BoldDecorator(ItalicDecorator(UnderlineDecorator(simple_text)))
print(decorated_text.render())

Output

<b><i><u>Hello</u></i></b>

🔍 Summary Table

Pattern Purpose Key Feature Example
Bridge Decouple abstraction and implementation Two separate class hierarchies Remote and Device
Decorator Add responsibilities dynamically Wrap object with new behavior Text formatting



Post a Comment

0 Comments