📄

Oop Solid Patterns Comprehensive

Intermediate 2 min read 200 words

OOP, SOLID Principles, and Design Patterns - Comprehensive Senior Guide

Table of Contents

  1. Object-Oriented Programming - Deep Dive
  2. SOLID Principles - Detailed Implementation
  3. Creational Design Patterns
  4. Structural Design Patterns
  5. Behavioral Design Patterns
  6. Advanced OOP Concepts
  7. Pattern Selection Guide

Object-Oriented Programming - Deep Dive

1. Four Pillars of OOP

A. Abstraction

Abstraction è il processo di nascondere la complessità e esporre solo l’interfaccia essenziale.

// ❌ WRONG - Over-complicated implementation exposed
public class PaymentProcessor
{
    public void ProcessCreditCard(string cardNumber, string cvv, string expiry,
        decimal amount, string name, string email, string address, 
        int zipCode, string country, bool is3DSecure, string bankCode)
    {
        // 100 lines of implementation details
        ValidateBankConnection();
        EncryptSensitiveData();
        CheckFraudScores();
        // ... lots of complexity
    }
}

// ✅ CORRECT - Implementation hidden, clean interface
public interface IPaymentProcessor
{
    Task<PaymentResult> ProcessAsync(PaymentDetails details);
}

public class PaymentDetails
{
    public string CardNumber { get; set; }
    public string CVV { get; set; }
    public string Expiry { get; set; }
    public decimal Amount { get; set; }
    // ... other properties encapsulated
}

public class CreditCardProcessor : IPaymentProcessor
{
    private readonly ICardValidator _cardValidator;
    private readonly IFraudDetectionService _fraudDetection;
    private readonly IEncryptionService _encryption;

    public async Task<PaymentResult> ProcessAsync(PaymentDetails details)
    {
        // Clean, abstracted interface
        // Implementation details hidden
        await _cardValidator.ValidateAsync(details);
        await _fraudDetection.CheckAsync(details);
        var encrypted = _encryption.Encrypt(details);

        return new PaymentResult { Success = true };
    }
}

// Advanced Abstraction - Abstract Classes
public abstract class BaseRepository<T> where T : class
{
    protected DbContext Context { get; set; }

    public abstract Task<T> GetByIdAsync(int id);
    public abstract Task<List<T>> GetAllAsync();
    public abstract Task AddAsync(T entity);
    public abstract Task UpdateAsync(T entity);
    public abstract Task DeleteAsync(int id);

    // Template method pattern - abstraction of common behavior
    protected virtual void ValidateEntity(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException(nameof(entity));
    }

    protected virtual async Task LogOperationAsync(string operation, T entity)
    {
        // Default implementation - can be overridden
        await Task.CompletedTask;
    }
}

// Abstraction Levels
/*
Level 1: Concrete Implementation
----- (Low-level details)
  public void PaymentProcess() { /* 100 lines */ }

Level 2: Public Method
-----
  public async Task<PaymentResult> ProcessAsync(PaymentDetails details)

Level 3: Interface
-----
  public interface IPaymentProcessor
  {
      Task<PaymentResult> ProcessAsync(PaymentDetails details);
  }

Level 4: Service Layer
-----
  public class OrderService
  {
      private readonly IPaymentProcessor _processor;
      public async Task CompleteOrderAsync(Order order)
      {
          var result = await _processor.ProcessAsync(order.PaymentDetails);
      }
  }

Level 5: API Layer
-----
  [HttpPost("complete")]
  public async Task<IActionResult> CompleteOrder([FromBody] OrderRequest request)
  {
      await _orderService.CompleteOrderAsync(request);
  }

Business stakeholders - não veem nenhum detalhe
*/

B. Encapsulation

Encapsulation protege estado interno e força acesso através de interfaces.

// ❌ BAD - Public fields, no protection
public class BankAccount
{
    public decimal Balance;
    public string AccountNumber;

    public void Withdraw(decimal amount)
    {
        Balance -= amount; // Sem validação!
    }
}

// ❌ WORSE - Alguém pode fazer isto:
var account = new BankAccount();
account.Balance = -1000000; // Set negative balance directly!

// ✅ CORRECT - Encapsulated with validation
public class BankAccount
{
    private decimal _balance;
    private readonly object _lockObject = new();
    private readonly List<Transaction> _transactions = new();

    public decimal Balance 
    { 
        get { return _balance; }
        private set { _balance = value; }
    }

    public string AccountNumber { get; private set; }

    public BankAccount(string accountNumber, decimal initialBalance)
    {
        if (string.IsNullOrEmpty(accountNumber))
            throw new ArgumentException("Account number required");

        if (initialBalance < 0)
            throw new ArgumentException("Initial balance cannot be negative");

        AccountNumber = accountNumber;
        _balance = initialBalance;
    }

    public bool Withdraw(decimal amount)
    {
        lock (_lockObject)
        {
            if (amount <= 0)
                throw new ArgumentException("Withdrawal amount must be positive");

            if (amount > _balance)
                return false; // Insufficient funds

            _balance -= amount;
            _transactions.Add(new Transaction 
            { 
                Type = "Withdrawal", 
                Amount = amount, 
                Date = DateTime.UtcNow 
            });

            return true;
        }
    }

    public void Deposit(decimal amount)
    {
        lock (_lockObject)
        {
            if (amount <= 0)
                throw new ArgumentException("Deposit amount must be positive");

            _balance += amount;
            _transactions.Add(new Transaction 
            { 
                Type = "Deposit", 
                Amount = amount, 
                Date = DateTime.UtcNow 
            });
        }
    }

    public IReadOnlyList<Transaction> GetTransactions()
    {
        return _transactions.AsReadOnly(); // Retorna cópia immutable
    }
}

// Encapsulation Benefits:
// 1. ✓ Validação de entrada
// 2. ✓ Proteção de estado interno
// 3. ✓ Thread safety (lock)
// 4. ✓ Auditoria (transactions log)
// 5. ✓ Flexibilidade de implementação (pode mudar internamente)

C. Inheritance

Inheritance cria relacionamento “is-a” entre classes.

// Hierarchy - Base class
public abstract class Animal
{
    public string Name { get; set; }

    public virtual void Speak()
    {
        Console.WriteLine($"{Name} makes a sound");
    }

    public abstract void Move();
}

// Single Inheritance
public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} barks: Woof!");
    }

    public override void Move()
    {
        Console.WriteLine($"{Name} runs on four legs");
    }
}

