Solid Principles with Real-Time
The SOLID principles! These are five fundamental design principles in object-oriented programming that promote creating understandable, flexible, and maintainable code. Each principle focuses on different aspects of class design and how they interact with each other.
Here's a quick rundown of the five principles:
Single Responsibility Principle (SRP):?A class should have one and only one reason to change.?This keeps classes focused and reduces the risk of introducing bugs when making modifications.
Open/Closed Principle (OCP):?Software entities (classes, functions, etc.) ?should be open for extension (adding new functionality) but closed for modification (changing existing code).?This allows for future adaptations without touching the core code.
Liskov Substitution Principle (LSP):?Subclasses should be substitutable for their base classes without altering the correctness of the program.?This ensures smooth inheritance and compatibility between classes.
Interface Segregation Principle (ISP):?Clients should not be forced to depend on interfaces they don't use.?This prevents unnecessary dependencies and keeps interfaces lean and focused.
Dependency Inversion Principle (DIP):?Depend on?abstractions, not concretions. High-level modules should not depend on low-level modules. Both should depend on abstractions.?This decouples components and makes code more flexible and adaptable.
Understanding and applying these principles can significantly improve the quality of your code. They lead to fewer bugs, easier maintenance, and more flexible software that can evolve.
1. Single Responsibility Principle (SRP)
Violation of SRP in C#:
{
public void ProcessOrder(Order order)
{
// Calculate discounts
// Validate payment
// Send confirmation email
// Update inventory
}
}
This class has multiple responsibilities:
// Separate responsibilities into distinct classes
class OrderDiscountCalculator
{
public void CalculateDiscounts(Order order) { ... }
}
class PaymentValidator
{
public void ValidatePayment(Order order) { ... }
}
class EmailSender
{
public void SendConfirmationEmail(Order order) { ... }
}
class InventoryUpdater
{
public void UpdateInventory(Order order) { ... }
}
class OrderProcessor
{
private readonly OrderDiscountCalculator _discountCalculator;
private readonly PaymentValidator _paymentValidator;
private readonly EmailSender _emailSender;
private readonly InventoryUpdater _inventoryUpdater;
public OrderProcessor(
OrderDiscountCalculator discountCalculator,
PaymentValidator paymentValidator,
EmailSender emailSender,
InventoryUpdater inventoryUpdater)
{
// Inject dependencies
}
public void ProcessOrder(Order order)
{
_discountCalculator.CalculateDiscounts(order);
_paymentValidator.ValidatePayment(order);
_emailSender.SendConfirmationEmail(order);
_inventoryUpdater.UpdateInventory(order);
}
}
Now, each class has a single responsibility.
2. Open/Closed Principle
Violating OCP in C#:
class CheckoutService
{
public void Checkout(PaymentDetails details)
{
if (details.PaymentMethod == "PayPal")
{
// Process payment using PayPal logic directly
}
else if (details.PaymentMethod == "Stripe")
{
// Process payment using Stripe logic directly
}
else
{
// Handle unsupported payment methods
}
}
}
This code violates OCP because:
Solution for OCP in C#:
To adhere to OCP, you'd refactor using interfaces and dependency injection:
interface IPaymentProcessor
{
void ProcessPayment(PaymentDetails details);
}
class PayPalPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(PaymentDetails details)
{
// Process payment using PayPal
}
}
class StripePaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(PaymentDetails details)
{
// Process payment using Stripe
}
}
class CheckoutService
{
private IPaymentProcessor paymentProcessor;
public CheckoutService(IPaymentProcessor paymentProcessor)
{
this.paymentProcessor = paymentProcessor;
}
public void Checkout(PaymentDetails details)
{
paymentProcessor.ProcessPayment(details);
}
New payment methods can be added by implementing IPaymentProcessor, without changing CheckoutService.
Benefits for OCP:
3. Liskov Substitution Principle (LSP)
Violation LSP in C#:
interface IFileWriter
{
void Write(string data);
}
Implementing classes:
class TextFileWriter : IFileWriter
{
public void Write(string data)
{
// Write text to a file
File.WriteAllText("text.txt", data);
}
}
class EncryptedFileWriter : IFileWriter
{
public void Write(string data)
{
// Encrypt data before writing
string encryptedData = Encrypt(data);
File.WriteAllText("encrypted.txt", encryptedData);
}
private string Encrypt(string data)
{
// ... encryption logic
}
}
Potential LSP violation:
领英推荐
The EncryptedFileWriter class, while implementing IFileWriter, potentially violates LSP because:
interface IEncryptedFileWriter : IFileWriter
{
// Additional methods for encryption-related operations
Public string Encrypt(string data)
}
class EncryptedFileWriter : IEncryptedFileWriter
{
// ... (implementation)
}
Benefits for LSP:
4. Interface Segregation Principle
Violation ISP in C#:
interface IDocument
{
void Open();
void Save();
void Print();
void EditContent();
void Approve(); // Not all documents require approval
}
class Memo : IDocument
{
public void Open() { ... }
public void Save() { ... }
public void Print() { ... }
public void EditContent() { ... }
public void Approve() { throw new InvalidOperationException(); } // Violates ISP
}
class Contract : IDocument
{
public void Open() { ... }
public void Save() { ... }
public void Print() { ... }
public void EditContent() { ... }
public void Approve() { ... }
}
Solution for ISP in C#:
interface IEditableDocument
{
void Open();
void Save();
void Print();
void EditContent();
}
interface IApprovableDocument
{
void Approve();
}
Classes
class Memo : IEditableDocument
{
public void Open() { ... }
public void Save() { ... }
public void Print() { ... }
public void EditContent() { ... }
}
class Contract : IEditableDocument, IApprovableDocument
{
public void Open() { ... }
public void Save() { ... }
public void Print() { ... }
public void EditContent() { ... }
public void Approve() { ... }
}
Client Code
void ProcessDocument(IEditableDocument document)
{
document.Open();
document.EditContent();
document.Save();
}
void ApproveContract(IApprovableDocument contract)
{
contract.Approve();
}
Benefits for ISP:
5. Dependency Inversion Principle (DIP)
Violation DIP in C#:
class Logger
{
private DatabaseService databaseService;
public Logger()
{
databaseService = new DatabaseService(); // Tight coupling to concrete class
}
public void LogMessage(string message)
{
databaseService.SaveMessage(message);
}
}
Applying DIP:
Introduce an interface:
interface IDataService
{
void SaveMessage(string message);
}
Make?Logger?depend on the interface:
class Logger
{
private IDataService dataService;
public Logger(IDataService dataService) // Dependency injected through constructor
{
this.dataService = dataService;
}
public void LogMessage(string message)
{
dataService.SaveMessage(message);
}
}
Implement the interface:
class DatabaseService : IDataService
{
public void SaveMessage(string message)
{
// Implementation for saving to database
}
}
Inject the dependency:
// Injecting a database service
var logger = new Logger(new DatabaseService());
logger.LogMessage("Hello, world!");
// Injecting a file service (for testing or flexibility)
var fileService = new FileService();
var logger2 = new Logger(fileService);
logger2.LogMessage("Testing with file service");
Benefits of DIP:
Note: Above C# code snippets not tested.