ingressu.com

Mastering Python Design Patterns: Enhance Your Coding Skills

Written on

Chapter 1: Introduction to Design Patterns

Design patterns serve as solutions to common issues in software development, assisting developers in crafting code that is maintainable, testable, and scalable. Python, known for its versatility and readability, is particularly suited for implementing these classic design patterns. A key concept in achieving flexibility and abstraction is polymorphism, which allows objects to take on various forms.

In this article, we will delve into four essential design patterns — Factory, Template Method, Command, and Observer — and illustrate how polymorphism is fundamental to each. Throughout, we will emphasize best practices in Pythonic idioms and style.

Section 1.1: The Factory Pattern

The Factory Pattern encapsulates the creation of families of related or dependent objects, promoting loose coupling and concealing complex construction logic. Polymorphism enables the selection of desired products at runtime.

Example: ShapeFactory

from abc import ABC, abstractmethod

class Shape(ABC):

@abstractmethod

def draw(self):

pass

class Rectangle(Shape):

def draw(self):

print("Drawing rectangle")

class Square(Shape):

def draw(self):

print("Drawing square")

class Circle(Shape):

def draw(self):

print("Drawing circle")

class ShapeFactory(object):

@staticmethod

def create_shape(shape_type):

if shape_type == "rectangle":

return Rectangle()

elif shape_type == "square":

return Square()

elif shape_type == "circle":

return Circle()

else:

raise ValueError("Invalid shape type")

if __name__ == "__main__":

for shape in ["rectangle", "square", "circle"]:

ShapeFactory.create_shape(shape).draw()

Output:

Drawing rectangle

Drawing square

Drawing circle

Section 1.2: The Template Method Pattern

The Template Method pattern defines the steps of an algorithm while deferring some of these steps to subclasses. It establishes invariant sequences, ensures consistency, and prevents code duplication. Variations of the algorithm arise through extension while maintaining the original structure.

Example: OrderProcessing

class PaymentProcessor(metaclass=ABCMeta):

@abstractmethod

def pay(self, amount):

pass

class BankTransferPaymentProcessor(PaymentProcessor):

def pay(self, amount):

print(f"Paying ${amount} via bank transfer")

class CreditCardPaymentProcessor(PaymentProcessor):

def pay(self, amount):

print(f"Charging ${amount} to credit card")

class Order(object):

def __init__(self, payment_processor: PaymentProcessor):

self.payment_processor = payment_processor

def place(self, total):

self.payment_processor.pay(total)

print("Order placed successfully")

if __name__ == "__main__":

order = Order(BankTransferPaymentProcessor())

order.place(100.0)

order = Order(CreditCardPaymentProcessor())

order.place(100.0)

Output:

Paying $100.00 via bank transfer

Order placed successfully

Charging $100.00 to credit card

Order placed successfully

Chapter 2: The Command Pattern

The Command Pattern decouples the requester from the receiver, transforming actions into independent objects. Commands encapsulate invocation while encoding metadata, enabling undo/redo capabilities and composition. Invokers maintain references to commands without needing to know about the receivers.

Example: LightController

class SwitchOnCommand(object):

def __init__(self, device):

self.device = device

def execute(self):

self.device.switch_on()

class SwitchOffCommand(object):

def __init__(self, device):

self.device = device

def execute(self):

self.device.switch_off()

class Device(object):

def switch_on(self):

print("Device turned on")

def switch_off(self):

print("Device turned off")

class RemoteControl(object):

def __init__(self):

self.commands = []

def set_command(self, command):

self.commands.append(command)

def press_button(self):

for cmd in self.commands:

cmd.execute()

if __name__ == "__main__":

lamp = Device()

controller = RemoteControl()

controller.set_command(SwitchOnCommand(lamp))

controller.press_button()

controller.set_command(SwitchOffCommand(lamp))

controller.press_button()

Output:

Device turned on

Device turned off

Chapter 3: The Observer Pattern

The Observer Pattern creates a connection between subjects and their dependents, facilitating the propagation of state updates. Subjects notify dependents of state changes, prompting them to update themselves accordingly. This loose coupling allows for the addition or removal of dependents without impacting the subjects.

Example: StockTicker

class Observer(object):

def update(self, stock):

pass

class Observable(object):

def __init__(self):

self.observers = []

def register(self, observer):

self.observers.append(observer)

def remove(self, observer):

self.observers.remove(observer)

def notify(self, price):

for obs in self.observers:

obs.update(price)

class StockObserver(Observer):

def update(self, stock):

print(f"Stock updated to {stock.price}")

class Stock(Observable):

def __init__(self, symbol):

super().__init__()

self.symbol = symbol

self.price = 0

def attach(self, observer):

self.register(observer)

def detach(self, observer):

self.remove(observer)

def updatePrice(self, price):

old_price = self.price

self.price = price

self.notify(price)

if __name__ == "__main__":

apple = Stock("AAPL")

google = Stock("GOOG")

investor = StockObserver()

apple.attach(investor)

google.attach(investor)

apple.updatePrice(150)

google.updatePrice(1000)

Output:

Stock updated to 150

Stock updated to 1000

Conclusion

Utilizing polymorphism alongside timeless design patterns empowers developers to combat code complexity effectively. Coupled with Python's inherent readability and simplicity, creating robust, maintainable solutions becomes a fundamental skill. Embrace these principles widely and share the knowledge of quality coding practices with peers and future developers.

The first video, "Master Python Design Patterns: Build Flexible & Robust Code," provides a comprehensive overview of design patterns in Python, demonstrating their practical applications.

The second video, "Why Use Design Patterns When Python Has Functions?" explores the rationale behind using design patterns in Python, despite its functional capabilities.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

The Tongass National Forest: An Ecological Treasure of Alaska

Explore the ecological significance of Alaska's Tongass National Forest, its role in carbon storage, and the threats it faces from logging.

Embracing the Easy Path: A Guide to Effective Goal Achievement

Discover how focusing on simple, essential tasks can lead to achieving your goals without feeling overwhelmed.

Mastering Goal Setting: 5 Essential Steps for Success

Discover five crucial steps for effective goal setting to enhance your personal development and achieve your aspirations.

Unlocking the Secrets of Viral Storytelling for Writers

Discover a simple formula for crafting engaging stories that captivate readers and boost your writing skills.

Exploring the Divide: Believers and Non-Believers in Tech

A deep dive into the contrasting perspectives of tech believers and skeptics, highlighting startup culture and the evolving landscape of innovation.

Unlocking Your Entrepreneurial Dreams: The Three Key Elements

Discover the three vital components essential for achieving success in your business venture.

# Embracing Patience in the Art of Writing and Growth

Discover the importance of patience and planning in writing, drawing parallels to physical training and personal growth.

The Impact of XRP's Legal Victory on Future Crypto Investments

XRP's recent legal success could significantly influence the crypto market, potentially leading to impressive gains for Bitcoin and other cryptocurrencies.