Component Principles by Robert C. Martin with C# Examples

Component Principles by Robert C. Martin with C# Examples

Introduction

Robert C. Martin, also known as Uncle Bob, introduced several principles for designing software components that are cohesive, maintainable, and scalable. These principles help ensure high-quality modularity and reduce coupling between components.

1. The Reuse/Release Equivalence Principle (REP)

Definition: The granule of reuse is the granule of release.

This means that components intended for reuse should be released and versioned independently. If developers reuse a component, they should get updates via a managed release process (like a NuGet package).

C# Example:

Imagine you're building a shared logging library:


// Shared.Logging.csproj — versioned separately
public class Logger {
    public void Log(string message) {
        Console.WriteLine($"[LOG]: {message}");
    }
}
    

This component can now be reused across different projects, and any update should be released as a new version.

2. The Common Closure Principle (CCP)

Definition: Classes that change together should be packaged together.

Group classes into components so that a single change in requirements affects only one component, minimizing ripple effects.

C# Example:


// OrderProcessing.cs
public class OrderProcessor {
    public void ProcessOrder(Order order) {
        // Validate, calculate totals, etc.
    }
}

// InvoiceGenerator.cs
public class InvoiceGenerator {
    public Invoice Generate(Order order) {
        // Create invoice from order
    }
}
    

If the way orders are processed changes, it's likely that invoicing will change too. Keeping them together in a component respects CCP.

3. The Common Reuse Principle (CRP)

Definition: Classes that are used together should be packaged together.

This helps avoid unnecessary dependencies. If a class depends on one part of a component, it should benefit from the whole component.

C# Example:


// Bad Example — one utility class forces the whole component to be included
public class MathUtils {
    public static int Add(int a, int b) => a + b;
    public static int Multiply(int a, int b) => a * b;
    public static void ObsoleteLog() => Console.WriteLine("Deprecated!");
}
    

A better way is to split ObsoleteLog() into another component so consumers aren’t forced to depend on something they don’t use.

4. The Acyclic Dependencies Principle (ADP)

Definition: The dependency graph of components must have no cycles.

Cyclic dependencies make changes difficult and testing almost impossible. Components should depend only on abstractions or flow in one direction.

C# Example:

Suppose UI depends on BusinessLogic, and BusinessLogic depends on DataAccess. If DataAccess then depends back on UI, you have a cycle!


UI → BusinessLogic → DataAccess
          ↑_________________↓ (❌ cycle)
    

Break the cycle by depending on interfaces or abstractions.

5. The Stable Dependencies Principle (SDP)

Definition: Depend in the direction of stability.

A component should only depend on components that are more stable than itself. Stability is measured by how many other components depend on it.

C# Example:


// Stable core component
public interface IEmailSender {
    void SendEmail(string to, string subject, string body);
}

// Volatile implementation
public class SmtpEmailSender : IEmailSender {
    public void SendEmail(string to, string subject, string body) {
        // SMTP implementation
    }
}
    

The IEmailSender interface is stable and widely depended on. New implementations can come and go without breaking the stable abstraction.

6. The Stable Abstractions Principle (SAP)

Definition: A stable component should be abstract so that it can be extended without modification.

This balances SDP by saying: if your component is stable (many depend on it), it should also be abstract, allowing others to extend it.

C# Example:


// Core abstraction
public interface IPaymentGateway {
    void ProcessPayment(decimal amount);
}

// Extension
public class StripePayment : IPaymentGateway {
    public void ProcessPayment(decimal amount) {
        // Stripe-specific code
    }
}
    

The IPaymentGateway interface is stable and abstract. It lets developers build on it without changing the core.

Conclusion

Uncle Bob’s Component Principles guide us to write modular, testable, and maintainable software. Following REP, CCP, CRP, ADP, SDP, and SAP ensures your architecture is built to last.

Start small: Identify your components, create clear boundaries, and apply abstraction wisely.

Build components that are easy to change, reuse, and reason about.

Enjoyed the article? Share it with your team and follow for more clean architecture and C# tips.

Post a Comment

0 Comments