Behavioral Design Patterns for LLD - IndianTechnoEra
Latest update Android YouTube

Behavioral Design Patterns for LLD

Chapter 5: Behavioral Design Patterns - Mastering Object Communication

Series: Low Level Design for .NET Developers | Previous: Chapter 4: Structural Design Patterns | Next: Chapter 6: .NET Specific Architecture


📖 Introduction

Behavioral design patterns are concerned with algorithms and the assignment of responsibilities between objects. Unlike structural patterns that focus on composition, behavioral patterns focus on communication patterns between objects. They help make complex control flow easier to understand, maintain, and extend.

In this chapter, we'll explore seven essential behavioral patterns:

Pattern Purpose Real-World Analogy
Strategy Encapsulate interchangeable algorithms GPS navigation (different route strategies)
Observer Define one-to-many dependencies Email subscriptions (notify all subscribers)
Chain of Responsibility Pass requests along a chain of handlers Technical support escalation (level 1 → level 2 → level 3)
Command Encapsulate requests as objects Restaurant order (waiter takes order, kitchen executes)
State Alter behavior when internal state changes Traffic light (different behavior per color)
Template Method Define algorithm skeleton, defer steps to subclasses Brewing beverages (boil water → brew → pour → add condiments)
Mediator Reduce coupling between objects Air traffic control (planes communicate via tower)

1. Strategy Pattern

1.1 Understanding Strategy

Definition: The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Real-World Analogy: A GPS navigation system. You can choose different routes (strategies) - fastest route, shortest route, scenic route, avoid highways, etc. The navigation system uses the selected strategy to calculate directions without changing its core functionality.

1.2 ❌ Without Strategy - Rigid Code

// ❌ Without Strategy - Every new shipping method requires modifying this class
public class ShippingCalculator
{
    public decimal CalculateShipping(string method, decimal weight, decimal distance)
    {
        if (method == "Standard")
        {
            return weight * 0.5m + distance * 0.1m;
        }
        else if (method == "Express")
        {
            return weight * 1.0m + distance * 0.2m + 5.0m;
        }
        else if (method == "Overnight")
        {
            return weight * 2.0m + distance * 0.5m + 15.0m;
        }
        else if (method == "International")
        {
            return weight * 1.5m + distance * 0.3m + 20.0m;
        }
        // Adding new shipping method requires modifying this class!
        return 0;
    }
}

1.3 ✅ Strategy Pattern Implementation

// Strategy interface
public interface IShippingStrategy
{
    string StrategyName { get; }
    decimal Calculate(decimal weight, decimal distance);
    bool IsApplicable(string method);
}

// Concrete strategies
public class StandardShippingStrategy : IShippingStrategy
{
    public string StrategyName => "Standard";
    
    public decimal Calculate(decimal weight, decimal distance)
    {
        return weight * 0.5m + distance * 0.1m;
    }
    
    public bool IsApplicable(string method) => method == "Standard";
}

public class ExpressShippingStrategy : IShippingStrategy
{
    public string StrategyName => "Express";
    
    public decimal Calculate(decimal weight, decimal distance)
    {
        return weight * 1.0m + distance * 0.2m + 5.0m;
    }
    
    public bool IsApplicable(string method) => method == "Express";
}

public class OvernightShippingStrategy : IShippingStrategy
{
    public string StrategyName => "Overnight";
    
    public decimal Calculate(decimal weight, decimal distance)
    {
        return weight * 2.0m + distance * 0.5m + 15.0m;
    }
    
    public bool IsApplicable(string method) => method == "Overnight";
}

public class InternationalShippingStrategy : IShippingStrategy
{
    public string StrategyName => "International";
    
    public decimal Calculate(decimal weight, decimal distance)
    {
        // International shipping: base cost + customs handling
        return weight * 1.5m + distance * 0.3m + 20.0m;
    }
    
    public bool IsApplicable(string method) => method == "International";
}

// New strategy - Easy to add without modifying existing code!
public class EcoShippingStrategy : IShippingStrategy
{
    public string StrategyName => "Eco-Friendly";
    
    public decimal Calculate(decimal weight, decimal distance)
    {
        // Eco-friendly shipping: encourages consolidation, longer but greener
        decimal baseCost = weight * 0.4m + distance * 0.05m;
        decimal discount = weight > 10 ? 5.0m : 0; // Discount for heavier items
        return Math.Max(0, baseCost - discount);
    }
    
    public bool IsApplicable(string method) => method == "Eco" || method == "Eco-Friendly";
}

// Context that uses the strategy
public class ShippingCalculator
{
    private readonly List<IShippingStrategy> _strategies;
    
    public ShippingCalculator(IEnumerable<IShippingStrategy> strategies)
    {
        _strategies = strategies.ToList();
    }
    
    public decimal Calculate(string method, decimal weight, decimal distance)
    {
        var strategy = _strategies.FirstOrDefault(s => s.IsApplicable(method));
        
        if (strategy == null)
            throw new NotSupportedException($"Shipping method '{method}' not supported");
        
        Console.WriteLine($"Using {strategy.StrategyName} shipping strategy");
        var cost = strategy.Calculate(weight, distance);
        Console.WriteLine($"Weight: {weight}kg, Distance: {distance}km, Cost: {cost:C}");
        
        return cost;
    }
    
    public List<string> GetAvailableMethods()
    {
        return _strategies.Select(s => s.StrategyName).ToList();
    }
}

// Advanced: Strategy with custom parameters
public interface IPaymentStrategy
{
    bool ProcessPayment(decimal amount, Dictionary<string, string> parameters);
    string PaymentMethod { get; }
}

public class CreditCardPaymentStrategy : IPaymentStrategy
{
    public string PaymentMethod => "Credit Card";
    
    public bool ProcessPayment(decimal amount, Dictionary<string, string> parameters)
    {
        var cardNumber = parameters.GetValueOrDefault("CardNumber");
        var cvv = parameters.GetValueOrDefault("CVV");
        var expiry = parameters.GetValueOrDefault("Expiry");
        
        Console.WriteLine($"Processing credit card payment of {amount:C}");
        Console.WriteLine($"Card: ****{cardNumber?.Substring(Math.Max(0, cardNumber.Length - 4))}");
        
        // Validate and process
        return true;
    }
}

public class PayPalPaymentStrategy : IPaymentStrategy
{
    public string PaymentMethod => "PayPal";
    
    public bool ProcessPayment(decimal amount, Dictionary<string, string> parameters)
    {
        var email = parameters.GetValueOrDefault("Email");
        
        Console.WriteLine($"Processing PayPal payment of {amount:C}");
        Console.WriteLine($"Account: {email}");
        
        return true;
    }
}

public class CryptoPaymentStrategy : IPaymentStrategy
{
    public string PaymentMethod => "Cryptocurrency";
    
    public bool ProcessPayment(decimal amount, Dictionary<string, string> parameters)
    {
        var walletAddress = parameters.GetValueOrDefault("WalletAddress");
        var currency = parameters.GetValueOrDefault("Currency") ?? "BTC";
        
        Console.WriteLine($"Processing crypto payment of {amount:C} in {currency}");
        Console.WriteLine($"Wallet: {walletAddress?.Substring(0, 8)}...");
        
        return true;
    }
}

public class PaymentProcessor
{
    private readonly Dictionary<string, IPaymentStrategy> _strategies;
    
    public PaymentProcessor()
    {
        _strategies = new Dictionary<string, IPaymentStrategy>();
    }
    
    public void RegisterStrategy(IPaymentStrategy strategy)
    {
        _strategies[strategy.PaymentMethod] = strategy;
        Console.WriteLine($"Registered payment method: {strategy.PaymentMethod}");
    }
    
    public bool ProcessPayment(string method, decimal amount, Dictionary<string, string> parameters)
    {
        if (!_strategies.ContainsKey(method))
        {
            Console.WriteLine($"Payment method '{method}' not available");
            return false;
        }
        
        Console.WriteLine($"\n--- Processing {method} Payment ---");
        return _strategies[method].ProcessPayment(amount, parameters);
    }
}

// Usage
public class StrategyDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Strategy Pattern - Shipping Calculator ===\n");
        
        var strategies = new List<IShippingStrategy>
        {
            new StandardShippingStrategy(),
            new ExpressShippingStrategy(),
            new OvernightShippingStrategy(),
            new InternationalShippingStrategy(),
            new EcoShippingStrategy()  // New strategy added without modifying existing code
        };
        
        var calculator = new ShippingCalculator(strategies);
        
        Console.WriteLine("Available shipping methods:");
        foreach (var method in calculator.GetAvailableMethods())
        {
            Console.WriteLine($"  - {method}");
        }
        
        Console.WriteLine("\n--- Calculating Shipping Costs ---");
        calculator.Calculate("Standard", 5.5m, 100m);
        calculator.Calculate("Express", 5.5m, 100m);
        calculator.Calculate("Overnight", 5.5m, 100m);
        calculator.Calculate("International", 5.5m, 1000m);
        calculator.Calculate("Eco", 15m, 100m);
        
        Console.WriteLine("\n\n=== Strategy Pattern - Payment Processing ===\n");
        
        var paymentProcessor = new PaymentProcessor();
        
        // Register payment strategies
        paymentProcessor.RegisterStrategy(new CreditCardPaymentStrategy());
        paymentProcessor.RegisterStrategy(new PayPalPaymentStrategy());
        paymentProcessor.RegisterStrategy(new CryptoPaymentStrategy());
        
        // Process different payments
        paymentProcessor.ProcessPayment("Credit Card", 99.99m, new Dictionary<string, string>
        {
            ["CardNumber"] = "4111111111111111",
            ["CVV"] = "123",
            ["Expiry"] = "12/25"
        });
        
        paymentProcessor.ProcessPayment("PayPal", 49.99m, new Dictionary<string, string>
        {
            ["Email"] = "customer@example.com"
        });
        
        paymentProcessor.ProcessPayment("Cryptocurrency", 299.99m, new Dictionary<string, string>
        {
            ["WalletAddress"] = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
            ["Currency"] = "BTC"
        });
    }
}

