Design Pattern Overview
Design patterns are like proven solutions to common problems that software developers face. They give guidelines on structuring code and solving design issues flexibly and efficiently. Design patterns make code easier to read, reuse, and scale, making software development more efficient and reliable.
They are helpful to developers who focus on essential elements of the design instead of getting caught up in small details. The design patterns are a set of best practices developers can use to improve their software.
They promote good coding habits and make it easier to build software that can be easily modified and expanded upon in the future.
Types of Design Patterns
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview1.png)
1. Creational Patterns: These patterns focus on how objects are created. They provide flexible and controlled ways to create objects. Examples consist of Factory, Singleton, and Prototype patterns.
Singleton Pattern Example
The Singleton pattern ensures a single instance of a class is globally accessible.
Source Code:
class Singleton:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
# Usage
instance1 = Singleton()
instance2 = Singleton()
print(instance1 is instance2)
Output :
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview2.png)
Factory Pattern Example :
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class AnimalFactory:
@staticmethod
def create_animal(animal_type):
if animal_type == 'dog':
return Dog()
elif animal_type == 'cat':
return Cat()
else:
raise ValueError(f"Invalid animal type: {animal_type}")
# Usage
factory = AnimalFactory()
dog = factory.create_animal('dog')
print(dog.speak())
cat = factory.create_animal('cat')
print(cat.speak())
Output :
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview3.png)
Prototype Pattern :
The Prototype pattern allows for creating new objects by existing objects, promoting reusability and reducing the need for complex initialization processes or subclassing.
Source Code :
import copy
class Prototype:
def clone(self):
pass
class Rectangle(Prototype):
def __init__(self, width, height):
self.width = width
self.height = height
def clone(self):
return copy.deepcopy(self)
def __str__(self):
return f"Rectangle: width={self.width}, height={self.height}"
# Usage
rectangle = Rectangle(10, 5)
print(rectangle)
clone_rectangle = rectangle.clone()
print(clone_rectangle)
clone_rectangle.width = 7
print(clone_rectangle)
Output :
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview4.png)
2. Structural Patterns: These patterns organize and compose classes and objects. They enhance the functionality and relationships between objects. Examples consist of Adapters, decorators, and Composite patterns
Adapter Pattern: The Adapter pattern converts the interface of one class into another interface that clients expect, enabling objects with incompatible interfaces to work together.
Source Code :
# Adaptee class
class LegacyCalculator:
def add_numbers(self, x, y):
return x + y
# Target interface
class Calculator:
def add(self, x, y):
pass
# Adapter class
class CalculatorAdapter(Calculator):
def __init__(self, legacy_calculator):
self.legacy_calculator = legacy_calculator
def add(self, x, y):
return self.legacy_calculator.add_numbers(x, y)
# Usage
legacy_calculator = LegacyCalculator()
adapter = CalculatorAdapter(legacy_calculator)
result = adapter.add(5, 3)
print(result)
Output:
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview5.png)
Composite Pattern :
The Composite pattern enables you to create hierarchical structures of objects where individual and group objects can be treated uniformly.
Source Code :
# Component interface
class Component:
def operation(self):
pass
# Leaf class
class Leaf(Component):
def operation(self):
print("Leaf operation")
# Composite class
class Composite(Component):
def __init__(self):
self.children = []
def add(self, component):
self.children.append(component)
def remove(self, component):
self.children.remove(component)
def operation(self):
print("Composite operation")
for child in self.children:
child.operation()
# Usage
leaf1 = Leaf()
leaf2 = Leaf()
composite = Composite()
composite.add(leaf1)
composite.add(leaf2)
composite.operation()
Output:
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview6.png)
3. Behavioral Patterns: These patterns focus on how objects communicate and interact with each other. They define patterns of communication and collaboration to ensure practical object cooperation. Examples includes Observer, Strategy, and Command patterns.
Command Pattern:
The Command sample encapsulates requests as objects, decoupling senders from receivers and allowing for flexible request handling and undoable operations.
Source Code:
# Receiver class
class Light:
def turn_on(self):
print("Light is on.")
def turn_off(self):
print("Light is off.")
# Command interface
class Command:
def execute(self):
pass
# Concrete command classes
class TurnOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
class TurnOffCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_off()
# Usage
light = Light()
turn_on_command = TurnOnCommand(light)
turn_off_command = TurnOffCommand(light)
# Execute the commands
turn_on_command.execute()
turn_off_command.execute()
Output :
![Design Pattern – Overview](https://static.tutorialandexample.com/python/design-pattern-overview7.png)
Advantages of Design Patterns
1. Problem-Solving: Design patterns offer proven solutions to recurring problems. By following established patterns, we can efficiently address common challenges, saving time and effort in finding solutions from scratch.
2. Best Practices: Design patterns encapsulate best practices and guidelines from experienced developers. The industry knowledge and collective wisdom ensure that we adopt attempted-and-examined approaches that lead to robust, maintainable, and efficient solutions.
3. Code Readability and Maintainability: Design patterns promote code organization and structure. They improve code readability, making it easier for developers to understand and maintain.
4. Collaboration and Communication: Design patterns provide a common language and vocabulary for developers. They serve as a communication tool, enabling teams to discuss and share design decisions effectively. This shared understanding fosters collaboration, teamwork, and a smoother development process.
5. Flexibility and Adaptability: Design patterns enhance the adaptability and flexibility of software systems. By designing applications with patterns, we can more easily accommodate changes, new features, and evolving requirements. Patterns facilitate modularity, making modifying or replacing specific components easier without affecting the entire system.
6. Maintainable Code: Design patterns improve code maintainability by separating concerns, encapsulating functionality, and promoting modularity. They make it easier to manage changes and keep the codebase organized.
7. Scalability and Performance: Certain design patterns improve scalability and performance. For example, the sample reduces memory usage by sharing common data, while the pattern controls resource access, enhancing performance.