← Назад к вопросам

Можешь ли описать Invertion Of Control на примере такси

2.0 Middle🔥 191 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Inversion of Control (IoC) на примере такси

Inversion of Control (IoC) — это паттерн проектирования, при котором управление потоком выполнения программы передаётся фреймворку вместо того, чтобы управлять им явно в коде. Отличный пример — приложение для вызова такси.

Без IoC: Традиционный подход (Ты управляешь всем)

Представь, как ты вызываешь такси без приложения:

// БЕЗ IoC — ты полностью контролируешь процесс
public class TraditionalTaxiUser {
    
    public void orderTaxi() {
        // Ты сам создаёшь таксопарк
        TaxiDispatcher dispatcher = new TaxiDispatcher();
        
        // Ты сам создаёшь водителя
        Driver driver = new Driver("Ivan", "Lada", "yellow");
        dispatcher.registerDriver(driver);
        
        // Ты сам ищешь свободную машину
        Taxi taxi = dispatcher.findAvailableTaxi();
        if (taxi == null) {
            throw new NoTaxiAvailableException();
        }
        
        // Ты сам говоришь водителю адрес
        taxi.goToAddress("Petrovka St, 25");
        
        // Ты сам отслеживаешь процесс
        while (!taxi.hasArrived()) {
            System.out.println("Waiting...");
            Thread.sleep(1000);
        }
        
        // Ты сам рассчитываешь и платишь
        double fare = dispatcher.calculateFare("Petrovka St, 25");
        taxi.pay(fare);
        
        System.out.println("Trip completed");
    }
}

// Проблемы:
// 1. Ты сам управляешь жизненным циклом таксопарка
// 2. Ты сам ищешь водителя
// 3. Ты сам контролируешь поток выполнения
// 4. Много деталей, которые тебе не нужны

С IoC: Используешь приложение Яндекс.Такси (Фреймворк управляет)

Теперь используешь приложение — это IoC:

// С IoC — фреймворк управляет процессом
public class YandexTaxiUser {
    
    // Зависимость от интерфейса, не от конкретной реализации
    private final TaxiService taxiService;
    
    // IoC контейнер внедряет зависимость
    public YandexTaxiUser(TaxiService taxiService) {
        this.taxiService = taxiService;
    }
    
    public void orderTaxi() {
        // Ты просто говоришь что хочешь
        TaxiRequest request = new TaxiRequest(
            "Petrovka St, 25",    // откуда
            "Red Square, 1",      // куда
            TaxiClass.COMFORT     // класс
        );
        
        // Фреймворк (Яндекс) сам делает всё остальное
        taxiService.orderTaxi(request, new TaxiCallback() {
            @Override
            public void onTaxiFound(Taxi taxi) {
                // Информируешься о результате
                System.out.println("Taxi found: " + taxi.getPlateNumber());
                System.out.println("Driver: " + taxi.getDriver().getName());
                System.out.println("ETA: " + taxi.getETA() + " minutes");
            }
            
            @Override
            public void onTaxiArrived(Taxi taxi) {
                System.out.println("Taxi arrived at your location");
                taxi.startTrip();
            }
            
            @Override
            public void onTripCompleted(Trip trip) {
                System.out.println("Trip completed");
                System.out.println("Fare: " + trip.getFare());
                System.out.println("Distance: " + trip.getDistance() + " km");
            }
        });
        
        // Ты просто жди результата — фреймворк всё сделает
    }
}

// Преимущества:
// 1. Фреймворк управляет жизненным циклом
// 2. Фреймворк находит свободного водителя
// 3. Фреймворк контролирует поток выполнения
// 4. Ты думаешь только о том, что хочешь

Более точный пример с Spring

Это именно то, что делает Spring в Java приложениях:

// Интерфейсы — контракты
public interface TaxiDispatcher {
    Taxi findTaxi(String location);
    double calculateFare(String from, String to);
}

public interface PaymentService {
    void processPayment(double amount, String method);
}

public interface NotificationService {
    void notifyUser(String message);
}

// Реализации
@Service
public class YandexTaxiDispatcher implements TaxiDispatcher {
    @Override
    public Taxi findTaxi(String location) {
        // Логика поиска такси в Яндексе
        return new Taxi("YA123", "Lada");
    }
    
    @Override
    public double calculateFare(String from, String to) {
        // Расчёт стоимости в Яндексе
        return 350.0;
    }
}

@Service
public class UberTaxiDispatcher implements TaxiDispatcher {
    @Override
    public Taxi findTaxi(String location) {
        // Логика поиска такси в Uber
        return new Taxi("UB456", "Mercedes");
    }
    
    @Override
    public double calculateFare(String from, String to) {
        // Расчёт стоимости в Uber
        return 450.0;
    }
}

@Service
public class StripePaymentService implements PaymentService {
    @Override
    public void processPayment(double amount, String method) {
        // Интеграция со Stripe
    }
}

@Service
public class TwilioNotificationService implements NotificationService {
    @Override
    public void notifyUser(String message) {
        // Отправка уведомления через Twilio SMS
    }
}

// Бизнес-логика — зависит только от интерфейсов
@Service
public class TaxiOrderService {
    
