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

Что такое Low coupling?

2.0 Middle🔥 141 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

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

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

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

Что такое Low coupling

Low coupling (слабая связанность) — это принцип архитектуры ПО, который заключается в том, что модули, классы и компоненты должны быть максимально независимы друг от друга. Это означает, что изменение в одном компоненте должно минимально влиять на другие компоненты. Low coupling часто сочетается с high cohesion (высокой связностью) в рамках самого модуля.

Противоположность: High Coupling (плохо)

// ❌ ПЛОХО: High coupling — сильная зависимость
@Controller
public class OrderController {
    
    // Прямая зависимость от конкретного класса
    private MySQLDatabase database = new MySQLDatabase();
    private EmailService emailService = new GmailEmailService();
    private PaymentProcessor processor = new StripePaymentProcessor();
    
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest request) {
        // Если изменится реализация database, нужно менять этот класс
        Order order = database.save(new Order(request));
        
        // Если заменим Gmail на SendGrid, нужно менять этот класс
        emailService.sendConfirmation(request.getEmail());
        
        // Если переключимся на PayPal, нужно менять этот класс
        processor.charge(request.getAmount());
        
        return order;
    }
}

// Проблемы:
// 1. Сложно тестировать (невозможно подменить зависимости)
// 2. Сложно менять реализацию
// 3. Класс зависит от деталей реализации, а не интерфейсов
// 4. Нарушается SOLID принцип (D - Dependency Inversion)

Решение: Low Coupling через интерфейсы

// ✅ ХОРОШО: Low coupling — через интерфейсы

// Определяем интерфейсы (абстракции)
public interface IOrderDatabase {
    Order save(Order order);
    Order findById(String id);
}

public interface IEmailService {
    void sendConfirmation(String email);
    void sendNotification(String email, String message);
}

public interface IPaymentProcessor {
    PaymentResult charge(BigDecimal amount, String cardToken);
    void refund(String transactionId);
}

// Конкретные реализации
@Component
public class MySQLDatabase implements IOrderDatabase {
    // Реализация
}

@Component
public class SendGridEmailService implements IEmailService {
    // Реализация
}

@Component
public class PayPalPaymentProcessor implements IPaymentProcessor {
    // Реализация
}

// Контроллер зависит от интерфейсов, а не от конкретных классов
@Controller
public class OrderController {
    
    private final IOrderDatabase database;      // Зависимость от интерфейса
    private final IEmailService emailService;   // Зависимость от интерфейса
    private final IPaymentProcessor processor;  // Зависимость от интерфейса
    
    // Dependency Injection через конструктор
    @Autowired
    public OrderController(
            IOrderDatabase database,
            IEmailService emailService,
            IPaymentProcessor processor) {
        this.database = database;
        this.emailService = emailService;
        this.processor = processor;
    }
    
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest request) {
        Order order = database.save(new Order(request));
        emailService.sendConfirmation(request.getEmail());
        processor.charge(request.getAmount(), request.getCardToken());
        return order;
    }
}

Преимущества Low Coupling

// 1. ЛЁГКО ТЕСТИРОВАТЬ: можно подменить зависимости на mock'и

@ExtendWith(MockitoExtension.class)
public class OrderControllerTest {
    
    @Mock
    private IOrderDatabase database;
    
    @Mock
    private IEmailService emailService;
    
    @Mock
    private IPaymentProcessor processor;
    
    @InjectMocks
    private OrderController controller;
    
    @Test
    public void testCreateOrder() {
        OrderRequest request = new OrderRequest("test@example.com", 100.00);
        Order expectedOrder = new Order(request);
        
        // Подменяем реальные реализации на mock'и
        when(database.save(any())).thenReturn(expectedOrder);
        when(processor.charge(any(), any()))
            .thenReturn(new PaymentResult("success"));
        
        Order result = controller.createOrder(request);
        
        // Проверяем что методы были вызваны
        verify(emailService).sendConfirmation(request.getEmail());
        assertEquals(expectedOrder, result);
    }
}

// 2. ЛЕГКО МЕНЯТЬ РЕАЛИЗАЦИЮ: просто создаёшь новый класс