// Multi-level Inheritance
public class Terrier : Dog
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} yaps: Yip yip!");
    }
}

// ❌ ANTIPATTERN - Deep hierarchies (more than 3 levels)
public class BasePersistence { }
public class DataAccessLayer : BasePersistence { }
public class RepositoryBase : DataAccessLayer { }
public class ProductRepository : RepositoryBase { }
public class CachedProductRepository : ProductRepository { }
// Too deep! Hard to maintain

// ✅ BETTER - Use composition instead
public class CachedProductRepository
{
    private readonly IProductRepository _repository;
    private readonly IDistributedCache _cache;

    public async Task<Product> GetByIdAsync(int id)
    {
        var cached = await _cache.GetAsync($"product_{id}");
        if (cached != null)
            return cached;

        var product = await _repository.GetByIdAsync(id);
        await _cache.SetAsync($"product_{id}", product);
        return product;
    }
}

// Inheritance Rules for Senior Developers:
// 1. Use inheritance para "is-a" relationships ONLY
// 2. Never inherit just to reuse code (use composition instead)
// 3. Keep hierarchies flat (max 3 levels)
// 4. Prefer interfaces over abstract classes for contracts
// 5. Violate LSP (Liskov) e você criou um bug

D. Polymorphism

Polymorphism permite múltiplas formas de mesma interface.

// 1. COMPILE-TIME POLYMORPHISM (Method Overloading)
public class Calculator
{
    // Mesma método name, diferentes assinaturas
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
    public string Add(string a, string b) => a + b; // String concatenation
    public int Add(int a, int b, int c) => a + b + c;
    public List<int> Add(List<int> numbers) => numbers;

    // Compiler resuelve qual método chamar em compile-time
}

// 2. RUNTIME POLYMORPHISM (Method Overriding)
public abstract class Shape
{
    public abstract double GetArea();
    public abstract void Draw();
}

public class Circle : Shape
{
    public double Radius { get; set; }

    public override double GetArea() => Math.PI * Radius * Radius;
    public override void Draw() => Console.WriteLine("Drawing circle");
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public override double GetArea() => Width * Height;
    public override void Draw() => Console.WriteLine("Drawing rectangle");
}

// Runtime - which shape method gets called é determinado em runtime
public class ShapeProcessor
{
    public void ProcessShapes(List<Shape> shapes)
    {
        foreach (var shape in shapes)
        {
            double area = shape.GetArea(); // Polymorphic call
            shape.Draw();
        }
    }
}

// 3. INTERFACE POLYMORPHISM
public interface IPaymentMethod
{
    Task<bool> ProcessAsync(decimal amount);
    Task RefundAsync(decimal amount);
}

public class CreditCard : IPaymentMethod
{
    public async Task<bool> ProcessAsync(decimal amount) => true;
    public async Task RefundAsync(decimal amount) => Console.WriteLine("Refunding credit card");
}

public class PayPal : IPaymentMethod
{
    public async Task<bool> ProcessAsync(decimal amount) => true;
    public async Task RefundAsync(decimal amount) => Console.WriteLine("Refunding PayPal");
}

public class Cryptocurrency : IPaymentMethod
{
    public async Task<bool> ProcessAsync(decimal amount) => true;
    public async Task RefundAsync(decimal amount) => Console.WriteLine("Refunding crypto wallet");
}

public class PaymentGateway
{
    private readonly List<IPaymentMethod> _methods = new();

    public async Task ProcessPaymentAsync(IPaymentMethod method, decimal amount)
    {
        // Polimorfismo - mesma interface, múltiplas implementações
        bool success = await method.ProcessAsync(amount);
        if (!success)
            await method.RefundAsync(amount);
    }
}

// 4. GENERIC POLYMORPHISM
public class GenericRepository<T> where T : class
{
    private DbContext _context;

    public async Task<T> GetByIdAsync(int id)
    {
        return await _context.Set<T>().FindAsync(id);
    }

    public async Task<List<T>> GetAllAsync()
    {
        return await _context.Set<T>().ToListAsync();
    }
}

// Runtime - mesma repositório class, diferentes tipos
var userRepo = new GenericRepository<User>();
var productRepo = new GenericRepository<Product>();

// Polymorphism Benefits:
// ✓ Flexibility - easy to add new types
// ✓ Loose coupling - depend on abstractions
// ✓ Maintainability - changes in one place
// ✓ Extensibility - new implementations without changing caller

SOLID Principles - Detailed Implementation

S - Single Responsibility Principle

// ❌ VIOLATES SRP - Uma classe com múltiplas responsabilidades
public class UserManager
{
    // Responsabilidade 1: User management
    public void RegisterUser(string email, string password) { }
    public void LoginUser(string email, string password) { }
    public void UpdateUserProfile(int userId, string name) { }

    // Responsabilidade 2: Email handling
    public void SendWelcomeEmail(string email) { }
    public void SendPasswordResetEmail(string email) { }

    // Responsabilidade 3: Database operations
    public void SaveToDatabase(User user) { }
    public void DeleteFromDatabase(int userId) { }

    // Responsabilidade 4: Validation
    public bool ValidateEmail(string email) { }
    public bool ValidatePassword(string password) { }

    // Responsabilidade 5: Logging
    public void LogUserActivity(int userId, string activity) { }

    // Se precisa mudar: Email service, Database, Validation, Logging
    // CADA mudança afeta esta classe
}

// ✅ FOLLOWS SRP - Separação de responsabilidades
public interface IUserRepository
{
    Task<User> GetByEmailAsync(string email);
    Task AddAsync(User user);
    Task UpdateAsync(User user);
    Task DeleteAsync(int userId);
}

public interface IEmailService
{
    Task SendWelcomeEmailAsync(string email);
    Task SendPasswordResetEmailAsync(string email);
}

public interface IUserValidator
{
    ValidationResult ValidateEmail(string email);
    ValidationResult ValidatePassword(string password);
}

public interface ILogger
{
    void LogUserActivity(int userId, string activity);
}

public class UserRegistrationService
{
    private readonly IUserValidator _validator;
    private readonly IUserRepository _repository;
    private readonly IEmailService _emailService;
    private readonly ILogger _logger;

