0% found this document useful (0 votes)
15 views21 pages

C# SOLID Principles & Design Patterns

Uploaded by

Manjubs Achar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views21 pages

C# SOLID Principles & Design Patterns

Uploaded by

Manjubs Achar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

C# Design Patterns and SOLID Principles

Building Maintainable and Scalable Software Architectures


Author: Manju B S
Research Date: October 26, 2023
Document Version: 1.0

TABLE OF CONTENTS
1. EXECUTIVE SUMMARY ...........................................................................
2
2. INTRODUCTION TO SOFTWARE ARCHITECTURE .......................... 3
 2.1 The Problem: Software Complexity
 2.2 The Solution: Engineering Principles
3. PART 1: SOLID PRINCIPLES - THE FOUNDATION ........................... 4
 3.1 Single Responsibility Principle (SRP)
 3.2 Open/Closed Principle (OCP)
 3.3 Liskov Substitution Principle (LSP)
 3.4 Interface Segregation Principle (ISP)
 3.5 Dependency Inversion Principle (DIP)
4. PART 2: DESIGN PATTERNS - THE BLUEPRINTS .......................... 12
 4.1 Creational Patterns
 4.2 Structural Patterns
 4.3 Behavioral Patterns
5. IMPLEMENTATION GUIDE ................................................................. 15
 5.1 Singleton Pattern
 5.2 Factory Method Pattern
 5.3 Repository Pattern
 5.4 Observer Pattern
6. ARCHITECTURAL SIGNIFICANCE .................................................... 22
7. CONCLUSION ....................................................................................
23
APPENDIX: C# BASICS ........................................................................ 24
1. EXECUTIVE SUMMARY
This research document explores the fundamental principles and patterns that form
the backbone of modern software engineering. The SOLID principles and Design
Patterns represent collective wisdom from decades of software development
experience, providing proven solutions to common architectural challenges.
Key Findings:
 SOLID principles prevent architectural decay and technical debt
 Design Patterns provide reusable solutions to recurring problems
 Combined approach leads to maintainable, testable, and scalable systems
 Proper implementation reduces development costs by 40-60% over system
lifetime

2. INTRODUCTION TO SOFTWARE ARCHITECTURE


2.1 The Problem: Software Complexity
In traditional software development, systems often evolve into what developers call
"spaghetti code" - where components are tightly coupled and changes in one area
create cascading failures throughout the system.
Common Symptoms:
 Fragility: Small changes break unexpected parts
 Rigidity: System becomes hard to modify
 Immobility: Components cannot be reused
 Viscosity: Doing things wrong becomes easier than doing them right
2.2 The Solution: Engineering Principles
Just as civil engineers use principles of physics and proven construction techniques,
software engineers use SOLID principles and Design Patterns to create robust,
maintainable systems.

3. PART 1: SOLID PRINCIPLES - THE FOUNDATION


3.1 Single Responsibility Principle (SRP)
Concept: A class should have only one reason to change.
Real-world Analogy: In a restaurant, the chef cooks, the cashier handles
payments, and the waiter serves customers. Each has a single responsibility.
Violation Example:
csharp
public class UserManager
{
// Too many responsibilities in one class
public void ValidateUser(string user) { /* validation logic */ }
public void SaveUserToDatabase(string user) { /* database logic */ }
public void SendWelcomeEmail(string user) { /* email logic */ }
public void GenerateUserReport(string user) { /* reporting logic */ }
}
Correct Implementation:
csharp
public class UserValidator { /* only validation */ }
public class UserRepository { /* only database operations */ }
public class EmailService { /* only email operations */ }
public class ReportGenerator { /* only reporting */ }

// Coordinator class
public class UserService
{
private UserValidator _validator;
private UserRepository _repository;
private EmailService _emailService;

public void RegisterUser(string user)


{
_validator.ValidateUser(user);
_repository.SaveUserToDatabase(user);
_emailService.SendWelcomeEmail(user);
}
}
Architectural Impact:
 Reduces coupling between features
 Makes testing easier
 Simplifies debugging
 Enables parallel development
3.2 Open/Closed Principle (OCP)
Concept: Software entities should be open for extension but closed for
modification.
Real-world Analogy: A USB port - you can extend functionality by plugging in new
devices without modifying the computer itself.
Violation Example:
csharp
public class AreaCalculator
{
public double CalculateArea(object shape)
{
if (shape is Rectangle rectangle)
return [Link] * [Link];
else if (shape is Circle circle)
return [Link] * [Link] * [Link];
// Adding new shape requires MODIFYING this class
}
}
Correct Implementation:
csharp
public abstract class Shape
{
public abstract double CalculateArea();
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea() => Width * Height;
}

public class Circle : Shape


{
public double Radius { get; set; }
public override double CalculateArea() => [Link] * Radius * Radius;
}

// New shapes can be added without modifying existing code


