DEV Community

Marant7
Marant7

Posted on

Software Design Principles: A Practical Approach with Python

Software design is fundamental in building robust, scalable, and maintainable applications. There are several principles that guide how we structure code to ensure it's efficient and sustainable over time. In this article, we will explore some of the most important software design principles and apply them in a real-world example using Python.

Software Design Principles

1. Single Responsibility Principle (SRP)

This principle states that a class or module should have one and only one reason to change, meaning it should have a single responsibility. If a class has multiple responsibilities, it becomes harder to maintain and understand.

Example in Python:
Imagine a class that handles both business logic and data persistence. According to SRP, we should split these responsibilities into separate classes.

# Class to handle business logic
class Order:
    def __init__(self, items, total_amount):
        self.items = items
        self.total_amount = total_amount

    def calculate_total(self):
        return sum(item['price'] for item in self.items)


# Class to handle data persistence
class OrderPersistence:
    def save_order(self, order):
        # Imagine saving the order to a database
        print(f"Order for {len(order.items)} items saved with total of ${order.total_amount}")
Enter fullscreen mode Exit fullscreen mode

Here, we’ve separated the responsibilities of business logic (total calculation) and data persistence into two distinct classes.

2. Open/Closed Principle (OCP)

This principle suggests that code should be open for extension but closed for modification. This means that we should be able to add new functionality without modifying the existing code.

Example in Python: Suppose we want to calculate the total of an order with different payment methods (credit card, PayPal, etc.). We use inheritance to extend functionality without modifying the base classes.

# Base class for total calculation
class PaymentMethod:
    def calculate_total(self, order):
        raise NotImplementedError


# Class for calculating with credit card
class CreditCardPayment(PaymentMethod):
    def calculate_total(self, order):
        return order.total_amount * 1.02  # Apply 2% commission


# Class for calculating with PayPal
class PayPalPayment(PaymentMethod):
    def calculate_total(self, order):
        return order.total_amount * 1.05  # Apply 5% commission
Enter fullscreen mode Exit fullscreen mode

3.Liskov Substitution Principle (LSP)

This principle states that derived classes must be substitutable for their base classes without altering the correct behavior of the program. That is, if class B is a subclass of A, we should be able to use an object of B where an object of A is expected without breaking functionality.

Example in Python: Continuing from the previous example, subclasses of PaymentMethod should be able to be used wherever a PaymentMethod is expected without issues.

def process_payment(payment_method, order):
    total = payment_method.calculate_total(order)
    print(f"Total after payment processing: ${total}")

order = Order([{"price": 20}, {"price": 35}], 55)
payment_method = CreditCardPayment()
process_payment(payment_method, order)  # Works correctly with any subclass of PaymentMethod
Enter fullscreen mode Exit fullscreen mode

This code works seamlessly with both CreditCardPayment and PayPalPayment, adhering to the LSP principle.

4. Interface Segregation Principle (ISP)

The interface segregation principle tells us that we should not force classes to implement interfaces they do not use. Instead of having one large interface, we should have several smaller ones, each covering a cohesive set of operations.

Example in Python: Instead of creating a generic class that has all payment methods, we can separate interfaces for each type of payment.

class CreditCardPaymentProcessor:
    def process_credit_card(self, card_number, amount):
        print(f"Processing credit card payment of ${amount} using card {card_number}")


class PayPalPaymentProcessor:
    def process_paypal(self, account_email, amount):
        print(f"Processing PayPal payment of ${amount} for account {account_email}")

Enter fullscreen mode Exit fullscreen mode

This way, the classes implement only the interfaces necessary for the type of payment they process.

5. Dependency Inversion Principle (DIP)

The dependency inversion principle states that high-level classes should not depend on low-level classes, but on abstractions. Moreover, abstractions should not depend on details, but details should depend on abstractions.

Example in Python: To apply this principle, we can introduce an abstraction for payment processing rather than directly depending on classes like CreditCardPayment or PayPalPayment.

# Abstract payment processor interface
class PaymentProcessor:
    def process(self, order):
        raise NotImplementedError


# Credit card implementation
class CreditCardProcessor(PaymentProcessor):
    def process(self, order):
        print(f"Processing credit card payment of ${order.total_amount}")


# Class that handles the order
class OrderService:
    def __init__(self, payment_processor: PaymentProcessor):
        self.payment_processor = payment_processor

    def process_order(self, order):
        print("Processing order...")
        self.payment_processor.process(order)
Enter fullscreen mode Exit fullscreen mode

In this example, OrderService doesn’t depend on a concrete payment implementation, but rather on the PaymentProcessor abstraction. This allows us to change the payment method without modifying the OrderService class.

Conclusion

Software design is a crucial discipline for developing clean, scalable, and maintainable applications. By following principles such as SRP, OCP, LSP, ISP, and DIP, we can write more modular, flexible, and robust code. Through practical examples in Python, we’ve illustrated how to apply these principles in real-world projects.

Adopting these principles not only improves the quality of our code but also facilitates teamwork and the evolution of applications over time. If you want to create high-quality software, incorporating these principles into your daily development practices is essential.

Link Repository : https://github.com/Marant7/desingprinciples

Top comments (5)

Collapse
 
andree_sebastianfloresm profile image
Andree Sebastian FLORES MELENDEZ

Excellent article! It clearly and practically explains key software design principles such as SRP, OCP, and LSP, applying them with examples in Python. A useful read for those looking to write cleaner and more maintainable code. Thanks for sharing this very educational approach.

Collapse
 
erick_yoelaymachoque_a1 profile image
ERICK YOEL AYMA CHOQUE

I found this article very insightful — it presents key software design principles like SRP, OCP, and LSP in a way that's practical and easy to follow. The real-world Python examples made each concept clearer, especially how responsibilities and extensibility should be handled in code.

Collapse
 
elvis_ronaldleyvasardon profile image
ELVIS RONALD LEYVA SARDON

This was a clear and practical explanation of the SOLID principles, which is essential for any developer aspiring to write clean and scalable code. A notable aspect is how each principle is accompanied by Python examples, making it easy to understand and directly apply. As a constructive observation, it would be helpful to complement the examples with unit tests that demonstrate how these principles also improve code testability. This would reinforce the idea that good design not only improves software structure but also its maintainability and validation.

Collapse
 
brian_danilochitequispe profile image
BRIAN DANILO CHITE QUISPE

This article provides an excellent introduction to software design principles and how to practically apply them using Python. Explaining principles such as the Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), and Dependency Inversion Principle (DIP) with clear and well-structured examples offers an easy way to understand how to build robust, scalable, and maintainable applications.

Collapse
 
jf2021070309 profile image
Jaime Elias Flores Quispe

I found this article to be a great introduction to software design principles. The clear explanations and practical Python examples helped me understand how to apply SRP, OCP, LSP, ISP, and DIP in real projects. It’s a valuable resource for any developer looking to improve their software design skills