    public async Task RegisterUserAsync(string email, string password)
    {
        // Validate
        var emailValidation = _validator.ValidateEmail(email);
        if (!emailValidation.IsValid)
            throw new ValidationException(emailValidation.Errors);

        var passwordValidation = _validator.ValidatePassword(password);
        if (!passwordValidation.IsValid)
            throw new ValidationException(passwordValidation.Errors);

        // Create user
        var user = new User { Email = email, Password = password };

        // Save
        await _repository.AddAsync(user);

        // Send email
        await _emailService.SendWelcomeEmailAsync(email);

        // Log
        _logger.LogUserActivity(user.Id, "User registered");
    }
}

public class UserAuthenticationService
{
    private readonly IUserRepository _repository;
    private readonly ILogger _logger;

    public async Task<User> LoginAsync(string email, string password)
    {
        var user = await _repository.GetByEmailAsync(email);

        if (user == null || !VerifyPassword(user.Password, password))
        {
            _logger.LogUserActivity(0, $"Failed login attempt for {email}");
            throw new UnauthorizedException();
        }

        _logger.LogUserActivity(user.Id, "User logged in");
        return user;
    }

    private bool VerifyPassword(string hashedPassword, string inputPassword)
    {
        return BCrypt.Net.BCrypt.Verify(inputPassword, hashedPassword);
    }
}

// Benefícios:
// ✓ Cada classe tem uma razão para mudar
// ✓ Fácil testar em isolamento
// ✓ Reutilizar componentes
// ✓ Manutenção simplificada

O - Open/Closed Principle

// ❌ VIOLATES OCP - Precisa modificar classe existente para adicionar novo tipo
public class NotificationService
{
    public void SendNotification(string type, string message, string recipient)
    {
        if (type == "email")
            SendEmail(recipient, message);
        else if (type == "sms")
            SendSMS(recipient, message);
        else if (type == "push")
            SendPushNotification(recipient, message);
        else if (type == "telegram")
            SendTelegram(recipient, message); // ← NOVA mudança REQUER mudança na classe
        else if (type == "slack")
            SendSlack(recipient, message); // ← OUTRA mudança
        // Não escala bem!
    }
}

// ✅ FOLLOWS OCP - Aberta para extensão, fechada para modificação
public interface INotificationChannel
{
    Task SendAsync(string recipient, string message);
    bool Supports(string channel);
}

public class EmailChannel : INotificationChannel
{
    public bool Supports(string channel) => channel == "email";
    public async Task SendAsync(string recipient, string message)
    {
        // Email implementation
        await Task.CompletedTask;
    }
}

public class SMSChannel : INotificationChannel
{
    public bool Supports(string channel) => channel == "sms";
    public async Task SendAsync(string recipient, string message)
    {
        // SMS implementation
        await Task.CompletedTask;
    }
}

public class PushNotificationChannel : INotificationChannel
{
    public bool Supports(string channel) => channel == "push";
    public async Task SendAsync(string recipient, string message)
    {
        // Push implementation
        await Task.CompletedTask;
    }
}

// NOVA FUNCIONALIDADE - Sem modificar classe existente!
public class TelegramChannel : INotificationChannel
{
    public bool Supports(string channel) => channel == "telegram";
    public async Task SendAsync(string recipient, string message)
    {
        // Telegram implementation
        await Task.CompletedTask;
    }
}

public class NotificationService
{
    private readonly List<INotificationChannel> _channels;

    public NotificationService(IEnumerable<INotificationChannel> channels)
    {
        _channels = channels.ToList();
    }

    public async Task SendAsync(string type, string message, string recipient)
    {
        var channel = _channels.FirstOrDefault(c => c.Supports(type));
        if (channel == null)
            throw new InvalidOperationException($"Channel {type} not supported");

        await channel.SendAsync(recipient, message);
    }
}

// Registro DI
public static void ConfigureNotifications(IServiceCollection services)
{
    services.AddScoped<INotificationChannel, EmailChannel>();
    services.AddScoped<INotificationChannel, SMSChannel>();
    services.AddScoped<INotificationChannel, PushNotificationChannel>();
    services.AddScoped<INotificationChannel, TelegramChannel>(); // ← Só adiciona, sem modificar nada
    services.AddScoped<NotificationService>();
}

// Benefícios OCP:
// ✓ Estável para modificação
// ✓ Extensível sem quebrar código existente
// ✓ Fácil testar novos canais em isolamento
// ✓ Suporta plugin architecture

L - Liskov Substitution Principle

// ❌ VIOLATES LSP - Subclass não pode substituir parent
public class Bird
{
    public virtual void Fly()
    {
        Console.WriteLine("Flying");
    }

    public virtual int GetAltitude()
    {
        return 5000;
    }
}

public class Penguin : Bird
{
    public override void Fly()
    {
        throw new NotImplementedException("Penguins can't fly");
    }

    public override int GetAltitude()
    {
        return 0; // Penguins don't fly
    }
}

// Problema: Se código espera Bird, não pode passar Penguin
public void ReleaseBird(Bird bird)
{
    bird.Fly(); // Crashes se bird é Penguin!
}

// ✅ FOLLOWS LSP - Proper inheritance hierarchy
public interface IFlyer
{
    void Fly();
    int GetAltitude();
}

public interface ISwimmer
{
    void Swim();
}

public abstract class Bird
{
    public abstract void Move();
}

public class Eagle : Bird, IFlyer
{
    public void Fly() => Console.WriteLine("Eagle flying high");
    public int GetAltitude() => 8000;
    public override void Move() => Fly();
}

public class Duck : Bird, IFlyer, ISwimmer
{
    public void Fly() => Console.WriteLine("Duck flying");
    public int GetAltitude() => 2000;
    public void Swim() => Console.WriteLine("Duck swimming");
    public override void Move() => Swim();
}

public class Penguin : Bird, ISwimmer
{
    public void Swim() => Console.WriteLine("Penguin swimming");
    public override void Move() => Swim();
}

// Agora safe:
public void ReleaseFlyingBird(IFlyer bird)
{
    bird.Fly(); // Safe - só recebe things que podem voar
}

public void ReleaseSwimmingBird(ISwimmer bird)
{
    bird.Swim(); // Safe - só recebe things que podem nadar
}

// Liskov Substitution Checklist:
// ✓ Subclass nunca deve ser mais restrictiva que parent
// ✓ Subclass nunca deve requirir mais que parent
// ✓ Subclass nunca deve lançar exceções que parent não lançaria
// ✓ Subclass deve poder substituir parent em qualquer lugar

I - Interface Segregation Principle