2. Observer Pattern

2.1 Understanding Observer

Definition: The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Real-World Analogy: A newspaper publisher (subject) and subscribers (observers). When a new edition is published, all subscribers automatically receive it.

2.2 Observer Pattern Implementation

// Observer interface
public interface IOrderObserver
{
    void Update(Order order);
    string ObserverName { get; }
}

// Subject interface
public interface IOrderSubject
{
    void Attach(IOrderObserver observer);
    void Detach(IOrderObserver observer);
    void NotifyObservers();
}

// Subject - Order
public class Order : IOrderSubject
{
    private readonly List<IOrderObserver> _observers = new List<IOrderObserver>();
    private OrderStatus _status;
    
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public string CustomerEmail { get; set; }
    public decimal TotalAmount { get; set; }
    
    public OrderStatus Status
    {
        get => _status;
        set
        {
            _status = value;
            Console.WriteLine($"\n[Order #{Id}] Status changed to: {_status}");
            NotifyObservers();
        }
    }
    
    public void Attach(IOrderObserver observer)
    {
        _observers.Add(observer);
        Console.WriteLine($"Attached observer: {observer.ObserverName}");
    }
    
    public void Detach(IOrderObserver observer)
    {
        _observers.Remove(observer);
        Console.WriteLine($"Detached observer: {observer.ObserverName}");
    }
    
    public void NotifyObservers()
    {
        foreach (var observer in _observers)
        {
            observer.Update(this);
        }
    }
}

public enum OrderStatus
{
    Pending,
    Confirmed,
    Processing,
    Shipped,
    Delivered,
    Cancelled
}

// Concrete observers
public class EmailNotifier : IOrderObserver
{
    public string ObserverName => "Email Notifier";
    
    public void Update(Order order)
    {
        string subject = $"Order #{order.Id} Status Update";
        string body = $"Dear {order.CustomerName},\n\nYour order status has been updated to: {order.Status}\n\nOrder Total: {order.TotalAmount:C}\n\nThank you for your business!";
        
        Console.WriteLine($"📧 [Email] Sending to {order.CustomerEmail}: {subject}");
        Console.WriteLine($"   {body}");
        // Actual email sending logic would go here
    }
}

public class SmsNotifier : IOrderObserver
{
    private readonly string _phoneNumber;
    
    public SmsNotifier(string phoneNumber)
    {
        _phoneNumber = phoneNumber;
    }
    
    public string ObserverName => "SMS Notifier";
    
    public void Update(Order order)
    {
        string message = $"Order #{order.Id} is now {order.Status}. Total: {order.TotalAmount:C}";
        Console.WriteLine($"📱 [SMS] Sending to {_phoneNumber}: {message}");
        // Actual SMS sending logic would go here
    }
}

public class InventoryManager : IOrderObserver
{
    public string ObserverName => "Inventory Manager";
    
    public void Update(Order order)
    {
        if (order.Status == OrderStatus.Cancelled)
        {
            Console.WriteLine($"🔄 [Inventory] Restocking items for cancelled order #{order.Id}");
            // Restock logic
        }
        else if (order.Status == OrderStatus.Confirmed)
        {
            Console.WriteLine($"📦 [Inventory] Reserving stock for order #{order.Id}");
            // Reserve stock logic
        }
        else if (order.Status == OrderStatus.Shipped)
        {
            Console.WriteLine($"✅ [Inventory] Order #{order.Id} shipped, releasing reserved stock");
            // Release reserved stock
        }
    }
}

public class ShippingDepartment : IOrderObserver
{
    public string ObserverName => "Shipping Department";
    
    public void Update(Order order)
    {
        if (order.Status == OrderStatus.Processing)
        {
            Console.WriteLine($"🚚 [Shipping] Preparing shipping label for order #{order.Id}");
            // Create shipping label
        }
        else if (order.Status == OrderStatus.Shipped)
        {
            Console.WriteLine($"📦 [Shipping] Order #{order.Id} handed to carrier");
            // Update tracking
        }
    }
}

public class LoyaltyProgram : IOrderObserver
{
    public string ObserverName => "Loyalty Program";
    
    public void Update(Order order)
    {
        if (order.Status == OrderStatus.Delivered)
        {
            int pointsEarned = (int)(order.TotalAmount);
            Console.WriteLine($"⭐ [Loyalty] Customer earned {pointsEarned} points for order #{order.Id}");
            // Award points
        }
        else if (order.Status == OrderStatus.Cancelled)
        {
            Console.WriteLine($"⭐ [Loyalty] Points reversed for cancelled order #{order.Id}");
            // Reverse points
        }
    }
}

public class AnalyticsTracker : IOrderObserver
{
    public string ObserverName => "Analytics Tracker";
    
    public void Update(Order order)
    {
        Console.WriteLine($"📊 [Analytics] Recording order event: Order #{order.Id} - {order.Status}");
        Console.WriteLine($"   Customer: {order.CustomerName}, Amount: {order.TotalAmount:C}");
        // Send to analytics service
    }
}

// Event-driven observer with custom event args
public class OrderEventArgs : EventArgs
{
    public Order Order { get; }
    public DateTime EventTime { get; }
    public string EventType { get; }
    
    public OrderEventArgs(Order order, string eventType)
    {
        Order = order;
        EventTime = DateTime.Now;
        EventType = eventType;
    }
}

public class OrderEventPublisher
{
    // Event declaration
    public event EventHandler<OrderEventArgs> OrderCreated;
    public event EventHandler<OrderEventArgs> OrderStatusChanged;
    public event EventHandler<OrderEventArgs> OrderCompleted;
    
    public void CreateOrder(Order order)
    {
        Console.WriteLine($"\n--- Creating new order ---");
        OrderCreated?.Invoke(this, new OrderEventArgs(order, "Created"));
    }
    
    public void UpdateOrderStatus(Order order, OrderStatus newStatus)
    {
        var oldStatus = order.Status;
        order.Status = newStatus;
        
        OrderStatusChanged?.Invoke(this, new OrderEventArgs(order, $"Status changed from {oldStatus} to {newStatus}"));
        
        if (newStatus == OrderStatus.Delivered)
        {
            OrderCompleted?.Invoke(this, new OrderEventArgs(order, "Completed"));
        }
    }
}

// Event handlers
public class EmailEventHandler
{
    public void OnOrderCreated(object sender, OrderEventArgs e)
    {
        Console.WriteLine($"📧 [Event Handler] Order created email sent to {e.Order.CustomerEmail}");
    }
    
    public void OnOrderStatusChanged(object sender, OrderEventArgs e)
    {
        Console.WriteLine($"📧 [Event Handler] Status update email sent to {e.Order.CustomerEmail}");
    }
}

public class WarehouseEventHandler
{
    public void OnOrderCreated(object sender, OrderEventArgs e)
    {
        Console.WriteLine($"🏭 [Warehouse] Order #{e.Order.Id} sent to fulfillment center");
    }
}

// Usage
public class ObserverDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Observer Pattern - Order Tracking System ===\n");
        
        // Create order
        var order = new Order
        {
            Id = 1001,
            CustomerName = "John Doe",
            CustomerEmail = "john@example.com",
            TotalAmount = 299.99m
        };
        
        // Create observers
        var emailNotifier = new EmailNotifier();
        var smsNotifier = new SmsNotifier("+1234567890");
        var inventoryManager = new InventoryManager();
        var shippingDepartment = new ShippingDepartment();
        var loyaltyProgram = new LoyaltyProgram();
        var analyticsTracker = new AnalyticsTracker();
        
        // Attach observers
        Console.WriteLine("--- Attaching Observers ---");
        order.Attach(emailNotifier);
        order.Attach(smsNotifier);
        order.Attach(inventoryManager);
        order.Attach(shippingDepartment);
        order.Attach(loyaltyProgram);
        order.Attach(analyticsTracker);
        
        // Simulate order lifecycle
        Console.WriteLine("\n--- Order Lifecycle ---");
        order.Status = OrderStatus.Confirmed;
        order.Status = OrderStatus.Processing;
        order.Status = OrderStatus.Shipped;
        order.Status = OrderStatus.Delivered;
        
        // Detach some observers
        Console.WriteLine("\n--- Detaching Observers ---");
        order.Detach(smsNotifier);
        
        order.Status = OrderStatus.Cancelled;
        
        Console.WriteLine("\n\n=== Observer Pattern - Event-Based Version ===\n");
        
        var publisher = new OrderEventPublisher();
        var emailHandler = new EmailEventHandler();
        var warehouseHandler = new WarehouseEventHandler();
        
        // Subscribe to events
        publisher.OrderCreated += emailHandler.OnOrderCreated;
        publisher.OrderCreated += warehouseHandler.OnOrderCreated;
        publisher.OrderStatusChanged += emailHandler.OnOrderStatusChanged;
        
        var newOrder = new Order
        {
            Id = 2001,
            CustomerName = "Jane Smith",
            CustomerEmail = "jane@example.com",
            TotalAmount = 149.99m,
            Status = OrderStatus.Pending
        };
        
        publisher.CreateOrder(newOrder);
        publisher.UpdateOrderStatus(newOrder, OrderStatus.Confirmed);
        publisher.UpdateOrderStatus(newOrder, OrderStatus.Shipped);
        publisher.UpdateOrderStatus(newOrder, OrderStatus.Delivered);
    }
}

3. Chain of Responsibility Pattern

3.1 Understanding Chain of Responsibility

Definition: The Chain of Responsibility pattern passes requests along a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain.

Real-World Analogy: Technical support escalation. Level 1 support handles basic issues. If they can't solve it, they escalate to Level 2. Level 2 might escalate to Level 3 or engineering.

3.2 Chain of Responsibility Implementation

// Request class
public class ExpenseRequest
{
    public int Id { get; set; }
    public string EmployeeName { get; set; }
    public string Description { get; set; }
    public decimal Amount { get; set; }
    public string Department { get; set; }
    public DateTime RequestDate { get; set; }
}

