Jump to content

Separation of Concerns

From Knowledge Base

Principle of Separation of Concerns

Introduction

Software engineering's main goal is to address the complexity inherent in designing, developing, and maintaining software systems. One foundational principle, the Principle of Separation of Concerns, stands as a guiding beacon in this pursuit.

Purpose

The Principle of Separation of Concerns recognizes the limitations of human cognition when confronted with complex tasks. By isolating distinct aspects of a system's behavior into separate concerns, this principle aims to enhance comprehension, maintainability, and scalability in software design.

Application

In practice, Separation of Concerns advocates for the division of software functionalities into discrete units, each addressing a specific aspect of behavior. Whether in the design of data structures or the implementation of algorithms, this principle underscores the importance of segregating basic functionality from considerations such as data integrity and exception handling.

Practices

  • Divide functionalities into distinct concerns to enhance clarity and maintainability.
  • Document each concern separately to facilitate comprehension and implementation.
  • Optimize individual concerns independently to manage trade-offs effectively.

Example in C#

Below is an example illustrating the Principle of Separation of Concerns in the context of a simple order processing system.

  • Order Class: Represents the data structure for an order, isolated from processing or storage logic.
  • OrderProcessor Class: Focuses on processing an order, divided into distinct methods for validation, saving, and notification.
  • Separation of Concerns: Each responsibility (validation, saving, notification) is encapsulated in its method, making the code more maintainable and testable.
public class Order {
    public int Id { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
}

public class OrderProcessor {
    // Handles the logic for processing an order
    public void ProcessOrder(Order order)
    {
        if (ValidateOrder(order)) {
            SaveOrder(order);
            NotifyCustomer(order);
        }
    }

    private bool ValidateOrder(Order order) {
        // Ensures the order meets basic validation criteria
        return order != null && order.Quantity > 0;
    }

    private void SaveOrder(Order order) {
        // Logic for saving the order to a database or storage
        Console.WriteLine($"Order {order.Id} has been saved.");
    }

    private void NotifyCustomer(Order order) {
        // Logic for sending a notification to the customer
        Console.WriteLine($"Notification sent for order {order.Id}: {order.ProductName}");
    }
}

Bad Example would be:

public class OrderProcessor
{
    // Handles the logic for processing an order
    public void ProcessOrder(Order order) {
        // Combined validation, saving, and notification in one method
        if (order == null || order.Quantity <= 0) {
            Console.WriteLine("Invalid order.");
            return;
        }
        
        // Simulate saving the order
        Console.WriteLine($"Order {order.Id} has been saved.");

        // Simulate notifying the customer
        Console.WriteLine($"Notification sent for order {order.Id}: {order.ProductName}");
    }
}
Meme DRY vs SoC