// ❌ VIOLATES ISP - Fat interface força implementações desnecessárias
public interface IWorker
{
    void Work();
    void TakeBreak();
    void Eat();
    void Sleep();
    void Manage();
    void Code();
    void Design();
}

public class Developer : IWorker
{
    public void Work() => Console.WriteLine("Coding");
    public void TakeBreak() => Console.WriteLine("Break");
    public void Eat() => Console.WriteLine("Eating");
    public void Sleep() => Console.WriteLine("Sleeping");

    // Developer não gerencia
    public void Manage() => throw new NotImplementedException();

    // Developer pode codificar
    public void Code() => Console.WriteLine("Writing code");

    // Nem sempre desenvolvedor design
    public void Design() => throw new NotImplementedException();
}

// ✅ FOLLOWS ISP - Segregated, focused interfaces
public interface IWorker
{
    void Work();
}

public interface IHuman
{
    void Eat();
    void Sleep();
    void TakeBreak();
}

public interface IManager
{
    void Manage();
}

public interface IDeveloper
{
    void Code();
}

public interface IDesigner
{
    void Design();
}

public class Developer : IWorker, IHuman, IDeveloper
{
    public void Work() => Console.WriteLine("Working");
    public void Code() => Console.WriteLine("Coding");
    public void Eat() => Console.WriteLine("Eating");
    public void Sleep() => Console.WriteLine("Sleeping");
    public void TakeBreak() => Console.WriteLine("Break");
}

public class Manager : IWorker, IHuman, IManager
{
    public void Work() => Console.WriteLine("Managing");
    public void Manage() => Console.WriteLine("Managing team");
    public void Eat() => Console.WriteLine("Eating");
    public void Sleep() => Console.WriteLine("Sleeping");
    public void TakeBreak() => Console.WriteLine("Break");
}

public class TeamLead : IWorker, IHuman, IDeveloper, IManager
{
    public void Work() => Console.WriteLine("Working");
    public void Code() => Console.WriteLine("Coding");
    public void Manage() => Console.WriteLine("Managing");
    public void Eat() => Console.WriteLine("Eating");
    public void Sleep() => Console.WriteLine("Sleeping");
    public void TakeBreak() => Console.WriteLine("Break");
}

// ISP Guidelines:
// ✓ Interfaces devem ser específicas, não genéricas
// ✓ Muitas interfaces pequenas > poucas interfaces grandes
// ✓ Clientes não devem ser forçadas implementar métodos não usados
// ✓ Interface segregation aumenta reusabilidade

D - Dependency Inversion Principle

// ❌ VIOLATES DIP - High-level depends on low-level (tight coupling)
public class EmailSender
{
    public void SendEmail(string to, string message)
    {
        // Direct SMTP implementation
        using (var client = new SmtpClient("smtp.gmail.com"))
        {
            client.Send("from@email.com", to, "Subject", message);
        }
    }
}

public class NotificationService
{
    private EmailSender _emailSender = new EmailSender(); // Direct dependency

    public void NotifyUser(string email, string message)
    {
        _emailSender.SendEmail(email, message);
    }
}

// Problem: Se mudar SMTP provider, precisa mudar NotificationService

// ✅ FOLLOWS DIP - High-level depends on abstraction
public interface IEmailSender
{
    Task SendAsync(string to, string subject, string message);
}

public class GmailSender : IEmailSender
{
    public async Task SendAsync(string to, string subject, string message)
    {
        using (var client = new SmtpClient("smtp.gmail.com"))
        {
            await client.SendMailAsync("from@email.com", to, subject, message);
        }
    }
}

public class SendGridSender : IEmailSender
{
    private readonly SendGridClient _client;

    public async Task SendAsync(string to, string subject, string message)
    {
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("from@email.com"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message,
        };
        msg.AddTo(new EmailAddress(to));

        await _client.SendEmailAsync(msg);
    }
}

public class NotificationService
{
    private readonly IEmailSender _emailSender;

    // Dependency injected
    public NotificationService(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public async Task NotifyUserAsync(string email, string message)
    {
        await _emailSender.SendAsync(email, "Notification", message);
    }
}

// Dependency injection container
var services = new ServiceCollection();
services.AddScoped<IEmailSender, SendGridSender>(); // Swap implementation anytime
services.AddScoped<NotificationService>();

// DIP Benefits:
// ✓ Loose coupling - easy to swap implementations
// ✓ Testable - mock IEmailSender for tests
// ✓ Flexible - change providers without modifying NotificationService
// ✓ Follows Dependency Inversion - high-level doesn't depend on low-level

Creational Design Patterns

1. Singleton Pattern (Thread-Safe)

// ✅ RECOMMENDED - Lazy<T> implementation (simple and safe)
public class DatabaseConnection
{
    private static readonly Lazy<DatabaseConnection> _instance =
        new Lazy<DatabaseConnection>(() => new DatabaseConnection());

    public static DatabaseConnection Instance => _instance.Value;

    private DatabaseConnection()
    {
        Console.WriteLine("Initializing database connection");
        // Expensive initialization
    }

    public void Connect() => Console.WriteLine("Connected to database");
}

// ✅ ALTERNATIVE - Static constructor (also thread-safe)
public class Logger
{
    private static readonly Logger _instance = new Logger();

    public static Logger Instance => _instance;

    private Logger()
    {
        Console.WriteLine("Logger initialized");
    }

    public void Log(string message) => Console.WriteLine($"[LOG] {message}");
}

// ✅ MODERN - Static class (when no initialization needed)
public static class ApplicationSettings
{
    public const string AppName = "MyApp";
    public const string Version = "1.0.0";

