← Назад к вопросам
Можешь ли описать 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
- Слабая связанность — код не зависит от конкретных реализаций
- Тестируемость — легко подменить зависимости на mock'и
- Гибкость — можно менять реализацию без изменения кода
- Переиспользование — код может работать с разными реализациями
- Читаемость — сосредоточивайся на бизнес-логике, не на управлении объектами
Заключение
Inversion of Control — это когда ты не управляешь потоком выполнения, а фреймворк управляет. Ты только:
- Описываешь что нужно сделать
- Реализуешь интерфейсы
- Говоришь контейнеру как связать зависимости
Фреймворк (Spring) — это и есть IoC контейнер в Java мире. Он создаёт объекты, управляет их жизненным циклом, внедряет зависимости и контролирует поток выполнения.