# Dependency Injection: Loose Coupling Pattern

# Dependency Injection: Loose Coupling Pattern

## Problem

**Tight Coupling Issues:**
- Classes directly instantiate their dependencies, creating hard-coded relationships
- Testing becomes difficult because you can't easily mock dependencies
- Changing implementations requires modifying multiple classes
- Code reusability suffers due to inflexible dependencies
- Maintenance becomes expensive as the codebase grows

```csharp
// ❌ Tightly Coupled - Hard to test and maintain
public class OrderService
{
    private PaymentProcessor _paymentProcessor = new PaymentProcessor();
    private EmailNotifier _emailNotifier = new EmailNotifier();
    
    public void ProcessOrder(Order order)
    {
        _paymentProcessor.Process(order);
        _emailNotifier.SendConfirmation(order);
    }
}
```

**Why This Fails:**
- Can't test without actual payment processing
- Can't swap implementations without code changes
- Dependencies are hidden and implicit
- Violates Single Responsibility Principle

---

## Solution

**Dependency Injection Pattern:**
- Invert control by injecting dependencies from outside
- Use abstractions (interfaces) instead of concrete implementations
- Leverage IoC containers for automatic dependency resolution
- Enable loose coupling and high testability

**Key Principles:**
1. **Depend on abstractions, not concretions**
2. **Inject dependencies through constructor, property, or method**
3. **Use IoC containers for lifecycle management**
4. **Separate object creation from usage**

---

## Code Examples

### 1. Basic Dependency Injection (Constructor Injection)

```csharp
// Define abstractions
public interface IPaymentProcessor
{
    void Process(Order order);
}

public interface IEmailNotifier
{
    void SendConfirmation(Order order);
}

// Concrete implementations
public class PaymentProcessor : IPaymentProcessor
{
    public void Process(Order order)
    {
        Console.WriteLine($"Processing payment for order {order.Id}");
    }
}

public class EmailNotifier : IEmailNotifier
{
    public void SendConfirmation(Order order)
    {
        Console.WriteLine($"Sending confirmation email for order {order.Id}");
    }
}

// Service with injected dependencies
public class OrderService
{
    private readonly IPaymentProcessor _paymentProcessor;
    private readonly IEmailNotifier _emailNotifier;
    
    // Dependencies injected through constructor
    public OrderService(IPaymentProcessor paymentProcessor, IEmailNotifier emailNotifier)
    {
        _paymentProcessor = paymentProcessor ?? throw new ArgumentNullException(nameof(paymentProcessor));
        _emailNotifier = emailNotifier ?? throw new ArgumentNullException(nameof(emailNotifier));
    }
    
    public void ProcessOrder(Order order)
    {
        _paymentProcessor.Process(order);
        _emailNotifier.SendConfirmation(order);
    }
}

// Usage
var paymentProcessor = new PaymentProcessor();
var emailNotifier = new EmailNotifier();
var orderService = new OrderService(paymentProcessor, emailNotifier);
orderService.ProcessOrder(new Order { Id = 1 });
```

### 2. IoC Container - Microsoft.Extensions.DependencyInjection

```csharp
using Microsoft.Extensions.DependencyInjection;

// Setup IoC Container
var services = new ServiceCollection();

// Register dependencies
services.AddScoped<IPaymentProcessor, PaymentProcessor>();
services.AddScoped<IEmailNotifier, EmailNotifier>();
services.AddScoped<OrderService>();

// Build service provider
var serviceProvider = services.BuildServiceProvider();

// Resolve and use
var orderService = serviceProvider.GetRequiredService<OrderService>();
orderService.ProcessOrder(new Order { Id = 1 });
```

### 3. Advanced IoC Configuration - Autofac