    public static string GetSetting(string key) => "";
}

// Usage:
var db = DatabaseConnection.Instance;
db.Connect();

var logger = Logger.Instance;
logger.Log("Application started");

// Singleton Antipatterns:
// ❌ Static variables (not thread-safe)
// ❌ Double-checked locking (complex, often wrong)
// ❌ Singletons everywhere (hidden dependencies)

// When to use Singleton:
// ✓ Database connections (connection pooling usually better)
// ✓ Logger (if centralized)
// ✓ Configuration (if read-only)
// ✓ Rarely - prefer dependency injection

2. Factory Pattern

// ✅ Simple Factory
public interface ILogger { }

public class ConsoleLogger : ILogger { }
public class FileLogger : ILogger { }
public class CloudLogger : ILogger { }

public class LoggerFactory
{
    public static ILogger CreateLogger(string type) =>
        type.ToLower() switch
        {
            "console" => new ConsoleLogger(),
            "file" => new FileLogger(),
            "cloud" => new CloudLogger(),
            _ => throw new ArgumentException($"Unknown logger type: {type}")
        };
}

// ✅ Abstract Factory - Creating families of objects
public interface IUIFactory
{
    IButton CreateButton();
    ITextBox CreateTextBox();
}

public interface IButton { void Click(); }
public interface ITextBox { void Input(string text); }

// Windows family
public class WindowsButton : IButton
{
    public void Click() => Console.WriteLine("Windows button clicked");
}

public class WindowsTextBox : ITextBox
{
    public void Input(string text) => Console.WriteLine($"Windows text input: {text}");
}

public class WindowsUIFactory : IUIFactory
{
    public IButton CreateButton() => new WindowsButton();
    public ITextBox CreateTextBox() => new WindowsTextBox();
}

// Mac family
public class MacButton : IButton
{
    public void Click() => Console.WriteLine("Mac button clicked");
}

public class MacTextBox : ITextBox
{
    public void Input(string text) => Console.WriteLine($"Mac text input: {text}");
}

public class MacUIFactory : IUIFactory
{
    public IButton CreateButton() => new MacButton();
    public ITextBox CreateTextBox() => new MacTextBox();
}

// Client code - doesn't know which family
public class Application
{
    private readonly IButton _button;
    private readonly ITextBox _textBox;

    public Application(IUIFactory factory)
    {
        _button = factory.CreateButton();
        _textBox = factory.CreateTextBox();
    }

    public void Run()
    {
        _button.Click();
        _textBox.Input("Hello");
    }
}

// Usage:
IUIFactory factory = GetUIFactory(); // Returns Windows or Mac factory
var app = new Application(factory);
app.Run();

// When to use Factory:
// ✓ Object creation is complex
// ✓ Different implementations based on runtime conditions
// ✓ Want to decouple creation from usage
// ✓ Multiple related objects (Abstract Factory)

3. Builder Pattern

// ✅ String Query Builder
public class SqlQueryBuilder
{
    private string _select = "";
    private string _from = "";
    private string _where = "";
    private string _orderBy = "";
    private string _limit = "";

    public SqlQueryBuilder Select(string columns)
    {
        _select = $"SELECT {columns}";
        return this;
    }

    public SqlQueryBuilder From(string table)
    {
        _from = $"FROM {table}";
        return this;
    }

    public SqlQueryBuilder Where(string condition)
    {
        _where = $"WHERE {condition}";
        return this;
    }

    public SqlQueryBuilder OrderBy(string column)
    {
        _orderBy = $"ORDER BY {column}";
        return this;
    }

    public SqlQueryBuilder Limit(int count)
    {
        _limit = $"LIMIT {count}";
        return this;
    }

    public string Build()
    {
        return $"{_select} {_from} {_where} {_orderBy} {_limit}".Trim();
    }
}

// Usage
var query = new SqlQueryBuilder()
    .Select("Id, Name, Price")
    .From("Products")
    .Where("Price > 100")
    .OrderBy("Name")
    .Limit(10)
    .Build();

// Result: SELECT Id, Name, Price FROM Products WHERE Price > 100 ORDER BY Name LIMIT 10

// ✅ Configuration Builder
public class RequestBuilder
{
    private string _url;
    private HttpMethod _method = HttpMethod.Get;
    private Dictionary<string, string> _headers = new();
    private string _body;
    private TimeSpan _timeout = TimeSpan.FromSeconds(30);

    public RequestBuilder WithUrl(string url)
    {
        _url = url;
        return this;
    }

    public RequestBuilder WithMethod(HttpMethod method)
    {
        _method = method;
        return this;
    }

    public RequestBuilder WithHeader(string key, string value)
    {
        _headers[key] = value;
        return this;
    }

    public RequestBuilder WithBody(string body)
    {
        _body = body;
        return this;
    }

    public RequestBuilder WithTimeout(TimeSpan timeout)
    {
        _timeout = timeout;
        return this;
    }

    public HttpRequestMessage Build()
    {
        var request = new HttpRequestMessage(_method, _url);

        foreach (var header in _headers)
            request.Headers.Add(header.Key, header.Value);

        if (!string.IsNullOrEmpty(_body))
            request.Content = new StringContent(_body);

        request.Headers.Add("Timeout", _timeout.TotalSeconds.ToString());

        return request;
    }
}

// Usage
var request = new RequestBuilder()
    .WithUrl("https://api.example.com/products")
    .WithMethod(HttpMethod.Post)
    .WithHeader("Authorization", "Bearer token123")
    .WithBody(JsonConvert.SerializeObject(new { name = "Product" }))
    .WithTimeout(TimeSpan.FromSeconds(60))
    .Build();

// When to use Builder:
// ✓ Complex object construction
// ✓ Optional parameters
// ✓ Immutable objects
// ✓ Fluent interface preferred

Structural Design Patterns

1. Adapter Pattern

// ✅ Converting incompatible interfaces
public interface IModernPaymentProcessor
{
    Task<bool> ProcessAsync(decimal amount);
}

public class LegacyPaymentGateway
{
    public bool Process(double amount, string currency = "USD")
    {
        // Old interface - synchronous, uses double, has currency
        return amount > 0;
    }
}

// Adapter - makes legacy compatible with modern
public class LegacyPaymentAdapter : IModernPaymentProcessor
{
    private readonly LegacyPaymentGateway _legacyGateway;

    public LegacyPaymentAdapter(LegacyPaymentGateway legacyGateway)
    {
        _legacyGateway = legacyGateway;
    }

    public async Task<bool> ProcessAsync(decimal amount)
    {
        // Adapt: decimal -> double, async -> sync, USD assumed
        return await Task.FromResult(_legacyGateway.Process((double)amount, "USD"));
    }
}

// Now can use legacy gateway with modern interface
public class CheckoutService
{
    private readonly IModernPaymentProcessor _processor;

    public CheckoutService(IModernPaymentProcessor processor)
    {
        _processor = processor;
    }

