Design patterns are typical solutions to common software design problems. They are best practices adopted by experienced developers to solve recurring design issues. Python, being a dynamic and versatile language, supports multiple paradigms and allows easy implementation of various design patterns.
This document introduces key design patterns grouped into three main categories:
Each section provides explanations, use cases, and Python implementations.
Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could lead to design problems or added complexity. Creational patterns solve this problem by controlling the object creation process.
The Singleton pattern ensures a class has only one instance and provides a global point of access to it.
class Singleton:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2) # True
The Factory Method pattern defines an interface for creating objects, but allows subclasses to alter the type of objects that will be created.
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
def get_pet(pet="dog"):
pets = dict(dog=Dog(), cat=Cat())
return pets[pet]
pet = get_pet("cat")
print(pet.speak())
This pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
class Dog:
def speak(self):
return "Woof!"
class DogFactory:
def get_pet(self):
return Dog()
class PetStore:
def __init__(self, pet_factory):
self.pet = pet_factory.get_pet()
def show_pet(self):
return self.pet.speak()
store = PetStore(DogFactory())
print(store.show_pet())
The Builder pattern separates the construction of a complex object from its representation.
class Car:
def __init__(self):
self.make = None
self.model = None
class CarBuilder:
def __init__(self):
self.car = Car()
def set_make(self, make):
self.car.make = make
return self
def set_model(self, model):
self.car.model = model
return self
def build(self):
return self.car
builder = CarBuilder()
car = builder.set_make("Toyota").set_model("Camry").build()
print(car.make, car.model)
Structural patterns deal with object composition. They help ensure that if one part of a system changes, the entire structure does not need to do the same. These patterns help ensure a flexible and scalable architecture.
This pattern allows objects with incompatible interfaces to work together by wrapping their own interface around that of an already existing class.
class EuropeanSocketInterface:
def voltage(self):
return 230
class USASocket:
def get_voltage(self):
return 120
class Adapter(EuropeanSocketInterface):
def __init__(self, usa_socket):
self.usa_socket = usa_socket
def voltage(self):
return self.usa_socket.get_voltage()
adapter = Adapter(USASocket())
print(adapter.voltage())
This pattern attaches additional responsibilities to an object dynamically.
def make_bold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
@make_bold
def greet():
return "Hello"
print(greet())
The Proxy pattern provides a surrogate or placeholder for another object to control access to it.
class RealSubject:
def request(self):
return "RealSubject: Handling request."
class Proxy:
def __init__(self):
self._real_subject = RealSubject()
def request(self):
print("Proxy: Logging request...")
return self._real_subject.request()
proxy = Proxy()
print(proxy.request())
The Facade pattern provides a simplified interface to a complex subsystem.
class CPU:
def freeze(self):
print("Freezing CPU")
def jump(self, pos):
print(f"Jumping to {pos}")
class Memory:
def load(self, position, data):
print(f"Loading {data} at {position}")
class Computer:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
def start(self):
self.cpu.freeze()
self.memory.load("0x00", "OS")
self.cpu.jump("0x00")
computer = Computer()
computer.start()
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They help in defining how objects interact in a clean and organized way.
This pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified.
class Subject:
def __init__(self):
self._observers = []
def register(self, observer):
self._observers.append(observer)
def notify_all(self, message):
for obs in self._observers:
obs.notify(message)
class Observer:
def notify(self, message):
print(f"Received: {message}")
subject = Subject()
observer1 = Observer()
observer2 = Observer()
subject.register(observer1)
subject.register(observer2)
subject.notify_all("Hello Observers!")
This pattern enables selecting an algorithm’s behavior at runtime.
class TextFormatter:
def format(self, text):
raise NotImplementedError
class UpperCaseFormatter(TextFormatter):
def format(self, text):
return text.upper()
class LowerCaseFormatter(TextFormatter):
def format(self, text):
return text.lower()
class TextEditor:
def __init__(self, formatter):
self.formatter = formatter
def publish(self, text):
return self.formatter.format(text)
editor = TextEditor(UpperCaseFormatter())
print(editor.publish("Hello World"))
This pattern turns a request into a stand-alone object that contains all information about the request.
class Light:
def on(self):
print("Light on")
def off(self):
print("Light off")
class Command:
def execute(self):
pass
class OnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.on()
class OffCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.off()
light = Light()
on_cmd = OnCommand(light)
off_cmd = OffCommand(light)
on_cmd.execute()
off_cmd.execute()
This pattern lets multiple objects handle a request without knowing the handler.
class Handler:
def __init__(self, successor=None):
self.successor = successor
def handle(self, request):
handled = self.process(request)
if not handled and self.successor:
self.successor.handle(request)
def process(self, request):
raise NotImplementedError
class ConcreteHandler1(Handler):
def process(self, request):
if request < 10:
print(f"Handler1 handled: {request}")
return True
return False
class ConcreteHandler2(Handler):
def process(self, request):
if request < 20:
print(f"Handler2 handled: {request}")
return True
return False
h2 = ConcreteHandler2()
h1 = ConcreteHandler1(h2)
h1.handle(5)
h1.handle(15)
h1.handle(25)
Design patterns are fundamental building blocks in software architecture. In Python, their implementation is often cleaner due to dynamic typing and built-in features like decorators and first-class functions. Understanding design patterns enhances your code quality, improves readability, and fosters better maintainability.
This guide covered commonly used patterns across creational, structural, and behavioral categories. Patterns like Singleton, Factory, Observer, Decorator, and Strategy are especially common in Python development in frameworks such as Django, Flask, and more.
As you work on more complex systems, recognizing where and how to use these patterns will give you a solid edge in building robust software.
Python is commonly used for developing websites and software, task automation, data analysis, and data visualisation. Since it's relatively easy to learn, Python has been adopted by many non-programmers, such as accountants and scientists, for a variety of everyday tasks, like organising finances.
Learning Curve: Python is generally considered easier to learn for beginners due to its simplicity, while Java is more complex but provides a deeper understanding of how programming works.
The point is that Java is more complicated to learn than Python. It doesn't matter the order. You will have to do some things in Java that you don't in Python. The general programming skills you learn from using either language will transfer to another.
Read on for tips on how to maximize your learning. In general, it takes around two to six months to learn the fundamentals of Python. But you can learn enough to write your first short program in a matter of minutes. Developing mastery of Python's vast array of libraries can take months or years.
6 Top Tips for Learning Python
The following is a step-by-step guide for beginners interested in learning Python using Windows.
Best YouTube Channels to Learn Python
Write your first Python programStart by writing a simple Python program, such as a classic "Hello, World!" script. This process will help you understand the syntax and structure of Python code.
The average salary for Python Developer is ₹5,55,000 per year in the India. The average additional cash compensation for a Python Developer is within a range from ₹3,000 - ₹1,20,000.
Copyrights © 2024 letsupdateskills All rights reserved