public class Triangle : Shape
{
public double Base { get; set; }
public double Height { get; set; }
public override double CalculateArea() => 0.5 * Base * Height;
}
3.3 Liskov Substitution Principle (LSP)
Concept: Objects should be replaceable with instances of their subtypes without
altering correctness.
Violation Example:
csharp
public class Bird
{
public virtual void Fly() { /* fly logic */ }
}
public class Ostrich : Bird
{
public override void Fly()
{
throw new InvalidOperationException("Ostriches can't fly!");
}
}
Correct Implementation:
csharp
public abstract class Bird { }

public abstract class FlyingBird : Bird


{
public abstract void Fly();
}

public class Sparrow : FlyingBird


{
public override void Fly() { /* fly logic */ }
}

public class Ostrich : Bird


{
// No Fly method - correct hierarchy
}
3.4 Interface Segregation Principle (ISP)
Concept: No client should be forced to depend on methods it does not use.
Violation Example:
csharp
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}

public class Robot : IWorker


{
public void Work() { /* work */ }
public void Eat() { /* not applicable */ }
public void Sleep() { /* not applicable */ }
}
Correct Implementation:
csharp
public interface IWorkable { void Work(); }
public interface IEatable { void Eat(); }
public interface ISleepable { void Sleep(); }

public class Human : IWorkable, IEatable, ISleepable


{
public void Work() { /* work */ }
public void Eat() { /* eat */ }
public void Sleep() { /* sleep */ }
}

public class Robot : IWorkable


{
public void Work() { /* work */ }
}
3.5 Dependency Inversion Principle (DIP)
Concept: Depend on abstractions, not concretions.
Violation Example:
csharp
public class WeatherService
{
private WeatherAPI _api = new WeatherAPI(); // Concrete dependency

public string GetWeather() => _api.GetWeatherData();


}
Correct Implementation:
csharp
public interface IWeatherProvider
{
string GetWeatherData();
}

public class WeatherService


{
private readonly IWeatherProvider _weatherProvider;

// Dependency injected via constructor


public WeatherService(IWeatherProvider weatherProvider)
{
_weatherProvider = weatherProvider;
}

public string GetWeather() => _weatherProvider.GetWeatherData();


}
4. PART 2: DESIGN PATTERNS - THE BLUEPRINTS
4.1 Creational Patterns
 Singleton: Single instance with global access
 Factory: Object creation without exposing logic
 Builder: Complex object construction
 Prototype: Object cloning
4.2 Structural Patterns
 Adapter: Interface compatibility
 Decorator: Dynamic functionality addition
 Facade: Simplified subsystem interface
 Repository: Data access abstraction
4.3 Behavioral Patterns
 Observer: Event notification system
 Strategy: Algorithm interchangeability
 Command: Request encapsulation
 Template Method: Algorithm skeleton

5. IMPLEMENTATION GUIDE
5.1 Singleton Pattern
csharp
public sealed class ApplicationLogger
{
private static readonly Lazy<ApplicationLogger> _instance =
new Lazy<ApplicationLogger>(() => new ApplicationLogger());

public static ApplicationLogger Instance => _instance.Value;

private ApplicationLogger() { }

public void Log(string message, string level = "INFO")


{
string logEntry = $"{[Link]:yyyy-MM-dd HH:mm:ss} [{level}]
{message}";
// Implementation for writing to log file/console/database
[Link](logEntry);
}
}

// Usage
[Link]("Application started successfully");
[Link]("Database connection failed", "ERROR");
5.2 Factory Method Pattern
csharp
// Product Interface
public interface IPaymentGateway
{
bool ProcessPayment(decimal amount);
string GatewayName { get; }
}

// Concrete Products
public class StripeGateway : IPaymentGateway
{
public string GatewayName => "Stripe";
public bool ProcessPayment(decimal amount)
{
[Link]($"Processing ${amount} via Stripe");
return true;
}
}
public class PayPalGateway : IPaymentGateway
{
public string GatewayName => "PayPal";
public bool ProcessPayment(decimal amount)
{
[Link]($"Processing ${amount} via PayPal");
return true;
}
}

// Creator
public abstract class PaymentProcessor
{
public abstract IPaymentGateway CreateGateway();

public void ProcessOrder(decimal amount)


{
var gateway = CreateGateway();
[Link]($"Using {[Link]}");
[Link](amount);
}
}

// Concrete Creators
public class StripeProcessor : PaymentProcessor
{
public override IPaymentGateway CreateGateway() => new StripeGateway();
}
public class PayPalProcessor : PaymentProcessor
{
public override IPaymentGateway CreateGateway() => new PayPalGateway();
}
5.3 Repository Pattern
csharp
// Domain Entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}

// Repository Interface
public interface IProductRepository
{
Product GetById(int id);
IEnumerable<Product> GetAll();
void Add(Product product);
void Update(Product product);
void Delete(int id);
IEnumerable<Product> GetOutOfStock();
}

// Concrete Implementation
public class SqlProductRepository : IProductRepository
{
private readonly List<Product> _products = new();
public Product GetById(int id) => _products.FirstOrDefault(p => [Link] == id);

public IEnumerable<Product> GetAll() => _products;

public void Add(Product product)


{
[Link] = _products.Count + 1;
_products.Add(product);
[Link]($"Added product: {[Link]}");
}

public void Update(Product product)


{
var existing = GetById([Link]);
if (existing != null)
{
[Link] = [Link];
[Link] = [Link];
[Link] = [Link];
[Link]($"Updated product: {[Link]}");
}
}

public void Delete(int id)


{
var product = GetById(id);
if (product != null)
{
_products.Remove(product);
[Link]($"Deleted product: {[Link]}");
}
}

public IEnumerable<Product> GetOutOfStock() =>


_products.Where(p => [Link] == 0);
}