    public async Task CheckoutAsync(decimal amount)
    {
        bool success = await _processor.ProcessAsync(amount);
        Console.WriteLine(success ? "Payment processed" : "Payment failed");
    }
}

// Usage
var legacyGateway = new LegacyPaymentGateway();
IModernPaymentProcessor adapter = new LegacyPaymentAdapter(legacyGateway);
var checkout = new CheckoutService(adapter);
await checkout.CheckoutAsync(100);

// Adapter uses:
// ✓ Integrating legacy code
// ✓ Working with third-party libraries
// ✓ Converting between incompatible interfaces

2. Decorator Pattern

// ✅ Adding functionality dynamically
public interface IDataRepository
{
    Task<T> GetAsync<T>(int id);
    Task SaveAsync<T>(T entity);
}

public class BasicRepository : IDataRepository
{
    public async Task<T> GetAsync<T>(int id)
    {
        await Task.Delay(100); // DB query
        return (T)(object)new { Id = id };
    }

    public async Task SaveAsync<T>(T entity)
    {
        await Task.Delay(100); // DB insert
    }
}

// Decorator 1: Caching
public class CachedRepository : IDataRepository
{
    private readonly IDataRepository _innerRepository;
    private readonly IDistributedCache _cache;

    public CachedRepository(IDataRepository innerRepository, IDistributedCache cache)
    {
        _innerRepository = innerRepository;
        _cache = cache;
    }

    public async Task<T> GetAsync<T>(int id)
    {
        var cacheKey = $"{typeof(T).Name}_{id}";
        var cached = await _cache.GetStringAsync(cacheKey);

        if (!string.IsNullOrEmpty(cached))
            return JsonConvert.DeserializeObject<T>(cached);

        var result = await _innerRepository.GetAsync<T>(id);
        await _cache.SetStringAsync(cacheKey, JsonConvert.SerializeObject(result));
        return result;
    }

    public async Task SaveAsync<T>(T entity)
    {
        await _innerRepository.SaveAsync(entity);
        // Invalidate cache...
    }
}

// Decorator 2: Logging
public class LoggedRepository : IDataRepository
{
    private readonly IDataRepository _innerRepository;
    private readonly ILogger<LoggedRepository> _logger;

    public LoggedRepository(IDataRepository innerRepository, ILogger<LoggedRepository> logger)
    {
        _innerRepository = innerRepository;
        _logger = logger;
    }

    public async Task<T> GetAsync<T>(int id)
    {
        _logger.LogInformation($"Getting {typeof(T).Name} with id {id}");
        var result = await _innerRepository.GetAsync<T>(id);
        _logger.LogInformation($"Retrieved {typeof(T).Name}");
        return result;
    }

    public async Task SaveAsync<T>(T entity)
    {
        _logger.LogInformation($"Saving {typeof(T).Name}");
        await _innerRepository.SaveAsync(entity);
        _logger.LogInformation($"Saved {typeof(T).Name}");
    }
}

// Stack decorators - Composition
IDataRepository repo = new BasicRepository();
repo = new CachedRepository(repo, cache); // Add caching
repo = new LoggedRepository(repo, logger); // Add logging

// Usage
var user = await repo.GetAsync<User>(1); // Gets: cached → logged → basic

// Decorator Benefits:
// ✓ Add functionality without modifying original
// ✓ Multiple decorators can be stacked
// ✓ Better than inheritance for feature combination
// ✓ Runtime behavior changes

3. Facade Pattern

// ✅ Simplifying complex subsystem
public class OrderSubsystem
{
    // Complex interfaces for different components
    public interface IInventoryService
    {
        Task ReserveProductAsync(int productId, int quantity);
    }

    public interface IPaymentService
    {
        Task<bool> ProcessPaymentAsync(decimal amount);
    }

    public interface IShippingService
    {
        Task<string> CalculateShippingAsync(Address address);
        Task ArrangeShippingAsync(Order order);
    }

    public interface INotificationService
    {
        Task SendConfirmationAsync(string email);
    }

    // Complex process - requiring multiple services
    public class ComplexOrderProcess
    {
        private readonly IInventoryService _inventory;
        private readonly IPaymentService _payment;
        private readonly IShippingService _shipping;
        private readonly INotificationService _notification;

        public async Task ProcessOrderAsync(Order order)
        {
            // Many steps, many services
            await _inventory.ReserveProductAsync(order.ProductId, order.Quantity);
            bool paymentSuccess = await _payment.ProcessPaymentAsync(order.Total);

            if (!paymentSuccess)
                throw new PaymentFailedException();

            var shippingCost = await _shipping.CalculateShippingAsync(order.Address);
            await _shipping.ArrangeShippingAsync(order);
            await _notification.SendConfirmationAsync(order.CustomerEmail);
        }
    }
}

// Facade - Simple interface for complex process
public interface IOrderService
{
    Task<OrderResult> PlaceOrderAsync(OrderRequest request);
}

public class OrderFacade : IOrderService
{
    private readonly IInventoryService _inventory;
    private readonly IPaymentService _payment;
    private readonly IShippingService _shipping;
    private readonly INotificationService _notification;

    public OrderFacade(
        IInventoryService inventory,
        IPaymentService payment,
        IShippingService shipping,
        INotificationService notification)
    {
        _inventory = inventory;
        _payment = payment;
        _shipping = shipping;
        _notification = notification;
    }

    public async Task<OrderResult> PlaceOrderAsync(OrderRequest request)
    {
        try
        {
            // All complexity hidden inside
            await _inventory.ReserveProductAsync(request.ProductId, request.Quantity);

            if (!await _payment.ProcessPaymentAsync(request.Total))
                return new OrderResult { Success = false, Message = "Payment failed" };

            var shippingCost = await _shipping.CalculateShippingAsync(request.Address);
            var order = CreateOrder(request, shippingCost);

            await _shipping.ArrangeShippingAsync(order);
            await _notification.SendConfirmationAsync(request.Email);

            return new OrderResult { Success = true, OrderId = order.Id };
        }
        catch (Exception ex)
        {
            return new OrderResult { Success = false, Message = ex.Message };
        }
    }

    private Order CreateOrder(OrderRequest request, decimal shippingCost)
    {
        return new Order
        {
            ProductId = request.ProductId,
            Quantity = request.Quantity,
            Total = request.Total + shippingCost,
            Address = request.Address,
            CustomerEmail = request.Email
        };
    }
}

// Simple usage
public class CheckoutController
{
    private readonly IOrderService _orderService; // ← Uses facade