// Handler abstract class
public abstract class ExpenseHandler
{
    protected ExpenseHandler _nextHandler;
    protected string HandlerName;
    
    protected ExpenseHandler(string handlerName)
    {
        HandlerName = handlerName;
    }
    
    public void SetNext(ExpenseHandler handler)
    {
        _nextHandler = handler;
    }
    
    public abstract void Handle(ExpenseRequest request);
    
    protected void PassToNext(ExpenseRequest request)
    {
        if (_nextHandler != null)
        {
            Console.WriteLine($"   → Escalating to {_nextHandler.HandlerName}");
            _nextHandler.Handle(request);
        }
        else
        {
            Console.WriteLine($"   ❌ Request #{request.Id} cannot be approved - exceeds all approval limits");
        }
    }
}

// Concrete handlers
public class TeamLeadHandler : ExpenseHandler
{
    private readonly decimal _approvalLimit = 1000;
    
    public TeamLeadHandler() : base("Team Lead") { }
    
    public override void Handle(ExpenseRequest request)
    {
        Console.WriteLine($"\n[Team Lead] Reviewing request #{request.Id} for {request.Amount:C}");
        
        if (request.Amount <= _approvalLimit)
        {
            Console.WriteLine($"   ✅ Approved by Team Lead for ${request.Amount}");
            Console.WriteLine($"   📝 Notes: Request approved at team lead level");
        }
        else
        {
            Console.WriteLine($"   ⚠️  Amount ${request.Amount} exceeds team lead limit of ${_approvalLimit}");
            PassToNext(request);
        }
    }
}

public class DepartmentManagerHandler : ExpenseHandler
{
    private readonly decimal _approvalLimit = 5000;
    
    public DepartmentManagerHandler() : base("Department Manager") { }
    
    public override void Handle(ExpenseRequest request)
    {
        Console.WriteLine($"\n[Department Manager] Reviewing request #{request.Id} for {request.Amount:C}");
        
        // Additional validation for department manager
        if (request.Amount > 2000 && request.Department != "Sales")
        {
            Console.WriteLine($"   ⚠️  Department Manager: High-value request from non-sales department requires justification");
            // Could add additional checks here
        }
        
        if (request.Amount <= _approvalLimit)
        {
            Console.WriteLine($"   ✅ Approved by Department Manager for ${request.Amount}");
            Console.WriteLine($"   📝 Notes: Approved with department budget allocation");
        }
        else
        {
            Console.WriteLine($"   ⚠️  Amount ${request.Amount} exceeds department manager limit of ${_approvalLimit}");
            PassToNext(request);
        }
    }
}

public class DirectorHandler : ExpenseHandler
{
    private readonly decimal _approvalLimit = 20000;
    
    public DirectorHandler() : base("Director") { }
    
    public override void Handle(ExpenseRequest request)
    {
        Console.WriteLine($"\n[Director] Reviewing request #{request.Id} for {request.Amount:C}");
        
        // Director level approval with business justification
        if (request.Amount <= _approvalLimit)
        {
            Console.WriteLine($"   ✅ Approved by Director for ${request.Amount}");
            
            if (request.Amount > 10000)
            {
                Console.WriteLine($"   📊 Note: High-value expense flagged for quarterly review");
            }
        }
        else
        {
            Console.WriteLine($"   ⚠️  Amount ${request.Amount} exceeds director limit of ${_approvalLimit}");
            PassToNext(request);
        }
    }
}

public class VPFinanceHandler : ExpenseHandler
{
    private readonly decimal _approvalLimit = 50000;
    
    public VPFinanceHandler() : base("VP of Finance") { }
    
    public override void Handle(ExpenseRequest request)
    {
        Console.WriteLine($"\n[VP Finance] Reviewing request #{request.Id} for {request.Amount:C}");
        
        if (request.Amount <= _approvalLimit)
        {
            Console.WriteLine($"   ✅ Approved by VP Finance for ${request.Amount}");
            Console.WriteLine($"   📋 Note: Requires CFO sign-off for final approval");
        }
        else
        {
            Console.WriteLine($"   ⚠️  Amount ${request.Amount} exceeds VP Finance limit of ${_approvalLimit}");
            PassToNext(request);
        }
    }
}

public class CFOHandler : ExpenseHandler
{
    private readonly decimal _approvalLimit = 100000;
    
    public CFOHandler() : base("CFO") { }
    
    public override void Handle(ExpenseRequest request)
    {
        Console.WriteLine($"\n[CFO] Reviewing request #{request.Id} for {request.Amount:C}");
        
        if (request.Amount <= _approvalLimit)
        {
            Console.WriteLine($"   ✅ Approved by CFO for ${request.Amount}");
            Console.WriteLine($"   📈 Note: Expense will be included in quarterly financial report");
        }
        else
        {
            Console.WriteLine($"   ⚠️  Amount ${request.Amount} exceeds CFO limit of ${_approvalLimit}");
            PassToNext(request);
        }
    }
}

// Another example - Logger chain
public enum LogLevel
{
    Debug,
    Info,
    Warning,
    Error,
    Critical
}

public class LogMessage
{
    public LogLevel Level { get; set; }
    public string Message { get; set; }
    public DateTime Timestamp { get; set; }
    public string Source { get; set; }
    public Exception Exception { get; set; }
}

public abstract class LoggerHandler
{
    protected LoggerHandler _next;
    protected LogLevel _threshold;
    
    protected LoggerHandler(LogLevel threshold)
    {
        _threshold = threshold;
    }
    
    public void SetNext(LoggerHandler next)
    {
        _next = next;
    }
    
    public void Log(LogMessage message)
    {
        if (message.Level >= _threshold)
        {
            WriteLog(message);
        }
        
        if (_next != null && message.Level > _threshold)
        {
            _next.Log(message);
        }
    }
    
    protected abstract void WriteLog(LogMessage message);
}

public class ConsoleLogger : LoggerHandler
{
    public ConsoleLogger(LogLevel threshold) : base(threshold) { }
    
    protected override void WriteLog(LogMessage message)
    {
        Console.ForegroundColor = message.Level switch
        {
            LogLevel.Error or LogLevel.Critical => ConsoleColor.Red,
            LogLevel.Warning => ConsoleColor.Yellow,
            _ => ConsoleColor.White
        };
        
        Console.WriteLine($"[Console] {message.Timestamp:T} [{message.Level}] {message.Message}");
        Console.ResetColor();
    }
}

public class FileLogger : LoggerHandler
{
    private readonly string _filePath;
    
    public FileLogger(LogLevel threshold, string filePath) : base(threshold)
    {
        _filePath = filePath;
    }
    
    protected override void WriteLog(LogMessage message)
    {
        var logEntry = $"{message.Timestamp:yyyy-MM-dd HH:mm:ss} [{message.Level}] {message.Message}";
        if (message.Exception != null)
            logEntry += $"\nException: {message.Exception.Message}";
            
        File.AppendAllText(_filePath, logEntry + Environment.NewLine);
        Console.WriteLine($"[File] Written to {_filePath}");
    }
}

public class DatabaseLogger : LoggerHandler
{
    public DatabaseLogger(LogLevel threshold) : base(threshold) { }
    
    protected override void WriteLog(LogMessage message)
    {
        // Simulate database logging
        Console.WriteLine($"[Database] Saving log entry for {message.Level}");
    }
}

public class EmailLogger : LoggerHandler
{
    private readonly string _adminEmail;
    
    public EmailLogger(LogLevel threshold, string adminEmail) : base(threshold)
    {
        _adminEmail = adminEmail;
    }
    
    protected override void WriteLog(LogMessage message)
    {
        if (message.Level >= LogLevel.Error)
        {
            Console.WriteLine($"[Email] Sending alert to {_adminEmail}: {message.Message}");
        }
    }
}

// Usage
public class ChainOfResponsibilityDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Chain of Responsibility - Expense Approval System ===\n");
        
        // Build the chain
        var teamLead = new TeamLeadHandler();
        var manager = new DepartmentManagerHandler();
        var director = new DirectorHandler();
        var vpFinance = new VPFinanceHandler();
        var cfo = new CFOHandler();
        
        teamLead.SetNext(manager);
        manager.SetNext(director);
        director.SetNext(vpFinance);
        vpFinance.SetNext(cfo);
        
        // Test requests
        var requests = new List<ExpenseRequest>
        {
            new ExpenseRequest
            {
                Id = 1,
                EmployeeName = "John Smith",
                Description = "Office supplies",
                Amount = 500,
                Department = "IT",
                RequestDate = DateTime.Now
            },
            new ExpenseRequest
            {
                Id = 2,
                EmployeeName = "Jane Doe",
                Description = "New laptop",
                Amount = 2500,
                Department = "Engineering",
                RequestDate = DateTime.Now
            },
            new ExpenseRequest
            {
                Id = 3,
                EmployeeName = "Bob Johnson",
                Description = "Team building event",
                Amount = 8000,
                Department = "Sales",
                RequestDate = DateTime.Now
            },
            new ExpenseRequest
            {
                Id = 4,
                EmployeeName = "Alice Williams",
                Description = "Enterprise software license",
                Amount = 35000,
                Department = "Operations",
                RequestDate = DateTime.Now
            },
            new ExpenseRequest
            {
                Id = 5,
                EmployeeName = "Charlie Brown",
                Description = "Company retreat",
                Amount = 75000,
                Department = "HR",
                RequestDate = DateTime.Now
            }
        };
        
        foreach (var request in requests)
        {
            Console.WriteLine($"\n{'='.PadRight(50, '=')}");
            Console.WriteLine($"Processing Expense Request #{request.Id}");
            Console.WriteLine($"Employee: {request.EmployeeName}");
            Console.WriteLine($"Amount: {request.Amount:C}");
            Console.WriteLine($"Description: {request.Description}");
            Console.WriteLine($"Department: {request.Department}");
            Console.WriteLine($"{'='.PadRight(50, '=')}");
            
            teamLead.Handle(request);
        }
        
        Console.WriteLine("\n\n=== Chain of Responsibility - Logging System ===\n");
        
        // Build logger chain
        var consoleLogger = new ConsoleLogger(LogLevel.Debug);
        var fileLogger = new FileLogger(LogLevel.Info, "app.log");
        var dbLogger = new DatabaseLogger(LogLevel.Error);
        var emailLogger = new EmailLogger(LogLevel.Critical, "admin@company.com");
        
        consoleLogger.SetNext(fileLogger);
        fileLogger.SetNext(dbLogger);
        dbLogger.SetNext(emailLogger);
        
        // Log messages at different levels
        var messages = new[]
        {
            new LogMessage { Level = LogLevel.Debug, Message = "Debugging variable x", Timestamp = DateTime.Now },
            new LogMessage { Level = LogLevel.Info, Message = "User logged in", Timestamp = DateTime.Now },
            new LogMessage { Level = LogLevel.Warning, Message = "Disk space low", Timestamp = DateTime.Now },
            new LogMessage { Level = LogLevel.Error, Message = "Database connection failed", Timestamp = DateTime.Now, Exception = new Exception("Timeout") },
            new LogMessage { Level = LogLevel.Critical, Message = "System unavailable", Timestamp = DateTime.Now }
        };
        
        foreach (var msg in messages)
        {
            Console.WriteLine($"\n--- Logging {msg.Level} Message ---");
            consoleLogger.Log(msg);
        }
    }
}