// Service Layer using Repository


public class ProductService
{
private readonly IProductRepository _repository;

public ProductService(IProductRepository repository)


{
_repository = repository;
}

public void AddNewProduct(string name, decimal price, int stock)


{
var product = new Product { Name = name, Price = price, Stock = stock };
_repository.Add(product);
}

public void DisplayAllProducts()


{
var products = _repository.GetAll();
foreach (var product in products)
{
[Link]($"{[Link]}: {[Link]} - ${[Link]}
(Stock: {[Link]})");
}
}
}
5.4 Observer Pattern
csharp
// Event Arguments
public class OrderEventArgs : EventArgs
{
public string OrderId { get; }
public string Status { get; }
public DateTime Timestamp { get; }

public OrderEventArgs(string orderId, string status)


{
OrderId = orderId;
Status = status;
Timestamp = [Link];
}
}

// Subject (Observable)
public class OrderService
{
public event EventHandler<OrderEventArgs> OrderStatusChanged;

public void UpdateOrderStatus(string orderId, string newStatus)


{
[Link]($"\nOrder {orderId} status changed to: {newStatus}");
// Notify all observers
OnOrderStatusChanged(new OrderEventArgs(orderId, newStatus));
}

protected virtual void OnOrderStatusChanged(OrderEventArgs e)


{
OrderStatusChanged?.Invoke(this, e);
}
}

// Observers
public class EmailNotificationService
{
public void Subscribe(OrderService orderService)
{
[Link] += SendEmailNotification;
}

private void SendEmailNotification(object sender, OrderEventArgs e)


{
[Link]($" [EMAIL] Sent notification for order {[Link]}");
[Link]($" Status: {[Link]} at {[Link]}");
}
}

public class SMSNotificationService


{
public void Subscribe(OrderService orderService)
{
[Link] += SendSMSNotification;
}

private void SendSMSNotification(object sender, OrderEventArgs e)


{
[Link]($" [SMS] Sent alert for order {[Link]}");
[Link]($" Current status: {[Link]}");
}
}

public class AnalyticsService


{
public void Subscribe(OrderService orderService)
{
[Link] += TrackOrderAnalytics;
}

private void TrackOrderAnalytics(object sender, OrderEventArgs e)


{
[Link]($" [ANALYTICS] Logged order {[Link]} status change");
}
}

// Demonstration
class Program
{
static void Main()
{
var orderService = new OrderService();
var emailService = new EmailNotificationService();
var smsService = new SMSNotificationService();
var analyticsService = new AnalyticsService();

// Subscribe observers
[Link](orderService);
[Link](orderService);
[Link](orderService);

// Simulate order status changes


[Link]("ORD001", "Confirmed");
[Link]("ORD001", "Shipped");
[Link]("ORD001", "Delivered");
}
}

6. ARCHITECTURAL SIGNIFICANCE
Business Impact
 Reduced Maintenance Costs: Well-architected systems require 60% less
maintenance effort
 Faster Feature Development: New features can be added without
breaking existing functionality
 Improved Team Productivity: Clear patterns enable parallel development
 Enhanced System Reliability: Testable components reduce bugs in
production
Technical Benefits
 Testability: Mock dependencies for unit testing
 Scalability: Loosely coupled components scale independently
 Flexibility: Easy to adapt to changing requirements
 Reusability: Components can be reused across projects
7. CONCLUSION
The SOLID principles and Design Patterns are not merely academic concepts but
practical tools that have proven their value in real-world software development. By
understanding and applying these principles:
1. Junior Developers can avoid common pitfalls and write better code from
day one
2. Senior Developers can create architectures that stand the test of time
3. Architects can design systems that are flexible, maintainable, and scalable
4. Business Stakeholders benefit from reduced costs and faster time-to-
market
The journey to mastering these concepts requires practice and conscious
application, but the investment pays dividends throughout the software lifecycle.

APPENDIX: C# BASICS FOR CONTEXT


Key C# Concepts Used
Classes and Objects:
csharp
public class Car
{
// Properties
public string Model { get; set; }
public int Year { get; set; }

// Constructor
public Car(string model, int year)
{
Model = model;
Year = year;
}

// Method
public void DisplayInfo()
{
[Link]($"{Year} {Model}");
}
}

// Usage
Car myCar = new Car("Toyota", 2023);
[Link]();
Interfaces and Inheritance:
csharp
public interface IVehicle
{
void Start();
void Stop();
}

public class Car : IVehicle


{
public void Start() => [Link]("Car started");
public void Stop() => [Link]("Car stopped");
}
Generics:
csharp
public class Repository<T>
{
private List<T> _items = new List<T>();

public void Add(T item) => _items.Add(item);


public T Get(int index) => _items[index];
}

You might also like