Log In

Don't have an account? Sign up now

Lost Password?

Sign Up

Prev Next

Module 7: Object-Oriented Design & SOLID Principles

1. Recap of OOP in Real Projects

In academia, OOP is often taught using “Animal” or “Car” classes. In real-world .NET projects (like ASP.NET Core APIs), OOP is about managing complexity.

  • Encapsulation: Not just private variables, but protecting the “internal state” of a business process (e.g., a BankOrder class that only allows status changes through specific methods, never direct property setters).
  • Abstraction: Using Interfaces to define “What” a system does without caring “How” (e.g., IEmailService).
  • Inheritance vs. Composition: Real projects favor composition (wrapping functionality) over deep inheritance trees, which can become brittle.

2. The SOLID Principles

SOLID is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible, and maintainable.

S: Single Responsibility Principle (SRP)

“A class should have one, and only one, reason to change.”

  • Bad: A UserService class that validates user input, saves the user to the database, and sends a welcome email.
  • Good: Break it into UserValidator, UserRepository, and EmailService.
  • Why: If the email provider changes, you shouldn’t have to touch the database logic.

O: Open-Closed Principle (OCP)

“Software entities should be open for extension, but closed for modification.”

  • Example: If you have a DiscountCalculator that uses if/else for “Student” or “Senior,” you have to change the code every time you add a new discount type.
  • Solution: Use an interface IDiscountStrategy. You add a new class MemberDiscount without ever touching the existing calculator code.

L: Liskov Substitution Principle (LSP)

“Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.”

  • The Classic Violation: A Square inheriting from Rectangle. If a method expects a Rectangle and sets Height to 10 and Width to 5, a Square will break because its sides must be equal.
  • Rule of Thumb: If a subclass throws a NotImplementedException for a base method, you are likely violating LSP.

I: Interface Segregation Principle (ISP)

“Clients should not be forced to depend upon interfaces that they do not use.”

  • Bad: An IMachine interface with Print(), Fax(), and Scan(). An OldPrinter class is forced to implement Fax() even if it can’t do it.
  • Good: Split into IPrinter, IFax, and IScanner.

D: Dependency Inversion Principle (DIP)

“Depend upon abstractions, not concretions.”

  • The Concept: High-level modules (the Business Logic) should not depend on low-level modules (the Database). Both should depend on interfaces.

3. Coupling: Loose vs. Tight

This is the “health check” of your architecture.

FeatureTight Coupling (Bad)Loose Coupling (Good)
DependencyClass A creates an instance of Class B (new ClassB()).Class A is handed an interface (IB).
FlexibilityChanging B requires changing A.You can swap B for C without touching A.
TestingImpossible to unit test A without B.You can “Mock” the interface for testing.

4. Code Smells & Refactoring

Code Smells are surface indications that usually correspond to a deeper problem in the system.

  • God Object: A class that knows too much or does too much.
  • Shotgun Surgery: One change requires small edits to dozens of different classes.
  • Long Parameter List: A method requiring 10+ arguments (suggests you need a “Data Transfer Object” or DTO).
  • Refactoring: The process of restructuring existing computer code—changing the factoring—without changing its external behavior.

5. Dependency Injection (DI) in .NET

DI is the primary way we implement Dependency Inversion in .NET. Instead of a class “buying” its tools, they are “delivered” to it via the constructor.

Example in .NET Core:

C#

// The Interface
public interface IMessageService { void Send(string msg); }

// The Implementation
public class EmailService : IMessageService { 
    public void Send(string msg) => Console.WriteLine($"Email: {msg}"); 
}

// The Consumer (Loosely Coupled)
public class UserRegistration {
    private readonly IMessageService _svc;
    public UserRegistration(IMessageService svc) => _svc = svc; // DI happens here
}

In .NET, you register these in Program.cs using builder.Services.AddScoped<IMessageService, EmailService>();.


6. Real-World .NET Design Example

Imagine a Payment Processing System.

  1. Interface: IPaymentProcessor with a Process() method.
  2. Implementations: StripeProvider, PayPalProvider, CryptoProvider.
  3. The Controller: Your API controller asks for IPaymentProcessor.
  4. The Benefit: If the business decides to switch from PayPal to Stripe, you change one line in your configuration, and the entire app updates instantly without touching the business logic.

Leave a Comment

    🚀 Join Common Jobs Pro — Referrals & Profile Visibility Join Now ×
    🔥