4. Command Pattern

4.1 Understanding Command

Definition: The Command pattern encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of requests. It also supports undoable operations.

Real-World Analogy: A restaurant order. The waiter takes an order (command) from a customer and places it in the kitchen queue. The chef executes the command later. The same command pattern supports cancelling orders (undo).

4.2 Command Pattern Implementation

// Receiver - The actual business logic
public class Light
{
    private string _location;
    private bool _isOn;
    
    public Light(string location)
    {
        _location = location;
    }
    
    public void TurnOn()
    {
        _isOn = true;
        Console.WriteLine($"💡 Light in {_location} is ON");
    }
    
    public void TurnOff()
    {
        _isOn = false;
        Console.WriteLine($"💡 Light in {_location} is OFF");
    }
    
    public bool IsOn => _isOn;
}

public class Thermostat
{
    private int _temperature = 22;
    private string _location;
    
    public Thermostat(string location)
    {
        _location = location;
    }
    
    public void SetTemperature(int temperature)
    {
        _temperature = temperature;
        Console.WriteLine($"🌡️ Thermostat in {_location} set to {_temperature}°C");
    }
    
    public int GetTemperature() => _temperature;
}

public class GarageDoor
{
    private bool _isOpen;
    
    public void Open()
    {
        _isOpen = true;
        Console.WriteLine($"🚪 Garage door OPENED");
    }
    
    public void Close()
    {
        _isOpen = false;
        Console.WriteLine($"🚪 Garage door CLOSED");
    }
    
    public bool IsOpen => _isOpen;
}

public class Stereo
{
    private string _location;
    private int _volume = 10;
    private string _source = "Radio";
    
    public Stereo(string location)
    {
        _location = location;
    }
    
    public void TurnOn()
    {
        Console.WriteLine($"🎵 Stereo in {_location} turned ON");
    }
    
    public void TurnOff()
    {
        Console.WriteLine($"🎵 Stereo in {_location} turned OFF");
    }
    
    public void SetVolume(int volume)
    {
        _volume = volume;
        Console.WriteLine($"🎵 Stereo volume set to {_volume}");
    }
    
    public void SetSource(string source)
    {
        _source = source;
        Console.WriteLine($"🎵 Stereo source set to {_source}");
    }
}

// Command interface
public interface ICommand
{
    void Execute();
    void Undo();
    string Description { get; }
}

// Concrete commands
public class LightOnCommand : ICommand
{
    private Light _light;
    
    public LightOnCommand(Light light)
    {
        _light = light;
    }
    
    public string Description => $"Turn on light in {_light.GetType().GetProperty("_location")?.GetValue(_light) ?? "unknown location"}";
    
    public void Execute()
    {
        _light.TurnOn();
    }
    
    public void Undo()
    {
        _light.TurnOff();
    }
}

public class LightOffCommand : ICommand
{
    private Light _light;
    
    public LightOffCommand(Light light)
    {
        _light = light;
    }
    
    public string Description => $"Turn off light";
    
    public void Execute()
    {
        _light.TurnOff();
    }
    
    public void Undo()
    {
        _light.TurnOn();
    }
}

public class ThermostatSetCommand : ICommand
{
    private Thermostat _thermostat;
    private int _newTemperature;
    private int _oldTemperature;
    
    public ThermostatSetCommand(Thermostat thermostat, int temperature)
    {
        _thermostat = thermostat;
        _newTemperature = temperature;
    }
    
    public string Description => $"Set thermostat to {_newTemperature}°C";
    
    public void Execute()
    {
        _oldTemperature = _thermostat.GetTemperature();
        _thermostat.SetTemperature(_newTemperature);
    }
    
    public void Undo()
    {
        _thermostat.SetTemperature(_oldTemperature);
    }
}

public class GarageDoorOpenCommand : ICommand
{
    private GarageDoor _door;
    
    public GarageDoorOpenCommand(GarageDoor door)
    {
        _door = door;
    }
    
    public string Description => "Open garage door";
    
    public void Execute()
    {
        _door.Open();
    }
    
    public void Undo()
    {
        _door.Close();
    }
}

public class GarageDoorCloseCommand : ICommand
{
    private GarageDoor _door;
    
    public GarageDoorCloseCommand(GarageDoor door)
    {
        _door = door;
    }
    
    public string Description => "Close garage door";
    
    public void Execute()
    {
        _door.Close();
    }
    
    public void Undo()
    {
        _door.Open();
    }
}

public class MacroCommand : ICommand
{
    private List<ICommand> _commands = new List<ICommand>();
    private string _name;
    
    public MacroCommand(string name)
    {
        _name = name;
    }
    
    public void AddCommand(ICommand command)
    {
        _commands.Add(command);
    }
    
    public string Description => $"Macro: {_name} ({_commands.Count} commands)";
    
    public void Execute()
    {
        Console.WriteLine($"\n🎬 Executing macro: {_name}");
        foreach (var command in _commands)
        {
            command.Execute();
        }
    }
    
    public void Undo()
    {
        Console.WriteLine($"\n⏪ Undoing macro: {_name}");
        for (int i = _commands.Count - 1; i >= 0; i--)
        {
            _commands[i].Undo();
        }
    }
}

// Invoker
public class RemoteControl
{
    private Dictionary<int, ICommand> _slots = new Dictionary<int, ICommand>();
    private Stack<ICommand> _commandHistory = new Stack<ICommand>();
    
    public void SetCommand(int slot, ICommand command)
    {
        _slots[slot] = command;
        Console.WriteLine($"Configured slot {slot}: {command.Description}");
    }
    
    public void PressButton(int slot)
    {
        if (_slots.ContainsKey(slot))
        {
            Console.WriteLine($"\n🔘 Pressing button {slot}");
            var command = _slots[slot];
            command.Execute();
            _commandHistory.Push(command);
        }
        else
        {
            Console.WriteLine($"No command configured for slot {slot}");
        }
    }
    
    public void UndoLastCommand()
    {
        if (_commandHistory.Count > 0)
        {
            Console.WriteLine($"\n⏪ Undo last command");
            var lastCommand = _commandHistory.Pop();
            lastCommand.Undo();
        }
        else
        {
            Console.WriteLine("No commands to undo");
        }
    }
    
    public void ShowHistory()
    {
        Console.WriteLine($"\nCommand history ({_commandHistory.Count} commands):");
        foreach (var cmd in _commandHistory)
        {
            Console.WriteLine($"  - {cmd.Description}");
        }
    }
}

// Command Queue for asynchronous processing
public class CommandQueue
{
    private Queue<ICommand> _queue = new Queue<ICommand>();
    private bool _isProcessing = false;
    
    public void Enqueue(ICommand command)
    {
        _queue.Enqueue(command);
        Console.WriteLine($"📥 Enqueued: {command.Description}");
        ProcessQueue();
    }
    
    private async void ProcessQueue()
    {
        if (_isProcessing) return;
        
        _isProcessing = true;
        
        while (_queue.Count > 0)
        {
            var command = _queue.Dequeue();
            Console.WriteLine($"⚙️ Processing: {command.Description}");
            await Task.Delay(500); // Simulate async processing
            command.Execute();
        }
        
        _isProcessing = false;
    }
}

// Usage
public class CommandDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Command Pattern - Home Automation System ===\n");
        
        // Create receivers
        var livingRoomLight = new Light("Living Room");
        var kitchenLight = new Light("Kitchen");
        var thermostat = new Thermostat("Living Room");
        var garageDoor = new GarageDoor();
        var stereo = new Stereo("Living Room");
        
        // Create commands
        var livingRoomLightOn = new LightOnCommand(livingRoomLight);
        var livingRoomLightOff = new LightOffCommand(livingRoomLight);
        var kitchenLightOn = new LightOnCommand(kitchenLight);
        var kitchenLightOff = new LightOffCommand(kitchenLight);
        var setTemp22 = new ThermostatSetCommand(thermostat, 22);
        var setTemp24 = new ThermostatSetCommand(thermostat, 24);
        var garageOpen = new GarageDoorOpenCommand(garageDoor);
        var garageClose = new GarageDoorCloseCommand(garageDoor);
        
        // Create macro commands
        var movieNightMacro = new MacroCommand("Movie Night");
        movieNightMacro.AddCommand(livingRoomLightOff);
        movieNightMacro.AddCommand(new ThermostatSetCommand(thermostat, 20));
        movieNightMacro.AddCommand(new LightOnCommand(new Light("Hallway")));
        
        var partyMacro = new MacroCommand("Party Mode");
        partyMacro.AddCommand(livingRoomLightOn);
        partyMacro.AddCommand(kitchenLightOn);
        partyMacro.AddCommand(setTemp22);
        
        // Setup remote control
        var remote = new RemoteControl();
        remote.SetCommand(1, livingRoomLightOn);
        remote.SetCommand(2, livingRoomLightOff);
        remote.SetCommand(3, kitchenLightOn);
        remote.SetCommand(4, kitchenLightOff);
        remote.SetCommand(5, setTemp22);
        remote.SetCommand(6, setTemp24);
        remote.SetCommand(7, garageOpen);
        remote.SetCommand(8, garageClose);
        remote.SetCommand(9, movieNightMacro);
        remote.SetCommand(0, partyMacro);
        
        // Test commands
        remote.PressButton(1);  // Living room light on
        remote.PressButton(3);  // Kitchen light on
        remote.PressButton(6);  // Set temp to 24
        remote.PressButton(7);  // Open garage door
        remote.PressButton(2);  // Living room light off
        
        remote.UndoLastCommand();  // Undo living room light off
        
        remote.PressButton(9);  // Movie night macro
        remote.PressButton(0);  // Party macro
        
        remote.ShowHistory();
        
        // Command queue demo
        Console.WriteLine("\n\n=== Command Pattern - Command Queue ===\n");
        var queue = new CommandQueue();
        
        queue.Enqueue(livingRoomLightOn);
        queue.Enqueue(setTemp24);
        queue.Enqueue(garageOpen);
        queue.Enqueue(kitchenLightOn);
        
        Console.WriteLine("\nCommands queued for async processing...");
    }
}

