Structural design patterns are concerned with how classes and objects are composed to form larger structures. These patterns help ensure that if one part of a system changes, the entire system doesn't need to change as well. They focus on simplifying the design by identifying a simple way to realize relationships between entities.
Key Concepts
- Composition Over Inheritance: Structural patterns often emphasize composition over inheritance. This means that instead of creating a complex class hierarchy, you can achieve the same functionality by composing objects.
- Object Relationships: These patterns help manage relationships between objects to ensure that they can work together effectively.
- Flexibility and Reusability: By using structural patterns, you can create more flexible and reusable code.
Common Structural Patterns
Here are some of the most commonly used structural design patterns:
Pattern | Description |
---|---|
Adapter | Allows incompatible interfaces to work together. |
Bridge | Separates an object’s interface from its implementation. |
Composite | Composes objects into tree structures to represent part-whole hierarchies. |
Decorator | Adds additional responsibilities to an object dynamically. |
Facade | Provides a simplified interface to a complex subsystem. |
Flyweight | Reduces the cost of creating and manipulating a large number of similar objects. |
Proxy | Provides a surrogate or placeholder for another object. |
Example: Adapter Pattern
The Adapter pattern allows objects with incompatible interfaces to collaborate. It acts as a bridge between two incompatible interfaces.
Example Scenario
Imagine you have a MediaPlayer
interface that plays audio files and a AdvancedMediaPlayer
interface that plays both audio and video files. You want to use AdvancedMediaPlayer
in place of MediaPlayer
.
Code Example
# MediaPlayer interface class MediaPlayer: def play(self, audio_type, file_name): pass # AdvancedMediaPlayer interface class AdvancedMediaPlayer: def play_vlc(self, file_name): pass def play_mp4(self, file_name): pass # Concrete implementation of AdvancedMediaPlayer class VlcPlayer(AdvancedMediaPlayer): def play_vlc(self, file_name): print(f"Playing vlc file. Name: {file_name}") def play_mp4(self, file_name): pass class Mp4Player(AdvancedMediaPlayer): def play_vlc(self, file_name): pass def play_mp4(self, file_name): print(f"Playing mp4 file. Name: {file_name}") # Adapter class implementing MediaPlayer class MediaAdapter(MediaPlayer): def __init__(self, audio_type): if audio_type == "vlc": self.advanced_music_player = VlcPlayer() elif audio_type == "mp4": self.advanced_music_player = Mp4Player() def play(self, audio_type, file_name): if audio_type == "vlc": self.advanced_music_player.play_vlc(file_name) elif audio_type == "mp4": self.advanced_music_player.play_mp4(file_name) # Concrete implementation of MediaPlayer class AudioPlayer(MediaPlayer): def __init__(self): self.media_adapter = None def play(self, audio_type, file_name): if audio_type == "mp3": print(f"Playing mp3 file. Name: {file_name}") elif audio_type in ["vlc", "mp4"]: self.media_adapter = MediaAdapter(audio_type) self.media_adapter.play(audio_type, file_name) else: print(f"Invalid media. {audio_type} format not supported") # Client code if __name__ == "__main__": audio_player = AudioPlayer() audio_player.play("mp3", "beyond_the_horizon.mp3") audio_player.play("mp4", "alone.mp4") audio_player.play("vlc", "far_far_away.vlc") audio_player.play("avi", "mind_me.avi")
Explanation
- Interfaces:
MediaPlayer
andAdvancedMediaPlayer
define the interfaces for playing media files. - Concrete Implementations:
VlcPlayer
andMp4Player
implement theAdvancedMediaPlayer
interface. - Adapter:
MediaAdapter
implements theMediaPlayer
interface and uses an instance ofAdvancedMediaPlayer
to play the appropriate file format. - Client:
AudioPlayer
usesMediaAdapter
to play different types of media files.
Practical Exercise
Task
Create a Shape
interface with a draw
method. Implement two concrete classes, Rectangle
and Circle
, that implement the Shape
interface. Then, create a ShapeMaker
class that uses the Facade
pattern to draw these shapes.
Solution
# Shape interface class Shape: def draw(self): pass # Concrete implementations class Rectangle(Shape): def draw(self): print("Drawing a Rectangle") class Circle(Shape): def draw(self): print("Drawing a Circle") # Facade class class ShapeMaker: def __init__(self): self.rectangle = Rectangle() self.circle = Circle() def draw_rectangle(self): self.rectangle.draw() def draw_circle(self): self.circle.draw() # Client code if __name__ == "__main__": shape_maker = ShapeMaker() shape_maker.draw_rectangle() shape_maker.draw_circle()
Explanation
- Shape Interface: Defines the
draw
method. - Concrete Implementations:
Rectangle
andCircle
implement theShape
interface. - Facade:
ShapeMaker
provides a simplified interface to draw shapes. - Client: Uses
ShapeMaker
to draw shapes without knowing the underlying implementation.
Summary
In this section, we introduced structural design patterns, which focus on how classes and objects are composed to form larger structures. We discussed the importance of composition over inheritance and provided an example of the Adapter pattern. We also included a practical exercise to reinforce the concepts learned. In the next sections, we will delve deeper into individual structural patterns and their applications.
Software Design Patterns Course
Module 1: Introduction to Design Patterns
- What are Design Patterns?
- History and Origin of Design Patterns
- Classification of Design Patterns
- Advantages and Disadvantages of Using Design Patterns
Module 2: Creational Patterns
Module 3: Structural Patterns
Module 4: Behavioral Patterns
- Introduction to Behavioral Patterns
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Module 5: Application of Design Patterns
- How to Select the Right Pattern
- Practical Examples of Pattern Usage
- Design Patterns in Real Projects
- Refactoring Using Design Patterns
Module 6: Advanced Design Patterns
- Design Patterns in Modern Architectures
- Design Patterns in Microservices
- Design Patterns in Distributed Systems
- Design Patterns in Agile Development