```csharp
using Autofac;

// Build container
var builder = new ContainerBuilder();

// Register implementations
builder.RegisterType<PaymentProcessor>().As<IPaymentProcessor>();
builder.RegisterType<EmailNotifier>().As<IEmailNotifier>();
builder.RegisterType<OrderService>();

// Register with factory
builder.Register(c => new PaymentProcessor())
    .As<IPaymentProcessor>()
    .SingleInstance();

// Register with parameters
builder.RegisterType<OrderService>()
    .WithParameter("retryCount", 3);

var container = builder.Build();

// Resolve
using (var scope = container.BeginLifetimeScope())
{
    var orderService = scope.Resolve<OrderService>();
    orderService.ProcessOrder(new Order { Id = 1 });
}
```

### 4. Property Injection

```csharp
public class OrderService
{
    public IPaymentProcessor PaymentProcessor { get; set; }
    public IEmailNotifier EmailNotifier { get; set; }
    
    public void ProcessOrder(Order order)
    {
        PaymentProcessor?.Process(order);
        EmailNotifier?.SendConfirmation(order);
    }
}

// Registration with property injection
services.AddScoped<OrderService>(provider =>
{
    var service = new OrderService
    {
        PaymentProcessor = provider.GetRequiredService<IPaymentProcessor>(),
        EmailNotifier = provider.GetRequiredService<IEmailNotifier>()
    };
    return service;
});
```

### 5. Method Injection

```csharp
public class OrderService
{
    public void ProcessOrder(Order order, IPaymentProcessor paymentProcessor, IEmailNotifier emailNotifier)
    {
        paymentProcessor.Process(order);
        emailNotifier.SendConfirmation(order);
    }
}

// Usage
var orderService = new OrderService();
orderService.ProcessOrder(order, paymentProcessor, emailNotifier);
```

### 6. Testing with Mocks

```csharp
using Moq;
using Xunit;

public class OrderServiceTests
{
    [Fact]
    public void ProcessOrder_CallsPaymentProcessor()
    {
        // Arrange
        var mockPaymentProcessor = new Mock<IPaymentProcessor>();
        var mockEmailNotifier = new Mock<IEmailNotifier>();
        var orderService = new OrderService(mockPaymentProcessor.Object, mockEmailNotifier.Object);
        var order = new Order { Id = 1 };
        
        // Act
        orderService.ProcessOrder(order);
        
        // Assert
        mockPaymentProcessor.Verify(x => x.Process(order), Times.Once);
        mockEmailNotifier.Verify(x => x.SendConfirmation(order), Times.Once);
    }
}
```

### 7. Lifetime Management

```csharp
// Transient: New instance every time
services.AddTransient<IPaymentProcessor, PaymentProcessor>();

// Scoped: One instance per request/scope
services.AddScoped<IEmailNotifier, EmailNotifier>();

// Singleton: One instance for application lifetime
services.AddSingleton<ILogger, Logger>();

// Factory pattern
services.AddScoped<IPaymentProcessor>(provider =>
{
    var config = provider.GetRequiredService<IConfiguration>();
    return new PaymentProcessor(config["PaymentGateway"]);
});
```

---

## Tips & Best Practices

| Tip | Benefit |
|-----|---------|
| **Always depend on interfaces** | Enables polymorphism and easy mocking |
| **Use constructor injection** | Makes dependencies explicit and required |
| **Validate null dependencies** | Fails fast with clear error messages |
| **Choose appropriate lifetimes** | Prevents memory leaks and state issues |
| **Avoid service locator pattern** | Keeps dependencies explicit and testable |
| **Register at composition root** | Centralizes configuration, easier maintenance |
| **Use factory pattern for complex creation** | Handles conditional logic and parameters |
| **Keep interfaces focused** | Follows Interface Segregation Principle |
| **Avoid circular dependencies** | Refactor to break cycles early |
| **Document dependency requirements** | Helps team understand architecture |

**Anti-Patterns to Avoid:**
- ❌ Service Locator (hiding dependencies)
- ❌ Static dependencies
- ❌ God objects with too many dependencies
- ❌ Mixing concerns in constructors
- ❌ Registering concrete types directly

**When to Use DI:**
- ✅ Multi-layered applications
- ✅ Projects requiring testability
- ✅ Large teams with changing requirements
- ✅ Microservices architectures
- ✅ Any production application