5. State Pattern

5.1 Understanding State

Definition: The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

Real-World Analogy: A traffic light. The light behaves differently depending on its current state (red, yellow, green). The state transitions are predefined, and each state knows what to do next.

5.2 State Pattern Implementation

// State interface
public interface IOrderState
{
    void Process(OrderContext order);
    void Ship(OrderContext order);
    void Deliver(OrderContext order);
    void Cancel(OrderContext order);
    string GetStateName();
}

// Context
public class OrderContext
{
    private IOrderState _currentState;
    public int Id { get; }
    public string CustomerName { get; }
    public List<string> Items { get; }
    public DateTime CreatedAt { get; }
    public DateTime? ShippedAt { get; private set; }
    public DateTime? DeliveredAt { get; private set; }
    public string TrackingNumber { get; private set; }
    
    public OrderContext(int id, string customerName)
    {
        Id = id;
        CustomerName = customerName;
        Items = new List<string>();
        CreatedAt = DateTime.Now;
        _currentState = new PendingState();
        Console.WriteLine($"Order #{Id} created in {_currentState.GetStateName()} state");
    }
    
    public void SetState(IOrderState state)
    {
        Console.WriteLine($"Order #{Id}: {_currentState.GetStateName()} → {state.GetStateName()}");
        _currentState = state;
    }
    
    public void AddItem(string item)
    {
        Items.Add(item);
        Console.WriteLine($"Added item: {item}");
    }
    
    public void SetTrackingNumber(string trackingNumber)
    {
        TrackingNumber = trackingNumber;
        Console.WriteLine($"Tracking number assigned: {trackingNumber}");
    }
    
    public void SetShippedDate()
    {
        ShippedAt = DateTime.Now;
    }
    
    public void SetDeliveredDate()
    {
        DeliveredAt = DateTime.Now;
    }
    
    public void Process()
    {
        Console.WriteLine($"\n--- Processing Order #{Id} ---");
        _currentState.Process(this);
    }
    
    public void Ship()
    {
        Console.WriteLine($"\n--- Shipping Order #{Id} ---");
        _currentState.Ship(this);
    }
    
    public void Deliver()
    {
        Console.WriteLine($"\n--- Delivering Order #{Id} ---");
        _currentState.Deliver(this);
    }
    
    public void Cancel()
    {
        Console.WriteLine($"\n--- Cancelling Order #{Id} ---");
        _currentState.Cancel(this);
    }
    
    public void ShowStatus()
    {
        Console.WriteLine($"\nOrder #{Id} Status: {_currentState.GetStateName()}");
        if (ShippedAt.HasValue) Console.WriteLine($"  Shipped: {ShippedAt}");
        if (DeliveredAt.HasValue) Console.WriteLine($"  Delivered: {DeliveredAt}");
        if (!string.IsNullOrEmpty(TrackingNumber)) Console.WriteLine($"  Tracking: {TrackingNumber}");
    }
}

// Concrete states
public class PendingState : IOrderState
{
    public void Process(OrderContext order)
    {
        if (order.Items.Count == 0)
        {
            Console.WriteLine("❌ Cannot process order with no items");
            return;
        }
        
        Console.WriteLine("✅ Order validated and ready for processing");
        order.SetState(new ProcessingState());
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("❌ Cannot ship order before processing");
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine("❌ Cannot deliver order before shipping");
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("✅ Order cancelled before processing");
        order.SetState(new CancelledState());
    }
    
    public string GetStateName() => "Pending";
}

public class ProcessingState : IOrderState
{
    public void Process(OrderContext order)
    {
        Console.WriteLine("⏳ Order is already being processed");
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("📦 Preparing order for shipment");
        order.SetTrackingNumber($"TRK-{order.Id}-{DateTime.Now.Ticks}");
        order.SetShippedDate();
        order.SetState(new ShippedState());
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine("❌ Cannot deliver order before shipping");
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("✅ Order cancelled during processing");
        order.SetState(new CancelledState());
    }
    
    public string GetStateName() => "Processing";
}

public class ShippedState : IOrderState
{
    public void Process(OrderContext order)
    {
        Console.WriteLine("❌ Cannot process order that has already shipped");
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("⚠️ Order is already shipped");
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine($"🚚 Order out for delivery");
        order.SetDeliveredDate();
        order.SetState(new DeliveredState());
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("❌ Cannot cancel order after shipping");
    }
    
    public string GetStateName() => "Shipped";
}

public class DeliveredState : IOrderState
{
    public void Process(OrderContext order)
    {
        Console.WriteLine("❌ Order has already been delivered");
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("❌ Order has already been delivered");
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine("✅ Order already delivered");
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("✅ Order can be returned within 30 days");
        order.SetState(new ReturnEligibleState());
    }
    
    public string GetStateName() => "Delivered";
}

public class ReturnEligibleState : IOrderState
{
    public void Process(OrderContext order)
    {
        Console.WriteLine("❌ Cannot process delivered order");
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("❌ Cannot ship returned order");
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine("✅ Order is eligible for return");
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("🔄 Processing return request");
        order.SetState(new CancelledState());
    }
    
    public string GetStateName() => "Return Eligible";
}

public class CancelledState : IOrderState
{
    public void Process(OrderContext order)
    {
        Console.WriteLine("❌ Cannot process cancelled order");
    }
    
    public void Ship(OrderContext order)
    {
        Console.WriteLine("❌ Cannot ship cancelled order");
    }
    
    public void Deliver(OrderContext order)
    {
        Console.WriteLine("❌ Cannot deliver cancelled order");
    }
    
    public void Cancel(OrderContext order)
    {
        Console.WriteLine("✅ Order already cancelled");
    }
    
    public string GetStateName() => "Cancelled";
}

// Another example - Vending Machine
public interface IVendingMachineState
{
    void InsertMoney(decimal amount);
    void SelectProduct(string product);
    void Dispense();
    void Refund();
}

public class VendingMachine
{
    private IVendingMachineState _currentState;
    private decimal _balance;
    private string _selectedProduct;
    private Dictionary<string, decimal> _products;
    
    public VendingMachine()
    {
        _products = new Dictionary<string, decimal>
        {
            ["Soda"] = 1.50m,
            ["Chips"] = 1.00m,
            ["Candy"] = 0.75m,
            ["Water"] = 1.00m
        };
        
        _currentState = new NoMoneyState(this);
        Console.WriteLine("Vending Machine ready. State: No Money");
    }
    
    public void SetState(IVendingMachineState state)
    {
        _currentState = state;
        Console.WriteLine($"State changed to: {state.GetType().Name}");
    }
    
    public void InsertMoney(decimal amount)
    {
        Console.WriteLine($"\nInserted ${amount:F2}");
        _currentState.InsertMoney(amount);
    }
    
    public void SelectProduct(string product)
    {
        Console.WriteLine($"\nSelected: {product}");
        _currentState.SelectProduct(product);
    }
    
    public void Dispense()
    {
        Console.WriteLine($"\nDispensing...");
        _currentState.Dispense();
    }
    
    public void Refund()
    {
        Console.WriteLine($"\nRequesting refund...");
        _currentState.Refund();
    }
    
    public void AddMoney(decimal amount)
    {
        _balance += amount;
        Console.WriteLine($"Balance: ${_balance:F2}");
    }
    
    public decimal GetBalance() => _balance;
    
    public void SetSelectedProduct(string product)
    {
        _selectedProduct = product;
    }
    
    public string GetSelectedProduct() => _selectedProduct;
    
    public decimal GetProductPrice(string product)
    {
        return _products.ContainsKey(product) ? _products[product] : 0;
    }
    
    public void Reset()
    {
        _balance = 0;
        _selectedProduct = null;
        SetState(new NoMoneyState(this));
    }
    
    public void DispenseProduct()
    {
        Console.WriteLine($"Dispensing {_selectedProduct}");
        _balance -= _products[_selectedProduct];
        _selectedProduct = null;
        
        if (_balance > 0)
        {
            Console.WriteLine($"Remaining balance: ${_balance:F2}");
            SetState(new HasMoneyState(this));
        }
        else
        {
            SetState(new NoMoneyState(this));
        }
    }
}

public class NoMoneyState : IVendingMachineState
{
    private VendingMachine _machine;
    
    public NoMoneyState(VendingMachine machine)
    {
        _machine = machine;
    }
    
    public void InsertMoney(decimal amount)
    {
        _machine.AddMoney(amount);
        _machine.SetState(new HasMoneyState(_machine));
    }
    
    public void SelectProduct(string product)
    {
        Console.WriteLine("❌ Please insert money first");
    }
    
    public void Dispense()
    {
        Console.WriteLine("❌ No money inserted");
    }
    