    [HttpPost("order")]
    public async Task<IActionResult> PlaceOrder([FromBody] OrderRequest request)
    {
        var result = await _orderService.PlaceOrderAsync(request);
        return Ok(result);
    }
}

// Facade Benefits:
// ✓ Simplifies complex subsystems
// ✓ Decouples clients from subsystem
// ✓ Centralized error handling
// ✓ Easier maintenance

Behavioral Design Patterns

1. Observer Pattern

// ✅ Notification system
public interface IObserver
{
    void Update(string message);
}

public interface ISubject
{
    void Attach(IObserver observer);
    void Detach(IObserver observer);
    void Notify();
}

public class OrderService : ISubject
{
    private string _state;
    private List<IObserver> _observers = new();

    public void PlaceOrder(Order order)
    {
        _state = $"Order placed: {order.Id}";
        Notify();
    }

    public void ProcessPayment(Order order)
    {
        _state = $"Payment processed: {order.Id}";
        Notify();
    }

    public void ShipOrder(Order order)
    {
        _state = $"Order shipped: {order.Id}";
        Notify();
    }

    public void Attach(IObserver observer) => _observers.Add(observer);
    public void Detach(IObserver observer) => _observers.Remove(observer);
    public void Notify() => _observers.ForEach(o => o.Update(_state));
}

public class EmailNotifier : IObserver
{
    public void Update(string message) => Console.WriteLine($"Email: {message}");
}

public class SMSNotifier : IObserver
{
    public void Update(string message) => Console.WriteLine($"SMS: {message}");
}

public class LoggingObserver : IObserver
{
    public void Update(string message) => Console.WriteLine($"Log: {message}");
}

// Usage
var orderService = new OrderService();
orderService.Attach(new EmailNotifier());
orderService.Attach(new SMSNotifier());
orderService.Attach(new LoggingObserver());

var order = new Order { Id = 1 };
orderService.PlaceOrder(order);     // All observers notified
orderService.ProcessPayment(order); // All observers notified
orderService.ShipOrder(order);      // All observers notified

// Modern C# - Events
public class OrderServiceModern
{
    public event EventHandler<OrderEventArgs> OrderPlaced;
    public event EventHandler<OrderEventArgs> PaymentProcessed;

    public void PlaceOrder(Order order)
    {
        // ... business logic
        OrderPlaced?.Invoke(this, new OrderEventArgs { Order = order });
    }

    public void ProcessPayment(Order order)
    {
        // ... business logic
        PaymentProcessed?.Invoke(this, new OrderEventArgs { Order = order });
    }
}

public class OrderEventArgs : EventArgs
{
    public Order Order { get; set; }
}

// Subscriber
var orderService = new OrderServiceModern();
orderService.OrderPlaced += (sender, args) => Console.WriteLine($"Email: Order {args.Order.Id} placed");
orderService.PaymentProcessed += (sender, args) => Console.WriteLine($"SMS: Payment for {args.Order.Id} processed");

// Observer/Event Uses:
// ✓ Loose coupling between components
// ✓ Publish-subscribe pattern
// ✓ Event-driven architecture

2. Strategy Pattern

// ✅ Interchangeable algorithms
public interface IDiscountStrategy
{
    decimal CalculateDiscount(decimal originalPrice, int quantity);
}

public class VolumeDiscountStrategy : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal originalPrice, int quantity)
    {
        if (quantity >= 100)
            return originalPrice * 0.20m; // 20% discount

        if (quantity >= 50)
            return originalPrice * 0.15m; // 15% discount

        if (quantity >= 10)
            return originalPrice * 0.10m; // 10% discount

        return 0;
    }
}

public class SeasonalDiscountStrategy : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal originalPrice, int quantity)
    {
        if (DateTime.Now.Month == 12) // Christmas
            return originalPrice * 0.25m;

        if (DateTime.Now.Month == 7) // Summer sale
            return originalPrice * 0.15m;

        return 0;
    }
}

public class MembershipDiscountStrategy : IDiscountStrategy
{
    private readonly MembershipLevel _level;

    public MembershipDiscountStrategy(MembershipLevel level)
    {
        _level = level;
    }

    public decimal CalculateDiscount(decimal originalPrice, int quantity)
    {
        return _level switch
        {
            MembershipLevel.Gold => originalPrice * 0.25m,
            MembershipLevel.Silver => originalPrice * 0.15m,
            MembershipLevel.Bronze => originalPrice * 0.05m,
            _ => 0
        };
    }
}

public class PricingService
{
    private readonly IDiscountStrategy _discountStrategy;

    public PricingService(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public decimal CalculateFinalPrice(decimal originalPrice, int quantity)
    {
        var discount = _discountStrategy.CalculateDiscount(originalPrice, quantity);
        return originalPrice - discount;
    }
}

// Usage - Strategy determined at runtime
IDiscountStrategy strategy = GetApplicableStrategy(customer);
var pricingService = new PricingService(strategy);
decimal finalPrice = pricingService.CalculateFinalPrice(100, 50);

// Strategy Pattern Benefits:
// ✓ Encapsulate algorithms
// ✓ Swap strategies at runtime
// ✓ Avoid conditional logic
// ✓ Easy to test each strategy

3. Command Pattern

// ✅ Encapsulate requests as objects
public interface ICommand
{
    Task ExecuteAsync();
    Task UndoAsync();
}

public class TransferMoneyCommand : ICommand
{
    private readonly IAccountService _accountService;
    private readonly Account _from;
    private readonly Account _to;
    private readonly decimal _amount;
    private bool _executed = false;

    public TransferMoneyCommand(IAccountService accountService, Account from, Account to, decimal amount)
    {
        _accountService = accountService;
        _from = from;
        _to = to;
        _amount = amount;
    }

    public async Task ExecuteAsync()
    {
        await _accountService.WithdrawAsync(_from, _amount);
        await _accountService.DepositAsync(_to, _amount);
        _executed = true;
    }

    public async Task UndoAsync()
    {
        if (!_executed)
            throw new InvalidOperationException("Cannot undo unexecuted command");

        await _accountService.DepositAsync(_from, _amount);
        await _accountService.WithdrawAsync(_to, _amount);
        _executed = false;
    }
}

public class CommandInvoker
{
    private readonly Stack<ICommand> _executedCommands = new();
    private readonly Stack<ICommand> _undoneCommands = new();

    public async Task ExecuteAsync(ICommand command)
    {
        await command.ExecuteAsync();
        _executedCommands.Push(command);
        _undoneCommands.Clear(); // Clear redo history
    }

    public async Task UndoAsync()
    {
        if (_executedCommands.Count == 0)
            return;

        var command = _executedCommands.Pop();
        await command.UndoAsync();
        _undoneCommands.Push(command);
    }

