Asp.Net Web Api Syllabus

Solid Principles

The SOLID principles, introduced by Robert C. Martin (Uncle Bob), are a set of design principles that make software more maintainable, scalable, and robust. They are essential for developers aiming to build high-quality, clean, and sustainable software.

What Does SOLID Stand For?

  1. S: Single Responsibility Principle (SRP)
  2. O: Open/Closed Principle (OCP)
  3. L: Liskov Substitution Principle (LSP)
  4. I: Interface Segregation Principle (ISP)
  5. D: Dependency Inversion Principle (DIP)

Let’s explore each principle in detail with examples.

1. Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change.
This means a class should focus on a single responsibility or functionality.

Example:
Bad Design:

public class Report
{
public void GenerateReport()
{
// Logic for generating a report
}

public void SaveToDatabase()
{
// Logic for saving the report to the database
}

public void SendEmail()
{
// Logic for sending the report via email
}
}

In this design, the Report class has multiple responsibilities: generating a report, saving it, and emailing it. Any change in one feature might impact the others.

Better Design:

public class ReportGenerator
{
public void GenerateReport()
{
// Logic for generating a report
}
}

public class ReportSaver
{
public void SaveToDatabase()
{
// Logic for saving the report to the database
}
}

public class EmailSender
{
public void SendEmail()
{
// Logic for sending the report via email
}
}

2. Open/Closed Principle (OCP)

Definition: A class should be open for extension but closed for modification.
This means you should be able to add new functionality without changing existing code.

Example:
Bad Design:

public class PaymentProcessor
{
public void ProcessPayment(string paymentType)
{
if (paymentType == "CreditCard")
{
// Process credit card payment
}
else if (paymentType == "PayPal")
{
// Process PayPal payment
}
}
}

To add a new payment type, you’d need to modify the class.

Better Design:

public interface IPayment
{
void Process();
}

public class CreditCardPayment : IPayment
{
public void Process()
{
// Process credit card payment
}
}

public class PayPalPayment : IPayment
{
public void Process()
{
// Process PayPal payment
}
}

public class PaymentProcessor
{
public void ProcessPayment(IPayment payment)
{
payment.Process();
}
}

Now, adding a new payment type only requires creating a new class that implements IPayment.

3. Liskov Substitution Principle (LSP)

Definition: Subtypes should be substitutable for their base types without altering the correctness of the program.

Example:
Bad Design:

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 = base.Height = value; }
}

public override int Height
{
set { base.Width = base.Height = value; }
}
}

Here, substituting Square for Rectangle can break the program logic.

Better Design:

public interface IShape
{
int Area();
}

public class Rectangle : IShape
{
public int Width { get; set; }
public int Height { get; set; }

public int Area()
{
return Width * Height;
}
}

public class Square : IShape
{
public int Side { get; set; }

public int Area()
{
return Side * Side;
}
}

4. Interface Segregation Principle (ISP)

Definition: A class should not be forced to implement interfaces it does not use.

Example:
Bad Design:

public interface IPrinter
{
void Print();
void Scan();
void Fax();
}

public class BasicPrinter : IPrinter
{
public void Print()
{
// Print implementation
}

public void Scan()
{
throw new NotImplementedException();
}

public void Fax()
{
throw new NotImplementedException();
}
}

Better Design:

public interface IPrinter
{
void Print();
}

public interface IScanner
{
void Scan();
}

public interface IFax
{
void Fax();
}

public class BasicPrinter : IPrinter
{
public void Print()
{
// Print implementation
}
}

5. Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

Example:
Bad Design:

public class LightBulb
{
public void TurnOn() { }
public void TurnOff() { }
}

public class Switch
{
private LightBulb _lightBulb = new LightBulb();

public void Toggle(bool isOn)
{
if (isOn)
_lightBulb.TurnOn();
else
_lightBulb.TurnOff();
}
}

Better Design:

public interface IDevice
{
void TurnOn();
void TurnOff();
}

public class LightBulb : IDevice
{
public void TurnOn() { }
public void TurnOff() { }
}

public class Switch
{
private readonly IDevice _device;

public Switch(IDevice device)
{
_device = device;
}

public void Toggle(bool isOn)
{
if (isOn)
_device.TurnOn();
else
_device.TurnOff();
}
}

Conclusion

By adhering to the SOLID principles, you can write cleaner, modular, and testable code, which leads to easier maintenance and scaling of your projects. Mastering these principles is a fundamental step for becoming a proficient software developer.

Leave a Comment

Your email address will not be published. Required fields are marked *