← Tilbage til koncepter

Dependency Injection

Inversion of Control pattern til løs kobling mellem components

Kategori: Design Patterns

🎯 Key Points

  • Constructor injection - Injicér dependencies gennem constructor (mest common)
  • Setter injection - Injicér dependencies gennem setter methods (optional dependencies)
  • Interface injection - Dependencies implementerer et interface for injection
  • Service containers - Centralt registry til håndtering af dependencies og deres lifecycle
  • Dependency Inversion Principle - Depend on abstractions (interfaces) ikke concrete classes
  • Auto-wiring - Automatisk resolution af dependencies baseret på type hints
  • Service providers - Register bindings mellem interfaces og implementations
  • Singleton vs Transient - Lifecycle management af injected objects
  • Method injection - Injicér dependencies direkte i method calls
  • Container configuration - Definér hvordan dependencies skal resolves

💻 Kode Eksempel

<?php

// Step 1: Define interfaces (abstractions)
interface LoggerInterface {
    public function log(string $message, array $context = []): void;
}

interface UserRepositoryInterface {
    public function find(int $id): ?User;
    public function save(User $user): bool;
}

// Step 2: Concrete implementations
class FileLogger implements LoggerInterface {
    public function __construct(
        private string $logPath
    ) {}
    
    public function log(string $message, array $context = []): void {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = json_encode($context);
        file_put_contents(
            $this->logPath,
            "[{$timestamp}] {$message} {$contextStr}" . PHP_EOL,
            FILE_APPEND
        );
    }
}

class DatabaseUserRepository implements UserRepositoryInterface {
    public function __construct(
        private PDO $database
    ) {}
    
    public function find(int $id): ?User {
        $stmt = $this->database->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        return $data ? new User($data['id'], $data['name'], $data['email']) : null;
    }
    
    public function save(User $user): bool {
        // Implementation here
        return true;
    }
}

// Step 3: Service with injected dependencies
class UserService {
    public function __construct(
        private UserRepositoryInterface $repository,
        private LoggerInterface $logger
    ) {}
    
    public function getUser(int $id): ?User {
        $this->logger->log('Fetching user', ['id' => $id]);
        $user = $this->repository->find($id);
        
        if ($user) {
            $this->logger->log('User found', ['user_id' => $id]);
        } else {
            $this->logger->log('User not found', ['user_id' => $id]);
        }
        
        return $user;
    }
}

// Step 4: Manual dependency injection (without container)
$pdo = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
$logger = new FileLogger('/var/log/app.log');
$repository = new DatabaseUserRepository($pdo);
$userService = new UserService($repository, $logger);

$user = $userService->getUser(123);

💼 Hvornår bruges det?

  • Testing - Mock dependencies i unit tests uden at ændre production kode
  • Framework architecture - Laravel og Symfony bruger DI til at wire hele applikationen
  • Swappable implementations - Skift mellem FileLogger og DatabaseLogger uden at ændre consumer kode
  • Configuration management - Injicér configuration objects frem for hardcoded values
  • Third-party service integration - Injicér API clients som dependencies for nem testing
  • Multi-tenancy - Injicér tenant-specific dependencies baseret på context

⭐ Best Practices

  • Prefer constructor injection for required dependencies - gør dependencies eksplicitte
  • Type hint interfaces ikke concrete classes - følg Dependency Inversion Principle
  • Brug setter injection kun for optional dependencies der har reasonable defaults
  • Keep constructors simple - lav ikke kompleks logik i constructors med DI
  • Brug service containers til at manage complex dependency graphs automatisk
  • Document service bindings klart - hvilke interfaces er bundet til hvilke implementations
  • Avoid service locator anti-pattern - injicér dependencies direkte, ikke containeren
  • Use auto-wiring når muligt for at reducere manuel configuration

ℹ️ Quick Info

Kategori
Design Patterns
Sværhedsgrad
Mellem til Avanceret