    public async Task RedoAsync()
    {
        if (_undoneCommands.Count == 0)
            return;

        var command = _undoneCommands.Pop();
        await command.ExecuteAsync();
        _executedCommands.Push(command);
    }
}

// Usage
var invoker = new CommandInvoker();

var transferCommand = new TransferMoneyCommand(
    accountService,
    fromAccount,
    toAccount,
    100);

await invoker.ExecuteAsync(transferCommand);
await invoker.UndoAsync();  // Reverses transfer
await invoker.RedoAsync();  // Re-applies transfer

// Command Pattern Uses:
// ✓ Undo/Redo functionality
// ✓ Transaction logging
// ✓ Queuing operations
// ✓ Scheduling tasks
// ✓ Macro recording

Advanced OOP Concepts

1. Generic Constraints

// ✅ Reference type constraint
public class ReferenceRepository<T> where T : class
{
    public void Add(T item) { }
    // T can be any reference type
}

// ✅ Value type constraint
public class ValueTypeHandler<T> where T : struct
{
    public void Process(T value) { }
    // T can be any value type (struct, int, bool, etc.)
}

// ✅ Default constructor constraint
public class Factory<T> where T : new()
{
    public T CreateInstance()
    {
        return new T(); // T must have parameterless constructor
    }
}

// ✅ Base class constraint
public class EmployeeRepository<T> where T : Employee
{
    public void ProcessEmployee(T employee)
    {
        Console.WriteLine($"Processing {employee.Name}");
    }
}

// ✅ Interface constraint
public class ValidationService<T> where T : IValidatable
{
    public bool Validate(T item)
    {
        return item.IsValid();
    }
}

// ✅ Enum constraint
public class EnumHelper<T> where T : Enum
{
    public List<T> GetAllValues()
    {
        return Enum.GetValues(typeof(T)).Cast<T>().ToList();
    }
}

// ✅ Multiple constraints
public class AdvancedService<T> where T : class, IDisposable, IComparable<T>, new()
{
    // T must be:
    // - Reference type
    // - Implement IDisposable
    // - Implement IComparable<T>
    // - Have parameterless constructor
}

// ✅ Generic type constraints
public class GenericRepository<T> where T : class
{
    private List<T> _items = new();

    public void Add<TKey>(TKey id, T item) where TKey : IEquatable<TKey>
    {
        _items.Add(item);
    }
}

2. Covariance and Contravariance

// ✅ Covariance (out) - Return type can be more specific
public interface IRepository<out T>
{
    T Get();
}

public class Animal { }
public class Dog : Animal { }

public class DogRepository : IRepository<Dog>
{
    public Dog Get() => new Dog();
}

// Covariance allows this:
IRepository<Animal> repo = new DogRepository(); // ✓ Safe - Dog IS-A Animal
Animal animal = repo.Get(); // ✓ You get at least an Animal

// ✅ Contravariance (in) - Parameter type can be more general
public interface IProcessor<in T>
{
    void Process(T item);
}

public class AnimalProcessor : IProcessor<Animal>
{
    public void Process(Animal animal) { }
}

// Contravariance allows this:
IProcessor<Dog> processor = new AnimalProcessor(); // ✓ Safe
processor.Process(new Dog()); // ✓ AnimalProcessor can process any Animal, including Dogs

// Real-world example with Func/Action
Func<Dog> getDog = () => new Dog();
Func<Animal> getAnimal = getDog; // ✓ Covariance - return type is more specific

Action<Animal> processAnimal = (a) => Console.WriteLine(a);
Action<Dog> processDog = processAnimal; // ✓ Contravariance - parameter type is more specific

Pattern Selection Guide

// When to use each pattern:

// CREATIONAL PATTERNS (Object creation)
/*
Singleton      - Single instance, lazy initialization
Factory        - Create objects without specifying classes
Abstract Factory - Create families of related objects
Builder        - Complex object construction
Prototype      - Clone objects
*/

// STRUCTURAL PATTERNS (Object composition)
/*
Adapter        - Make incompatible interfaces work together
Bridge         - Separate abstraction from implementation
Composite      - Tree-like hierarchies
Decorator      - Add functionality dynamically
Facade         - Simplified interface for complex system
Flyweight      - Share objects efficiently
Proxy          - Control access to another object
*/

// BEHAVIORAL PATTERNS (Object communication)
/*
Chain of Resp.  - Pass requests along a chain
Command         - Encapsulate requests as objects
Interpreter    - Language interpretation
Iterator       - Sequential access to elements
Mediator       - Centralized communication
Memento        - Capture/restore object state
Observer       - Notify multiple objects
State          - Alter behavior when state changes
Strategy       - Encapsulate algorithms
Template Method - Define algorithm skeleton
Visitor        - Apply operations to objects
*/

public class PatternSelector
{
    public string SelectPattern(string problem)
    {
        return problem switch
        {
            // Single instance needed
            "Need one global logger" => "Singleton",

            // Creating different types
            "Creating notifications (email, SMS, push)" => "Factory or Strategy",

            // Complex construction
            "Building complex configurations" => "Builder",

            // Interface incompatibility
            "Using legacy code with new interface" => "Adapter",

            // Adding features dynamically
            "Adding caching to repository" => "Decorator",

            // Simplifying complex system
            "Wrapping complex order processing" => "Facade",

            // Handling multiple observers
            "Notifying multiple listeners of events" => "Observer",

            // Selecting algorithm at runtime
            "Different discount calculations" => "Strategy",

            // Undoable operations
            "Implementing undo/redo" => "Command",

            // Hierarchical data
            "File system folders and files" => "Composite",

            // State-dependent behavior
            "Order status transitions" => "State",

            _ => "No pattern identified"
        };
    }
}

Concluzie

Diferențiatori Senior Developer în OOP/SOLID/Patterns:

  1. Understanding Trade-offs

    • Know when to apply patterns
    • Understand cost of abstraction
    • Don’t over-engineer
  2. Code Review Skills

    • Identify violations
    • Suggest improvements
    • Explain rationale
  3. Mentoring

    • Teach principles clearly
    • Show examples
    • Help junior devs improve
  4. Production Mindset

    • Performance implications
    • Maintainability long-term
    • Real-world constraints
  5. Continuous Learning

    • Patterns evolve
    • New languages bring new patterns
    • Study codebase designs

Data: 2025 C# Version: 12+ .NET Version: 8+