    public void Refund()
    {
        Console.WriteLine("❌ No money to refund");
    }
}

public class HasMoneyState : IVendingMachineState
{
    private VendingMachine _machine;
    
    public HasMoneyState(VendingMachine machine)
    {
        _machine = machine;
    }
    
    public void InsertMoney(decimal amount)
    {
        _machine.AddMoney(amount);
        Console.WriteLine($"Total balance: ${_machine.GetBalance():F2}");
    }
    
    public void SelectProduct(string product)
    {
        var price = _machine.GetProductPrice(product);
        
        if (price == 0)
        {
            Console.WriteLine($"❌ Product {product} not available");
            return;
        }
        
        if (_machine.GetBalance() >= price)
        {
            _machine.SetSelectedProduct(product);
            _machine.Dispense();
        }
        else
        {
            Console.WriteLine($"❌ Insufficient funds. Need ${price - _machine.GetBalance():F2} more");
        }
    }
    
    public void Dispense()
    {
        if (_machine.GetSelectedProduct() != null)
        {
            _machine.DispenseProduct();
        }
        else
        {
            Console.WriteLine("❌ No product selected");
        }
    }
    
    public void Refund()
    {
        var refund = _machine.GetBalance();
        _machine.Reset();
        Console.WriteLine($"💰 Refunded ${refund:F2}");
    }
}

// Usage
public class StateDemo
{
    public static void Run()
    {
        Console.WriteLine("=== State Pattern - Order Management System ===\n");
        
        var order = new OrderContext(1001, "John Doe");
        order.AddItem("Laptop");
        order.AddItem("Mouse");
        
        order.ShowStatus();
        
        order.Process();
        order.Ship();
        order.Deliver();
        
        order.ShowStatus();
        
        // Try invalid transitions
        Console.WriteLine("\n--- Testing Invalid Transitions ---");
        order.Process();  // Cannot process delivered order
        
        Console.WriteLine("\n--- Testing Cancel Scenarios ---");
        var order2 = new OrderContext(1002, "Jane Smith");
        order2.AddItem("Book");
        order2.Cancel();  // Cancel pending order
        
        var order3 = new OrderContext(1003, "Bob Johnson");
        order3.AddItem("Phone");
        order3.Process();
        order3.Cancel();  // Cancel processing order
        
        Console.WriteLine("\n\n=== State Pattern - Vending Machine ===\n");
        
        var machine = new VendingMachine();
        
        machine.SelectProduct("Soda");  // No money
        machine.InsertMoney(0.50m);
        machine.SelectProduct("Soda");  // Insufficient
        machine.InsertMoney(1.00m);
        machine.SelectProduct("Soda");  // Sufficient
        machine.Dispense();
        
        Console.WriteLine("\n--- Testing with change ---");
        machine.InsertMoney(2.00m);
        machine.SelectProduct("Chips");
        machine.Dispense();  // Should have change
    }
}

6. Template Method Pattern

6.1 Understanding Template Method

Definition: The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It lets subclasses redefine certain steps without changing the algorithm's structure.

Real-World Analogy: Making coffee and tea both follow the same algorithm: boil water → brew → pour → add condiments. The specific steps differ, but the overall structure is the same.

6.2 Template Method Implementation

// Abstract class with template method
public abstract class DataProcessor
{
    // Template method - defines the algorithm skeleton
    public void ProcessData()
    {
        Console.WriteLine($"\n=== Processing Data with {GetProcessorName()} ===");
        
        var data = LoadData();
        var validatedData = ValidateData(data);
        var transformedData = TransformData(validatedData);
        SaveData(transformedData);
        NotifyCompletion();
        
        Console.WriteLine("=== Processing Complete ===\n");
    }
    
    // Abstract methods - must be implemented by subclasses
    protected abstract string GetProcessorName();
    protected abstract string LoadData();
    protected abstract bool ValidateData(string data);
    protected abstract string TransformData(string data);
    protected abstract void SaveData(string data);
    
    // Hook method - optional override
    protected virtual void NotifyCompletion()
    {
        Console.WriteLine("Data processing completed successfully");
    }
    
    // Concrete method - common implementation
    protected void Log(string message)
    {
        Console.WriteLine($"[{DateTime.Now:T}] {message}");
    }
}

public class CsvDataProcessor : DataProcessor
{
    private string _filePath;
    
    public CsvDataProcessor(string filePath)
    {
        _filePath = filePath;
    }
    
    protected override string GetProcessorName() => "CSV Processor";
    
    protected override string LoadData()
    {
        Log($"Loading CSV data from {_filePath}");
        // Simulate loading CSV
        return "Name,Age,Email\nJohn,30,john@example.com\nJane,25,jane@example.com";
    }
    
    protected override bool ValidateData(string data)
    {
        Log("Validating CSV data format");
        // Validate CSV format
        var lines = data.Split('\n');
        if (lines.Length < 2)
        {
            Log("❌ CSV validation failed: No data rows");
            return false;
        }
        
        var headers = lines[0].Split(',');
        if (headers.Length != 3)
        {
            Log("❌ CSV validation failed: Invalid column count");
            return false;
        }
        
        Log("✅ CSV data validated successfully");
        return true;
    }
    
    protected override string TransformData(string data)
    {
        Log("Transforming CSV data to internal format");
        // Transform CSV to internal format
        var lines = data.Split('\n');
        var result = new StringBuilder();
        
        for (int i = 1; i < lines.Length; i++)
        {
            if (!string.IsNullOrWhiteSpace(lines[i]))
            {
                var parts = lines[i].Split(',');
                result.AppendLine($"User: {parts[0]}, Age: {parts[1]}, Email: {parts[2]}");
            }
        }
        
        return result.ToString();
    }
    
    protected override void SaveData(string data)
    {
        Log($"Saving processed CSV data to database");
        // Save to database
        Console.WriteLine($"Data saved: {data}");
    }
}

public class JsonDataProcessor : DataProcessor
{
    private string _jsonData;
    
    public JsonDataProcessor(string jsonData)
    {
        _jsonData = jsonData;
    }
    
    protected override string GetProcessorName() => "JSON Processor";
    
    protected override string LoadData()
    {
        Log("Loading JSON data");
        return _jsonData;
    }
    
    protected override bool ValidateData(string data)
    {
        Log("Validating JSON format");
        // Simple JSON validation
        if (!data.StartsWith("{") || !data.EndsWith("}"))
        {
            Log("❌ JSON validation failed: Invalid JSON format");
            return false;
        }
        
        Log("✅ JSON data validated successfully");
        return true;
    }
    
    protected override string TransformData(string data)
    {
        Log("Transforming JSON data to internal format");
        // Parse and transform JSON
        // Simplified transformation
        return data.Replace("{", "").Replace("}", "").Replace("\"", "");
    }
    
    protected override void SaveData(string data)
    {
        Log($"Saving processed JSON data to API");
        Console.WriteLine($"API Response: {data}");
    }
    
    protected override void NotifyCompletion()
    {
        base.NotifyCompletion();
        Console.WriteLine("📧 Sending completion notification to admin");
    }
}

// Another example - Report Generator
public abstract class ReportGenerator
{
    // Template method
    public void GenerateReport()
    {
        Console.WriteLine($"\n--- Generating {GetReportType()} Report ---");
        
        var data = FetchData();
        var processedData = ProcessData(data);
        var formattedReport = FormatReport(processedData);
        ExportReport(formattedReport);
        
        if (ShouldSendNotification())
        {
            SendNotification();
        }
    }
    
    protected abstract string GetReportType();
    protected abstract string FetchData();
    protected abstract string ProcessData(string data);
    protected abstract string FormatReport(string data);
    protected abstract void ExportReport(string report);
    
    // Hook method - optional override
    protected virtual bool ShouldSendNotification() => true;
    
    protected virtual void SendNotification()
    {
        Console.WriteLine($"📧 {GetReportType()} report generated and ready");
    }
    
    protected void Log(string message)
    {
        Console.WriteLine($"  [LOG] {message}");
    }
}

public class SalesReportGenerator : ReportGenerator
{
    private DateTime _startDate;
    private DateTime _endDate;
    
    public SalesReportGenerator(DateTime startDate, DateTime endDate)
    {
        _startDate = startDate;
        _endDate = endDate;
    }
    
    protected override string GetReportType() => "Sales";
    
    protected override string FetchData()
    {
        Log($"Fetching sales data from {_startDate:d} to {_endDate:d}");
        // Simulate database query
        return "SalesData: Total Revenue: $50,000, Orders: 250, Avg Order: $200";
    }
    
    protected override string ProcessData(string data)
    {
        Log("Calculating sales metrics and trends");
        // Process and aggregate data
        return $"{data}, Growth: 15%, Top Product: Laptop";
    }
    
    protected override string FormatReport(string data)
    {
        Log("Formatting sales report");
        return $@"
╔════════════════════════════════════════╗
║         SALES PERFORMANCE REPORT        ║
╠════════════════════════════════════════╣
║ Period: {_startDate:d} - {_endDate:d}                     ║
║ {data,-40} ║
╚════════════════════════════════════════╝";
    }
    
    protected override void ExportReport(string report)
    {
        Log("Exporting to PDF and Excel");
        Console.WriteLine(report);
        File.WriteAllText($"SalesReport_{DateTime.Now:yyyyMMdd}.txt", report);
        Console.WriteLine("✅ Sales report exported successfully");
    }
}

public class UserActivityReportGenerator : ReportGenerator
{
    private string _userId;
    
    public UserActivityReportGenerator(string userId)
    {
        _userId = userId;
    }
    
    protected override string GetReportType() => "User Activity";
    
    protected override string FetchData()
    {
        Log($"Fetching activity for user {_userId}");
        return $"User {_userId} activities: Logins: 45, Actions: 127, LastActive: {DateTime.Now:d}";
    }
    
    protected override string ProcessData(string data)
    {
        Log("Analyzing user behavior patterns");
        return $"{data}, Engagement Score: 85/100, Session Duration: 12min";
    }
    
