День четыреста шестьдесят седьмой. #DesignPatterns
Принципы SOLID.
4. Принцип разделения интерфейса (ISP)
«Клиенты не должны вынужденно зависеть от методов, которыми не пользуются» (Мартин Р. Принципы, паттерны и практики гибкой разработки. — 2006)
Принцип разделения интерфейса предназначен для получения простого и слабосвязанного кода. Клиенты сервиса должны зависеть лишь от тех методов, которые используют, и не должны знать о существовании не интересующих их частей интерфейса сервиса. Проблема в том, что разработчик сервиса не всегда знает о том, кто и как его будет использовать. Поэтому может потребоваться несколько перегруппировок методов таким образом, чтобы их использование было удобным максимальному числу клиентов.
Принцип ISP является частным случаем принципа наименьшего знания: для получения простого в сопровождении кода каждый класс должен знать минимум информации об окружающем коде - только то, что необходимо для решения его задачи. На практике это значит, что нужно минимизировать число зависимостей класса и стремиться использовать наиболее простые типы зависимостей. Чем больше у класса зависимостей, тем сложнее понять его роль, сложнее тестировать и использовать повторно. Также это увеличивает вероятность поломки класса при изменении зависимостей. Зависимости от простых к сложным:
- примитивные типы;
- структуры и неизменяемые пользовательские типы;
- объекты со стабильным интерфейсом и поведением (поведение которых не зависит от внешнего окружения);
- объекты с изменчивыми интерфейсом и поведением (типы на стыке модулей, или типы, которые работают с внешним окружением: файлами, базами данных, сокетами и т.п.).
Не следует путать принцип разделения интерфейса (ISP) с принципом единственной обязанности (SRP). Если класс или модуль отвечает за выполнение разнородных задач, то он нарушает принцип SRP. Но можем ли мы, глядя на класс или его интерфейс, сказать, что он нарушает он принцип ISP?
Например, класс репозитория, который содержит CRUD-операции. Нарушает ли он ISP? Мы не знаем! Нарушение этого принципа зависит не столько от самого класса, сколько от сценариев его использования. Если в нашей бизнес-модели четко разделяются операции чтения и обновления данных, то наличие одного класса со всеми операциями работы с данными однозначно нарушает ISP. В то же время если приложение содержит множество простых форм, которые 1 к 1 с поставщиками данных, то принцип ISP не нарушается.
Таким образом, следование принципу единственной обязанности приводит к связным (cohesive) классам, что позволяет с меньшими усилиями их понимать и развивать. Следование принципу разделения интерфейсов уменьшает связанность (coupling) между классами и их клиентами, чтобы клиенты использовали более простые зависимости.
Типичные примеры нарушения ISP
- Метод принимает в качестве аргумента производный класс, хотя достаточно использовать базовый.
- У класса два или более ярко выраженных вида клиентов.
- Класс зависит от более сложной зависимости, чем нужно: принимает интерфейс провайдера вместо результатов его работы и т. п.
- Класс зависит от сложного интерфейса, что делает его зависимым от всех типов, используемых в этом интерфейсе.
Лишь по исходному коду класса или его интерфейса мы не можем судить о том, нарушает он принцип разделения интерфейсов или нет. Для этого нужно посмотреть контекст его использования. Если класс используется разными клиентами, это может говорить о слишком большом числе обязанностей, поэтому его нужно упростить. В некоторых случаях у класса может быть одна обязанность, которая рассматривается клиентами с разных точек зрения. Тогда это нужно выразить явно путем реализации двух или более интерфейсов.
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 20.
Post #567
1.06K