@Component
public class PostgresDatabase implements IOrderDatabase {
    // Новая реализация для PostgreSQL
    // OrderController не нужно менять!
}

// 3. ЛЕГКО ДОБАВЛЯТЬ НОВЫЕ ФУНКЦИИ: через декораторы или новые реализации

@Component
public class CachingOrderDatabase implements IOrderDatabase {
    
    private final IOrderDatabase delegate;
    private final Cache<String, Order> cache;
    
    @Override
    public Order save(Order order) {
        Order saved = delegate.save(order);
        cache.put(order.getId(), saved);
        return saved;
    }
    
    @Override
    public Order findById(String id) {
        return cache.getOrElse(id, () -> delegate.findById(id));
    }
}

Принципы достижения Low Coupling

// 1. ЗАВИСИМОСТЬ ИНВЕРСИЯ (Dependency Inversion Principle)
// Зависи от абстракций, не от конкретных реализаций

@Service
public class GoodService {
    private final SomeInterface dependency;  // ✅ Зависимость от интерфейса
    
    public GoodService(SomeInterface dependency) {
        this.dependency = dependency;
    }
}

// 2. ИНВЕРСИЯ УПРАВЛЕНИЯ (Inversion of Control)
// Spring контейнер создаёт объекты и внедряет зависимости

@Configuration
public class AppConfig {
    @Bean
    public IOrderDatabase orderDatabase() {
        return new MySQLDatabase();
    }
    
    @Bean
    public OrderController orderController(IOrderDatabase database) {
        return new OrderController(database);
    }
}

// 3. СЛАБАЯ СВЯЗЬ ЧЕРЕЗ СОБЫТИЯ (Event-Driven Architecture)

@Component
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void createOrder(Order order) {
        // Сохраняем заказ
        // Публикуем событие (другие компоненты могут слушать)
        eventPublisher.publishEvent(new OrderCreatedEvent(order));
    }
}

// Другой сервис слушает событие (не знает о OrderService)
@Component
public class EmailNotificationService {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        sendConfirmationEmail(event.getOrder().getEmail());
    }
}

Архитектурные паттерны для Low Coupling

// 1. DEPENDENCY INJECTION (DI)
@Service
public class Service {
    private final Dependency dep;
    
    public Service(Dependency dep) {  // Внедрение через конструктор
        this.dep = dep;
    }
}

// 2. REPOSITORY PATTERN
public interface UserRepository {
    User findById(String id);
    void save(User user);
}

@Service
public class UserService {
    private final UserRepository repository;  // Зависимость от интерфейса
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}

// 3. ADAPTER PATTERN: адаптер для преобразования интерфейсов
public class LegacySystemAdapter implements IModernInterface {
    private final LegacySystem legacy;
    
    @Override
    public void doSomething() {
        // Преобразуем вызов в формат legacy системы
        legacy.doLegacyAction();
    }
}

Low Coupling vs High Cohesion

// ✅ ХОРОШО: Low Coupling + High Cohesion

// Low Coupling: компоненты слабо связаны
public interface PaymentService {
    PaymentResult process(Payment payment);
}

// High Cohesion: внутри класса всё связано с одной задачей
@Service
public class StripePaymentService implements PaymentService {
    // Все методы связаны с обработкой платежей через Stripe
    
    private String validateCard(Card card) { ... }
    private String chargeCard(Card card, BigDecimal amount) { ... }
    private String handleError(StripeException ex) { ... }
    
    @Override
    public PaymentResult process(Payment payment) {
        Card card = validateCard(payment.getCard());
        return new PaymentResult(chargeCard(card, payment.getAmount()));
    }
}

Заключение

Low coupling — это один из самых важных принципов в архитектуре ПО. Он:

  • Упрощает тестирование
  • Облегчает поддержку и расширение
  • Снижает риск регрессий при изменениях
  • Улучшает переиспользуемость кода
  • Делает систему более гибкой и масштабируемой

Добиваются низкой связанности через использование интерфейсов, dependency injection, паттернов проектирования и правильного разделения ответственности между компонентами.