    protected override string FormatReport(string data)
    {
        Log("Formatting user activity report");
        return $@"
┌────────────────────────────────────────┐
│         USER ACTIVITY REPORT           │
├────────────────────────────────────────┤
│ User ID: {_userId}                                    │
│ {data,-38} │
└────────────────────────────────────────┘";
    }
    
    protected override void ExportReport(string report)
    {
        Log("Sending to dashboard");
        Console.WriteLine(report);
        // Send to web dashboard
        Console.WriteLine("✅ Report pushed to user dashboard");
    }
    
    protected override bool ShouldSendNotification() => false;
}

// Usage
public class TemplateMethodDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Template Method Pattern - Data Processors ===\n");
        
        var csvProcessor = new CsvDataProcessor("data.csv");
        csvProcessor.ProcessData();
        
        var jsonProcessor = new JsonDataProcessor("{\"name\":\"John\",\"age\":30,\"email\":\"john@example.com\"}");
        jsonProcessor.ProcessData();
        
        Console.WriteLine("\n=== Template Method Pattern - Report Generators ===\n");
        
        var salesReport = new SalesReportGenerator(DateTime.Now.AddDays(-30), DateTime.Now);
        salesReport.GenerateReport();
        
        var userReport = new UserActivityReportGenerator("USER123");
        userReport.GenerateReport();
    }
}

7. Mediator Pattern

7.1 Understanding Mediator

Definition: The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly.

Real-World Analogy: Air traffic control tower (mediator). Pilots don't talk directly to each other; they communicate through the tower, which ensures safe landings and takeoffs.

7.2 Mediator Pattern Implementation

// Mediator interface
public interface IChatMediator
{
    void SendMessage(string message, User user);
    void AddUser(User user);
    void RemoveUser(User user);
}

// Colleague abstract class
public abstract class User
{
    protected IChatMediator _mediator;
    public string Name { get; }
    
    public User(string name, IChatMediator mediator)
    {
        Name = name;
        _mediator = mediator;
    }
    
    public abstract void Send(string message);
    public abstract void Receive(string message);
}

// Concrete mediator
public class ChatRoomMediator : IChatMediator
{
    private List<User> _users = new List<User>();
    
    public void AddUser(User user)
    {
        _users.Add(user);
        Console.WriteLine($"[System] {user.Name} joined the chat room");
        BroadcastMessage($"{user.Name} has joined the chat", user);
    }
    
    public void RemoveUser(User user)
    {
        _users.Remove(user);
        Console.WriteLine($"[System] {user.Name} left the chat room");
        BroadcastMessage($"{user.Name} has left the chat", user);
    }
    
    public void SendMessage(string message, User sender)
    {
        Console.WriteLine($"[{DateTime.Now:T}] {sender.Name} (to all): {message}");
        
        foreach (var user in _users)
        {
            if (user != sender)
            {
                user.Receive($"[{sender.Name}]: {message}");
            }
        }
    }
    
    private void BroadcastMessage(string message, User excludeUser = null)
    {
        foreach (var user in _users)
        {
            if (user != excludeUser)
            {
                user.Receive($"[System]: {message}");
            }
        }
    }
    
    public void SendPrivateMessage(string message, User sender, User recipient)
    {
        Console.WriteLine($"[{DateTime.Now:T}] {sender.Name} (to {recipient.Name}): {message}");
        recipient.Receive($"[Private from {sender.Name}]: {message}");
    }
}

// Concrete colleague
public class ChatUser : User
{
    public ChatUser(string name, IChatMediator mediator) : base(name, mediator) { }
    
    public override void Send(string message)
    {
        Console.WriteLine($"{Name} sending: {message}");
        _mediator.SendMessage(message, this);
    }
    
    public override void Receive(string message)
    {
        Console.WriteLine($"{Name} received: {message}");
    }
    
    public void SendPrivateMessage(string message, ChatUser recipient)
    {
        var chatMediator = _mediator as ChatRoomMediator;
        chatMediator?.SendPrivateMessage(message, this, recipient);
    }
}

// Another example - UI Component Mediator
public interface IUIDialogMediator
{
    void Notify(object sender, string eventName);
    void RegisterComponent(string name, UIComponent component);
}

public abstract class UIComponent
{
    protected IUIDialogMediator _mediator;
    public string Name { get; }
    
    protected UIComponent(string name, IUIDialogMediator mediator)
    {
        Name = name;
        _mediator = mediator;
    }
    
    public abstract void Update(string data);
    public abstract void Changed(string eventData);
}

public class TextBox : UIComponent
{
    public string Text { get; private set; }
    
    public TextBox(string name, IUIDialogMediator mediator) : base(name, mediator) { }
    
    public void SetText(string text)
    {
        Text = text;
        Console.WriteLine($"[TextBox {Name}] Text set to: {text}");
        Changed(text);
    }
    
    public override void Update(string data)
    {
        Text = data;
        Console.WriteLine($"[TextBox {Name}] Updated with: {data}");
    }
    
    public override void Changed(string eventData)
    {
        _mediator.Notify(this, "TextChanged");
    }
}

public class Button : UIComponent
{
    public bool IsEnabled { get; private set; } = true;
    
    public Button(string name, IUIDialogMediator mediator) : base(name, mediator) { }
    
    public void Click()
    {
        if (IsEnabled)
        {
            Console.WriteLine($"[Button {Name}] Clicked");
            _mediator.Notify(this, "Click");
        }
        else
        {
            Console.WriteLine($"[Button {Name}] Disabled - cannot click");
        }
    }
    
    public void SetEnabled(bool enabled)
    {
        IsEnabled = enabled;
        Console.WriteLine($"[Button {Name}] {(enabled ? "Enabled" : "Disabled")}");
    }
    
    public override void Update(string data) { }
    public override void Changed(string eventData) { }
}

public class ListBox : UIComponent
{
    public List<string> Items { get; } = new List<string>();
    public string SelectedItem { get; private set; }
    
    public ListBox(string name, IUIDialogMediator mediator) : base(name, mediator) { }
    
    public void AddItem(string item)
    {
        Items.Add(item);
        Console.WriteLine($"[ListBox {Name}] Added item: {item}");
    }
    
    public void SelectItem(string item)
    {
        if (Items.Contains(item))
        {
            SelectedItem = item;
            Console.WriteLine($"[ListBox {Name}] Selected: {item}");
            Changed(item);
        }
    }
    
    public override void Update(string data)
    {
        // Update based on data
    }
    
    public override void Changed(string eventData)
    {
        _mediator.Notify(this, "SelectionChanged");
    }
}

public class Label : UIComponent
{
    public string Text { get; private set; }
    
    public Label(string name, IUIDialogMediator mediator) : base(name, mediator) { }
    
    public void SetText(string text)
    {
        Text = text;
        Console.WriteLine($"[Label {Name}] Text set to: {text}");
    }
    
    public override void Update(string data)
    {
        SetText(data);
    }
    
    public override void Changed(string eventData) { }
}

// Concrete mediator for UI components
public class UserFormMediator : IUIDialogMediator
{
    private Dictionary<string, UIComponent> _components = new Dictionary<string, UIComponent>();
    
    public void RegisterComponent(string name, UIComponent component)
    {
        _components[name] = component;
        Console.WriteLine($"Registered component: {name}");
    }
    
    public void Notify(object sender, string eventName)
    {
        var senderName = (sender as UIComponent)?.Name;
        
        switch (eventName)
        {
            case "TextChanged":
                if (sender is TextBox textBox)
                {
                    // Validate username
                    if (textBox.Name == "Username")
                    {
                        var isValid = !string.IsNullOrWhiteSpace(textBox.Text) && textBox.Text.Length >= 3;
                        var submitButton = _components["Submit"] as Button;
                        submitButton?.SetEnabled(isValid);
                        
                        var statusLabel = _components["Status"] as Label;
                        statusLabel?.SetText(isValid ? "Username valid" : "Username must be at least 3 characters");
                    }
                    // Validate password confirmation
                    else if (textBox.Name == "PasswordConfirm")
                    {
                        var passwordBox = _components["Password"] as TextBox;
                        var confirmBox = textBox;
                        var match = passwordBox?.Text == confirmBox.Text;
                        
                        var submitButton = _components["Submit"] as Button;
                        if (submitButton != null && _components["Username"] is TextBox usernameBox)
                        {
                            var usernameValid = !string.IsNullOrWhiteSpace(usernameBox.Text) && usernameBox.Text.Length >= 3;
                            submitButton.SetEnabled(usernameValid && match);
                        }
                        
                        var statusLabel = _components["Status"] as Label;
                        statusLabel?.SetText(match ? "Passwords match" : "Passwords do not match");
                    }
                }
                break;
                
            case "SelectionChanged":
                if (sender is ListBox listBox && listBox.Name == "RoleList")
                {
                    var roleLabel = _components["SelectedRole"] as Label;
                    roleLabel?.SetText($"Selected role: {listBox.SelectedItem}");
                }
                break;
                
            case "Click":
                if (sender is Button button && button.Name == "Submit")
                {
                    var username = (_components["Username"] as TextBox)?.Text;
                    var password = (_components["Password"] as TextBox)?.Text;
                    var role = (_components["RoleList"] as ListBox)?.SelectedItem;
                    
                    Console.WriteLine($"\n[Form Submitted]");
                    Console.WriteLine($"  Username: {username}");
                    Console.WriteLine($"  Password: {new string('*', password?.Length ?? 0)}");
                    Console.WriteLine($"  Role: {role}");
                    Console.WriteLine($"  ✓ Form submitted successfully!\n");
                    
                    // Reset form
                    (_components["Username"] as TextBox)?.SetText("");
                    (_components["Password"] as TextBox)?.SetText("");
                    (_components["PasswordConfirm"] as TextBox)?.SetText("");
                    (_components["Status"] as Label)?.SetText("Form submitted!");
                }
                break;
        }
    }
}

