SOLID Principles 🧱

The Secret to Clean, Professional Code

Beginner Friendly 30 min read Lesson 11 of 11

Hey There! Ready to Learn SOLID Principles? 🏗️

SOLID is a set of 5 design principles that help you write clean, maintainable, and professional code. It's like the golden rules of building great software!

Why SOLID Matters:
  • 🏗️ Easy to maintain - Change code without breaking things
  • 🧩 Easy to extend - Add new features without touching old code
  • 👥 Team-friendly - Multiple developers can work together
  • 🐛 Fewer bugs - Clean code = fewer problems
🎯 After this lesson, you'll be able to:
  • ✅ Understand all 5 SOLID principles
  • ✅ Spot bad code that violates SOLID
  • ✅ Write clean, professional code
  • ✅ Build applications that are easy to maintain
  • ✅ Impress your team with your coding skills!

Part 1: What are SOLID Principles?

In Simple Words: SOLID is an acronym for 5 principles that make your code better!
📝 The SOLID Acronym
Letter Principle Simple Meaning
S Single Responsibility One job per class
O Open/Closed Add new stuff, don't change old stuff
L Liskov Substitution Children should act like their parents
I Interface Segregation Don't force things they don't need
D Dependency Inversion Depend on ideas, not specific things
😂 Fun Joke: Why did the SOLID principles break up? Because they couldn't agree on how to build a house! (Okay, I'll stop 😅)

Part 2: S - Single Responsibility Principle

What It Means: A class should have one job - and do it well!
❌ Bad Example

One class doing EVERYTHING:

public class Employee
{
    // 1. Stores data
    public string Name { get; set; }
    
    // 2. Calculates bonus
    public decimal CalcBonus() => Salary * 0.1m;
    
    // 3. Saves to database
    public void Save() { }
    
    // 4. Sends emails
    public void SendEmail() { }
}

⚠️ Four responsibilities in one class!

✅ Good Example

Each class has ONE job:

// Job 1: Store data
public class Employee
{
    public string Name { get; set; }
}

// Job 2: Calculate bonus
public class BonusCalculator
{
    public decimal Calc(Employee e) => e.Salary * 0.1m;
}

// Job 3: Save data
public class EmployeeRepo
{
    public void Save(Employee e) { }
}

✅ Each class has ONE responsibility!

Remember: If you need to change a class for different reasons, it has too many responsibilities!
😂 Joke: Why did the class get fired? Because it couldn't handle all its responsibilities!

Part 3: O - Open/Closed Principle

What It Means: Open for extension, closed for modification. Add new features without changing existing code!
❌ Bad Example

Must change existing code to add new shapes:

public class AreaCalculator
{
    public double Area(object shape)
    {
        if (shape is Circle c)
            return Math.PI * c.Radius * c.Radius;
        else if (shape is Rectangle r)
            return r.Width * r.Height;
        // Adding new shapes requires changing THIS method!
        return 0;
    }
}
✅ Good Example

Add new shapes without changing existing code:

// Base class - open for extension
public abstract class Shape
{
    public abstract double Area();
}

// Each shape in its own class
public class Circle : Shape
{
    public override double Area() => Math.PI * Radius * Radius;
}

✅ Add new shapes by creating new classes!

Part 4: L - Liskov Substitution Principle

What It Means: A child class should be able to replace its parent class without breaking things!
❌ Bad Example

Square breaking Rectangle behavior:

public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }
}

public class Square : Rectangle
{
    public override int Width
    {
        set { base.Width = value; base.Height = value; }
    }
}

⚠️ Square changes both width AND height!

✅ Good Example

Separate classes for different shapes:

public abstract class Shape
{
    public abstract int Area();
}

public class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }
    public override int Area() => Width * Height;
}

public class Square : Shape
{
    public int Side { get; set; }
    public override int Area() => Side * Side;
}

✅ Each shape is separate and works correctly!

Remember: If a child class changes behavior in unexpected ways, it violates LSP!

Part 5: I - Interface Segregation Principle

What It Means: Don't force a class to implement methods it doesn't need!
❌ Bad Example

One big interface with everything:

public interface IWorker
{
    void Work();
    void Eat();
    void Sleep();
}

// Robot forced to Eat and Sleep!
public class Robot : IWorker
{
    public void Work() { }
    public void Eat() { } // Robots don't eat!
    public void Sleep() { } // Robots don't sleep!
}
✅ Good Example

Small, focused interfaces:

public interface IWorkable
{
    void Work();
}

public interface IEatable
{
    void Eat();
}

public interface ISleepable
{
    void Sleep();
}

// Robot only implements what it needs
public class Robot : IWorkable
{
    public void Work() { }
}

✅ Each class implements only what it needs!

Part 6: D - Dependency Inversion Principle

What It Means: Depend on abstractions (interfaces), not concrete things (specific classes)!
❌ Bad Example

High-level class depends on low-level class:

public class EmailSender
{
    public void Send(string msg) { }
}

// ❌ Depends on specific EmailSender
public class Notification
{
    private EmailSender sender = new EmailSender();
    
