Coupling и Cohesion: два понятия, которые стоит понимать каждому разработчику
Последнее время я довольно активно изучаю паттерны проектирования и архитектуру приложений. И чем больше в это погружаюсь, тем чаще встречаю два понятия, которые лежат где-то в основе практически всего — coupling и cohesion.
На первый взгляд они звучат довольно абстрактно, но на самом деле описывают очень практичные вещи: насколько части системы зависят друг от друга и насколько хорошо они подходят друг другу внутри одного модуля или класса.
Coupling — связанность
Coupling показывает, насколько сильно один компонент зависит от другого.
Например, если класс напрямую создаёт и использует конкретную реализацию:
class OrderService { public function pay(Order $order): void { $gateway = new StripePaymentGateway(); $gateway->charge($order->total()); } }
OrderService жёстко связан со StripePaymentGateway.
Если завтра понадобится PayPal, тестовый mock или другой способ оплаты, придётся менять сам OrderService.
Можно уменьшить связанность, например, используя интерфейс и dependency injection:
class OrderService { public function __construct( private PaymentGateway $gateway ) {} public function pay(Order $order): void { $this->gateway->charge($order->total()); } }
Теперь OrderService не знает, какой именно платёжный шлюз используется.
Это уже напрямую связано с SOLID, особенно с Dependency Inversion Principle.
Cohesion — связность внутри компонента
Если coupling отвечает на вопрос:
«Насколько сильно этот компонент зависит от других?»
то cohesion скорее отвечает:
«Насколько хорошо всё внутри этого компонента относится к одной задаче?»
Представим класс:
class UserService { public function register(): void {} public function sendEmail(): void {} public function generatePdf(): void {} public function resizeImage(): void {} }
Формально всё это может работать. Но у класса очень низкая cohesion — в нём собраны совершенно разные обязанности.
Гораздо лучше разделить их:
UserService
EmailService
PdfGenerator
ImageResizer
Каждый компонент занимается своей задачей.
И здесь мы снова приходим к Single Responsibility Principle из SOLID.
Как это связано с паттернами?
На самом деле многие паттерны проектирования можно рассматривать через призму coupling и cohesion.
Dependency Injection помогает уменьшить coupling.
Strategy позволяет заменить конкретную реализацию и тем самым уменьшить зависимость между компонентами.
Facade скрывает сложность подсистемы и уменьшает количество зависимостей клиента.
Mediator уменьшает количество прямых связей между объектами.
А такие идеи, как Single Responsibility, помогают создавать компоненты с высокой cohesion.
Получается довольно простая картина:
High Cohesion
+
Low Coupling
↓
более гибкая архитектура
Конечно, это не означает, что нужно стремиться вообще избавиться от всех зависимостей. Зависимости между компонентами неизбежны.
Вопрос в другом — насколько они сильные и насколько оправданные.
Зачем вообще это понимать?
Понимание coupling и cohesion помогает смотреть на код немного шире, чем просто:
«Работает ли этот метод?»
Можно начать задавать другие вопросы:
Почему изменение одного класса требует изменений ещё в пяти местах?
Почему этот класс знает слишком много о других компонентах?
Почему в одном классе собрано столько разных задач?
Насколько легко заменить конкретную реализацию?
Насколько просто протестировать этот компонент отдельно?
Именно такие вопросы постепенно переводят мышление от написания отдельных классов к проектированию системы.
Для меня это одна из причин, почему при изучении паттернов и архитектуры важно понимать не только сами паттерны, но и фундаментальные принципы, которые за ними стоят.
Low coupling + high cohesion — пожалуй, одна из самых полезных концепций, которую стоит держать в голове при проектировании PHP-приложений.

Добавить комментарий