// Usage
public class MediatorDemo
{
    public static void Run()
    {
        Console.WriteLine("=== Mediator Pattern - Chat Room ===\n");
        
        var chatRoom = new ChatRoomMediator();
        
        var alice = new ChatUser("Alice", chatRoom);
        var bob = new ChatUser("Bob", chatRoom);
        var charlie = new ChatUser("Charlie", chatRoom);
        var david = new ChatUser("David", chatRoom);
        
        chatRoom.AddUser(alice);
        chatRoom.AddUser(bob);
        chatRoom.AddUser(charlie);
        
        alice.Send("Hello everyone!");
        bob.Send("Hi Alice!");
        charlie.Send("Hey team!");
        
        Console.WriteLine("\n--- David joins ---");
        chatRoom.AddUser(david);
        david.Send("Good morning all!");
        
        Console.WriteLine("\n--- Private message ---");
        alice.SendPrivateMessage("Hey Bob, check your email", bob as ChatUser);
        
        Console.WriteLine("\n--- Bob leaves ---");
        chatRoom.RemoveUser(bob);
        alice.Send("Where did Bob go?");
        
        Console.WriteLine("\n\n=== Mediator Pattern - UI Form ===\n");
        
        var mediator = new UserFormMediator();
        
        // Create components
        var usernameBox = new TextBox("Username", mediator);
        var passwordBox = new TextBox("Password", mediator);
        var confirmBox = new TextBox("PasswordConfirm", mediator);
        var roleList = new ListBox("RoleList", mediator);
        var submitButton = new Button("Submit", mediator);
        var statusLabel = new Label("Status", mediator);
        var selectedRoleLabel = new Label("SelectedRole", mediator);
        
        // Register with mediator
        mediator.RegisterComponent("Username", usernameBox);
        mediator.RegisterComponent("Password", passwordBox);
        mediator.RegisterComponent("PasswordConfirm", confirmBox);
        mediator.RegisterComponent("RoleList", roleList);
        mediator.RegisterComponent("Submit", submitButton);
        mediator.RegisterComponent("Status", statusLabel);
        mediator.RegisterComponent("SelectedRole", selectedRoleLabel);
        
        // Add roles
        roleList.AddItem("Administrator");
        roleList.AddItem("Manager");
        roleList.AddItem("User");
        roleList.AddItem("Guest");
        
        // Simulate user interaction
        Console.WriteLine("\n--- User Interaction ---");
        usernameBox.SetText("jo");
        usernameBox.SetText("john");
        passwordBox.SetText("secret123");
        confirmBox.SetText("secret123");
        roleList.SelectItem("Manager");
        submitButton.Click();
        
        // Test validation
        Console.WriteLine("\n--- Testing Validation ---");
        usernameBox.SetText("a");
        usernameBox.SetText("ab");
        usernameBox.SetText("abc");
        confirmBox.SetText("wrong");
    }
}

8. Choosing the Right Behavioral Pattern

Pattern When to Use Key Benefit .NET Use Cases
Strategy Multiple algorithms for the same task Runtime algorithm selection Sorting, filtering, payment processing, validation
Observer One-to-many notifications Loose coupling, event-driven architecture Events, delegates, MVVM, SignalR
Chain of Responsibility Multiple handlers for a request Decouples sender from receiver Middleware pipeline, logging, exception handling
Command Need to parameterize, queue, or undo operations Encapsulates requests, supports undo/redo Transaction management, macros, task queues
State Object behavior changes based on state Eliminates large conditional statements Workflow engines, order processing, game development
Template Method Common algorithm with varying steps Code reuse, enforces algorithm structure Data processors, report generators, base classes
Mediator Complex communication between objects Reduces coupling, centralizes control UI frameworks, chat systems, workflow orchestration

9. Summary and Key Takeaways

  • Strategy: Encapsulates algorithms. Use when you have multiple ways to perform an operation.
  • Observer: Defines one-to-many dependencies. Use for event-driven systems and notifications.
  • Chain of Responsibility: Passes requests along a chain. Use for processing pipelines and escalation workflows.
  • Command: Encapsulates requests as objects. Use for undo/redo, transaction logging, and task queues.
  • State: Changes behavior based on state. Use for workflow engines and state machines.
  • Template Method: Defines algorithm skeleton. Use when multiple classes share the same algorithm structure.
  • Mediator: Centralizes communication. Use for complex interactions between multiple objects.

🎯 Design Tips:

  • Start with Strategy when you have if-else chains for different behaviors
  • Use Observer for event-driven architectures and loose coupling
  • Apply Chain of Responsibility for request processing pipelines
  • Consider Command when you need to support undo/redo operations
  • Use State for objects with complex state-dependent behavior
  • Apply Template Method to enforce algorithm structure across subclasses
  • Use Mediator when you have many objects that need to communicate in complex ways

10. Practice Exercises

Exercise 1: Implement a Sorting Strategy

Create a sorting system that supports multiple sorting algorithms (Bubble Sort, Quick Sort, Merge Sort) using Strategy pattern.

Click to see solution
public interface ISortStrategy
{
    void Sort(int[] array);
    string StrategyName { get; }
}

public class BubbleSortStrategy : ISortStrategy
{
    public string StrategyName => "Bubble Sort";
    
    public void Sort(int[] array)
    {
        for (int i = 0; i < array.Length - 1; i++)
        {
            for (int j = 0; j < array.Length - i - 1; j++)
            {
                if (array[j] > array[j + 1])
                {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

public class QuickSortStrategy : ISortStrategy
{
    public string StrategyName => "Quick Sort";
    
    public void Sort(int[] array)
    {
        QuickSort(array, 0, array.Length - 1);
    }
    
    private void QuickSort(int[] array, int left, int right)
    {
        if (left < right)
        {
            int pivot = Partition(array, left, right);
            QuickSort(array, left, pivot - 1);
            QuickSort(array, pivot + 1, right);
        }
    }
    
    private int Partition(int[] array, int left, int right)
    {
        int pivot = array[right];
        int i = left - 1;
        
        for (int j = left; j < right; j++)
        {
            if (array[j] <= pivot)
            {
                i++;
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        
        int temp2 = array[i + 1];
        array[i + 1] = array[right];
        array[right] = temp2;
        
        return i + 1;
    }
}

public class SortContext
{
    private ISortStrategy _strategy;
    
    public void SetStrategy(ISortStrategy strategy)
    {
        _strategy = strategy;
        Console.WriteLine($"Using {_strategy.StrategyName}");
    }
    
    public void SortArray(int[] array)
    {
        if (_strategy == null)
            throw new InvalidOperationException("Strategy not set");
            
        _strategy.Sort(array);
        Console.WriteLine($"Sorted array: {string.Join(", ", array)}");
    }
}

Exercise 2: Create a Notification System with Multiple Observers

Implement a notification system where users can subscribe to different types of notifications.

Click to see solution
public interface INotificationObserver
{
    void Update(Notification notification);
    string ObserverId { get; }
}

public class Notification
{
    public string Type { get; set; }
    public string Title { get; set; }
    public string Message { get; set; }
    public DateTime Timestamp { get; set; }
}

public class EmailNotificationObserver : INotificationObserver
{
    private string _email;
    
    public EmailNotificationObserver(string email)
    {
        _email = email;
    }
    
    public string ObserverId => _email;
    
    public void Update(Notification notification)
    {
        Console.WriteLine($"📧 Email to {_email}: [{notification.Type}] {notification.Title} - {notification.Message}");
    }
}

public class SmsNotificationObserver : INotificationObserver
{
    private string _phone;
    
    public SmsNotificationObserver(string phone)
    {
        _phone = phone;
    }
    
    public string ObserverId => _phone;
    
    public void Update(Notification notification)
    {
        Console.WriteLine($"📱 SMS to {_phone}: {notification.Title}");
    }
}

public class NotificationService
{
    private Dictionary<string, List<INotificationObserver>> _observers = new();
    
    public void Subscribe(string notificationType, INotificationObserver observer)
    {
        if (!_observers.ContainsKey(notificationType))
            _observers[notificationType] = new List<INotificationObserver>();
            
        _observers[notificationType].Add(observer);
        Console.WriteLine($"Subscribed {observer.ObserverId} to {notificationType} notifications");
    }
    
    public void Unsubscribe(string notificationType, INotificationObserver observer)
    {
        if (_observers.ContainsKey(notificationType))
        {
            _observers[notificationType].Remove(observer);
            Console.WriteLine($"Unsubscribed {observer.ObserverId} from {notificationType}");
        }
    }
    
    public void SendNotification(Notification notification)
    {
        Console.WriteLine($"\n--- Sending {notification.Type} Notification ---");
        
        if (_observers.ContainsKey(notification.Type))
        {
            foreach (var observer in _observers[notification.Type])
            {
                observer.Update(notification);
            }
        }
        
        if (_observers.ContainsKey("All"))
        {
            foreach (var observer in _observers["All"])
            {
                observer.Update(notification);
            }
        }
    }
}

11. What's Next?

In Chapter 6, we'll explore .NET Specific Architecture - covering Dependency Injection, Async/Await best practices, Unit Testing, and real-world .NET patterns that are essential for professional development.

Chapter 6 Preview:

  • Dependency Injection - IoC containers, service lifetimes, DI best practices
  • Async/Await Patterns - Async best practices, cancellation tokens, error handling
  • Unit Testing - xUnit, Moq, Test patterns, mocking dependencies
  • .NET Core Middleware - Pipeline architecture, custom middleware
  • Entity Framework Patterns - Repository, Unit of Work, Specification

📝 Practice Assignments:

  1. Implement a payment processing system with multiple strategies (Credit Card, PayPal, Crypto)
  2. Create a stock market monitoring system using Observer pattern with multiple subscribers
  3. Build a customer support ticket escalation system using Chain of Responsibility
  4. Implement a task scheduler with undo/redo functionality using Command pattern
  5. Create a document workflow system with different states (Draft, Review, Approved, Published)
  6. Build a data export system using Template Method for CSV, JSON, XML formats
  7. Implement a UI form with complex validation using Mediator pattern

Happy Coding! 🚀

Post a Comment

Feel free to ask your query...
Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.