    public void Notify() => sender.Send("Hi");
}
✅ Good Example

Depend on abstraction (interface):

public interface IMessageSender
{
    void Send(string msg);
}

public class EmailSender : IMessageSender
{
    public void Send(string msg) { }
}

// ✅ Depends on interface, not concrete class
public class Notification
{
    private readonly IMessageSender sender;
    
    public Notification(IMessageSender sender) =>
        this.sender = sender;
    
    public void Notify() => sender.Send("Hi");
}

✅ Can switch to SMS, Push, etc. without changing Notification!

Remember: High-level modules should depend on abstractions, not concrete implementations!

Part 7: Complete Real-World Example - Library System 📚

Let's build a Library Management System using all SOLID principles!

// ===== SOLID Library System =====

// 1. SRP - Each class has one job
// 2. OCP - Add new book types easily
// 3. LSP - All books work the same way

// Interfaces (ISP - small, focused)
public interface IBook
{
    string Title { get; }
    string Author { get; }
}

public interface IBorrowable
{
    void Borrow();
    void Return();
    bool IsAvailable { get; }
}

public interface IPrintable
{
    void Print();
}

// Abstract base class (OCP - open for extension)
public abstract class Book : IBook, IBorrowable, IPrintable
{
    public string Title { get; set; }
    public string Author { get; set; }
    public bool IsAvailable { get; private set; }
    
    public Book(string title, string author)
    {
        Title = title;
        Author = author;
        IsAvailable = true;
    }
    
    public void Borrow()
    {
        if (!IsAvailable)
            throw new Exception("Book already borrowed");
        IsAvailable = false;
    }
    
    public void Return() => IsAvailable = true;
    public abstract void Print();
}
// Different book types (LSP - substitutable)
public class PhysicalBook : Book
{
    public int Pages { get; set; }
    
    public PhysicalBook(string title, string author, int pages)
        : base(title, author) => Pages = pages;
    
    public override void Print() =>
        Console.WriteLine($"📖 Physical: {Title} by {Author} ({Pages} pages)");
}

public class EBook : Book
{
    public string FileSize { get; set; }
    
    public EBook(string title, string author, string size)
        : base(title, author) => FileSize = size;
    
    public override void Print() =>
        Console.WriteLine($"📱 E-Book: {Title} by {Author} ({FileSize} MB)");
}

// Services (DIP - depend on abstractions)
public interface ILibrary
{
    void AddBook(Book book);
    void DisplayAll();
}

public class Library : ILibrary
{
    private List<Book> books = new List<Book>();
    
    public void AddBook(Book book) => books.Add(book);
    
    public void DisplayAll()
    {
        foreach (var book in books)
            book.Print();
    }
}

// Program (DIP - uses abstractions)
class Program
{
    static void Main()
    {
        ILibrary library = new Library();
        
        library.AddBook(new PhysicalBook("C# Guide", "John Doe", 500));
        library.AddBook(new EBook("Clean Code", "Robert Martin", "2.5"));
        
        library.DisplayAll();
    }
}
🎉 All SOLID principles applied!
  • SRP - Each class has one job
  • OCP - Add new book types easily
  • LSP - All books work the same way
  • ISP - Small, focused interfaces
  • DIP - Depend on abstractions
😂 Library Joke: Why did the book join a gym? To get more abs! (Get it? Abs-tractions? ...I'll stop 😅)

Part 8: Let's Practice! 🎮

What You're Building: A Payment Processing System using SOLID principles!
Support multiple payment methods (Credit Card, PayPal, Crypto) with clean design.
💳 About This Application

This is a Payment System like:

  • 💳 Stripe - Credit card payments
  • 💸 PayPal - Digital wallets
  • 🪙 Coinbase - Crypto payments

You'll build it using SOLID principles:

  • ✅ Each payment method is its own class
  • ✅ Easy to add new payment methods
  • ✅ Clean, testable code
Your Mission:
  1. Interfaces:
    • IPayment - Process()
    • IRefundable - Refund()
    • IReceiptable - GetReceipt()
  2. Abstract Class: Payment
    • Amount property
    • Implement IPayment
  3. Derived Classes:
    • CreditCardPayment
    • PayPalPayment
    • CryptoPayment
  4. Service: PaymentProcessor
    • Accepts any IPayment
    • Processes and generates receipt
💡 SOLID Applied:
  • SRP - Each class has one job
  • OCP - Add new payment types easily
  • LSP - All payments work the same
  • ISP - Small, focused interfaces
  • DIP - Depend on IPayment interface
Result: A flexible payment system that follows all SOLID principles!

🎉 What You Learned Today!

S

Single Responsibility
One job per class

O

Open/Closed
Extend, don't modify

L

Liskov Substitution
Children replace parents

I

Interface Segregation
Don't force unneeded methods

D

Dependency Inversion
Depend on abstractions
Real Example
Library System! 📚
Payment System
SOLID in action! 💳
Professional Code
You're a pro now! 🏆
Test Your Knowledge - Take Quiz