Dependency Injection

The Problem

If each service creates its own dependencies internally → Tightly coupled code, impossible to replace with mocks for testing, and no control over initialization order.

The Solution: Manual Constructor Injection

Instead of relying on a third-party DI framework, all dependencies are injected via constructors. This approach provides:

  • Clear initialization order — readable at a glance
  • Easy to replace with mock objects when writing Unit Tests
  • No additional external library dependencies
// EnemyService receives all dependencies through its constructor
// No hidden "new AStarPathfinder()" lurking inside
public EnemyService(HexMap map, LaneService laneService,
                    BaseHealthService health, CurrencyService currency)
{
    _map = map;
    _laneService = laneService;
    _baseHealthService = health;
    _currencyService = currency;
    _pathfinder = new AStarPathfinder();
    ActiveEnemies = new List<Enemy>();
}

Connection to Testability

Because EnemyService does not create its own CurrencyService or BaseHealthService, Unit Tests can pass in mock objects with custom behavior — allowing logic to be verified in complete isolation.