    private final TaxiDispatcher taxiDispatcher;      // Spring внедрит
    private final PaymentService paymentService;      // Spring внедрит
    private final NotificationService notificationService; // Spring внедрит
    
    // IoC: Spring сам создаст экземпляры и внедрит
    public TaxiOrderService(
        TaxiDispatcher taxiDispatcher,
        PaymentService paymentService,
        NotificationService notificationService) {
        
        this.taxiDispatcher = taxiDispatcher;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }
    
    public void orderTaxi(String from, String to) {
        // Логика не знает реализацию!
        Taxi taxi = taxiDispatcher.findTaxi(from);
        double fare = taxiDispatcher.calculateFare(from, to);
        
        notificationService.notifyUser("Taxi found: " + taxi);
        paymentService.processPayment(fare, "card");
        
        taxi.goToAddress(from);
        notificationService.notifyUser("Taxi arrived");
    }
}

// Spring контейнер (IoC контейнер) управляет всем
@SpringBootApplication
public class TaxiAppMain {
    public static void main(String[] args) {
        // Spring создаёт контекст
        ApplicationContext context = SpringApplication.run(
            TaxiAppMain.class, args);
        
        // Spring внедрил нужные реализации
        TaxiOrderService service = context.getBean(TaxiOrderService.class);
        
        // TaxiOrderService получила:
        // - YandexTaxiDispatcher (или UberTaxiDispatcher в зависимости от профиля)
        // - StripePaymentService
        // - TwilioNotificationService
        
        service.orderTaxi("Petrovka St", "Red Square");
    }
}

Ключевые различия

ТРАДИЦИОННЫЙ КОНТРОЛЬ (без IoC):
┌─────────────────────────────────────────┐
│ Твой код (ты управляешь)                │
│  ├─ Создаёшь объекты                   │
│  ├─ Управляешь зависимостями           │
│  ├─ Вызываешь методы в нужном порядке  │
│  └─ Контролируешь весь поток            │
└─────────────────────────────────────────┘

ИНВЕРСИЯ КОНТРОЛЯ (с IoC контейнером):
┌─────────────────────────────────────────┐
│ Spring контейнер (управляет)            │
│  ├─ Создаёт объекты                    │
│  ├─ Управляет зависимостями (DI)       │
│  ├─ Вызывает callback'и твоего кода    │
│  └─ Контролирует весь поток            │
├─────────────────────────────────────────┤
│ Твой код (только бизнес-логика)        │
│  ├─ Реализуешь интерфейсы              │
│  └─ Говоришь контейнеру что делать     │
└─────────────────────────────────────────┘

IoC Container — это сердце фреймворка

// Spring IoC контейнер делает примерно следующее:

public class SimpleIoCContainer {
    
    private Map<String, Object> singletons = new HashMap<>();
    private Map<Class<?>, Class<?>> bindings = new HashMap<>();
    
    // Регистрация binding'ов
    public void bind(Class<?> interfaceClass, Class<?> implementationClass) {
        bindings.put(interfaceClass, implementationClass);
    }
    
    // Получение объекта (singleton)
    public <T> T getBean(Class<T> interfaceClass) {
        String beanName = interfaceClass.getSimpleName();
        
        if (singletons.containsKey(beanName)) {
            return (T) singletons.get(beanName);
        }
        
        Class<?> implementationClass = bindings.get(interfaceClass);
        try {
            T instance = (T) implementationClass.getDeclaredConstructor()
                .newInstance();
            singletons.put(beanName, instance);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

// Использование
SimpleIoCContainer container = new SimpleIoCContainer();
container.bind(TaxiDispatcher.class, YandexTaxiDispatcher.class);
container.bind(PaymentService.class, StripePaymentService.class);

// Container создаст и вернёт singleton'ы
TaxiDispatcher dispatcher = container.getBean(TaxiDispatcher.class);
PaymentService payment = container.getBean(PaymentService.class);

Аналогия с жизнью

БЕЗ IoC (Традиционный путь):
Ты хочешь поужинать → Идёшь в магазин → Покупаешь продукты →
Идёшь домой → Готовишь сам → Ешь → Моешь посуду
(ТЫ управляешь всем процессом)

С IoC (Используешь фреймворк/сервис):
Ты хочешь поужинать → Заказываешь доставку из ресторана →
Ожидаешь доставку → Получаешь готовое блюдо → Ешь →
Отправляешь обратно грязную посуду
(РЕСТОРАН (контейнер) управляет процессом, ты только указываешь что хочешь)

Преимущества IoC

  1. Слабая связанность — код не зависит от конкретных реализаций
  2. Тестируемость — легко подменить зависимости на mock'и
  3. Гибкость — можно менять реализацию без изменения кода
  4. Переиспользование — код может работать с разными реализациями
  5. Читаемость — сосредоточивайся на бизнес-логике, не на управлении объектами

Заключение

Inversion of Control — это когда ты не управляешь потоком выполнения, а фреймворк управляет. Ты только:

  • Описываешь что нужно сделать
  • Реализуешь интерфейсы
  • Говоришь контейнеру как связать зависимости

Фреймворк (Spring) — это и есть IoC контейнер в Java мире. Он создаёт объекты, управляет их жизненным циклом, внедряет зависимости и контролирует поток выполнения.

Можешь ли описать Invertion Of Control на примере такси | PrepBro