Interface Segregation Principle. Принцип разделения интерфейсов в проектировании приложений::Журнал СА
www.samag.ru
Журнал «БИТ. Бизнес&Информационные технологии»      
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
1001 и 1 книга  
19.03.2018г.
Просмотров: 6995
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 7447
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4848
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3191
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3990
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3992
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6489
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3342
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3617
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7478
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10846
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12552
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14275
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9291
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7245
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5541
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4771
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3591
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3297
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3529
Комментарии: 1
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3182
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Interface Segregation Principle. Принцип разделения интерфейсов в проектировании приложений

Архив номеров / 2024 / Выпуск №4 (257) / Interface Segregation Principle. Принцип разделения интерфейсов в проектировании приложений

Рубрика: Разработка /  Принципы проектирования

 ВИЗИТКА 



Ольга Федорова,
технический лидер «Альфа банка»

 

Interface Segregation Principle
Принцип разделения интерфейсов в проектировании приложений


Эта статья из серии «SOLID» посвящена четвертому принципу проектирования приложений – Interface Segregation или «разделения интерфейсов». Как обычно, начнем с оригинальной формулировки Роберта Мартина, которая звучит следующим образом: программные сущности не должны зависеть от методов, которые они не используют.

 

Чтобы это продемонстрировать, я хочу обратиться к реализации коллекции в языке Java. Как минимум, потому что это хороший пример того, что даже на уровне языков инженерные принципы не всегда реализованы должным образом и не надо относится к системным библиотекам как к идеалу инженерной индустрии. Знатоки JVM стека могут мне возразить, что для такой реализации были свои исторические причины, я не буду с ними спорить, но давайте посмотрим, что из этого вышло.

Для тех, кто не знаком с Java, я прикладываю картинку со структурой класса Collections, в принципе, для общего понимания его достаточно.

 

Итак, вы разрабатываете библиотеку, которая отправляет сообщения через TCP- сеть (разумеется, с использованием того, что выше, это же базовые возможности языка). Она достаточно умная, чтобы отправлять их ассинхронно, согласно приоритету, и накапливать смс в буфер, если все сразу не пролезли в сеть. Соответственно, при добавлении СМС в очередь у вас достаточно много логики, вы должны это дело залогировать, найти правильное место в очереди согласно приоритетам и т.д. Все работает, все счастливы.

В один непрекрасный день вы сталкиваетесь с необходимостью дать клиентскому коду возможность пробежаться по СМС, стоящим в очереди. Конечно же, вы не можете допустить чтобы кто-то модифицировал состояние коллекции, поэтому внутри библиотеки тихонько вызывается Collection.unmodifiedSet(), а вот уже результат вы отдаете клиентскому коду.

Что делает клиентский код? Видит метод add и воодушевленно вызывает его. Не всегда обоснованно, зачастую просто потому, что «почему бы и нет?». Однако, вместо ожидаемого результата, он встретит ошибку.

Здесь мы и вспоминаем оригинальную формулировку Interface Segregation. Суть заключается в том, чтобы клиентский код зависел только от тех частей системы, которые он реально использует. Если клиентскому коду не требуется использовать какие-то функции или возможности, то лучше вообще не предоставлять ему доступ к ним. Это позволяет избежать ненужных зависимостей и сделать систему более гибкой и поддерживаемой.

Рассмотрим другой пример и тоже из Java. Дабы у вас не сложилось предвзятое отношение к этому языку, сразу оговорюсь, что такие проблемы есть и в C#, и в других языках. Итак, интерфейс Collection:

 

Какие недостатки есть у исходного интерфейса? Как минимум, его можно было бы разделить на несколько более специализированных интерфейсов. Например:

  • методы модификации коллекции: add, addAll, clear и т.д.;
  • методы итерации и доступа: iterator, isEmpty и пр.;
  • конвертеры toArray, toStream;
  • наследование java object.

Это и было сделано, в частности, в языке Kotlin, который, хоть и является в общественном сознании в некотором роде синтаксическим сахаром для Java, кратно улучшил ряд концепций исходного языка. 

Давайте рассмотрим другой пример. Предположим, у нас есть интерфейс Payment с определенным набором методов. Кто-то реализует этот интерфейс, и, на первый взгляд, вся эта схема выглядит неплохо.

public interface Payment {
         
void initiatePayments();
          Object
status();
          List<Object>
getPayments();
}

public class BankPayment implements Payment {
          @Override
          public void initiatePayments() {
                   //
реализация
          }

          @Override
          public Object status() {
                   //
реализация
          }

          @Override
          public List<Object> getPayments() {
                  
// реализация
          }
}


Предположим, по мере развития программы, в какой-то момент появляется LoanPayment со своими методами, весьма похожими на те, что уже есть в Payment, за исключением небольшого отличия. И в целом, на первый взгляд, можно согласится на то, чтобы класс имплементировал интерфейс, хотя наличие Unsupported Operation уже вызывает сомнения.

public class LoanPayment implements Payment {
          @Override
          public void initiatePayments() {
                  
throw new UnsupportedOperationException("This is not
a bank payment")
;
          }

          @Override
          public Object status() {
                   //
реализация
          }

          @Override
          public List<Object> getPayments() {
                  
// реализация

          }
}

Продукт продолжает эволюционировать и внезапно возникает необходимость расширить оригинальный интерфейс Payment, добавив к нему еще два дополнительных метода.

public interface Payment {
         
void initiatePayments();

          Object
status();

          List<Object>
getPayments();

         
void intiateLoanSettlement();

         
void initiateRePayment();
}

Теперь у нас возникает неприятная ситуация: если раньше банковский платеж не поддерживал только один из оригинальных трех методов, то теперь он не поддерживает еще и новые методы интерфейса Payment.

public class BankPayment implements Payment {
          @Override
          public void initiatePayments() {
                   //
реализация
          }

          @Override
          public Object status() {
                   //
реализация
          }

          @Override
          public List<Object> getPayments() {
                  
// реализация
          }

          //
Новые методы, добавленные к интерфейсу Payment
          @Override
          public void intiateLoanSettlement() {
                  
throw new UnsupportedOperationException("This is not
a loan payment"
);
          }

          @Override
          public void initiateRePayment() {
                  
throw new UnsupportedOperationException("This is not
a
loan payment");
          }

}

Это уже делает код очень неуклюжим.

Как мы видели ранее, во-первых, это может привести к неудачным последствиям, когда клиенты кода начинают использовать функциональность, которую они не должны использовать.

Во-вторых, тестировать такой класс становится сложнее даже на уровне юнит-тестов.

В-третьих, это очевидно не финальная версия класса, и по мере развития кодовой базы поддержка такого кода будет только усложняться. 

В данном случае, возможно решить проблему путем наследования и вынесения общих методов в более верхнеуровневый интерфейс, который будет расширен оригинальным интерфейсом.

Кроме того, это подходит под принцип open-closed, о котором мы говорили ранее. Мы не модифицируем существующий код, а расширяем его. В результате код становится более чистым и соблюдает принципы SOLID.

public interface Payment {
          Object
status();
          List<Object>
getPayments();
}

public interface Bank extends Payment {
         
void initiatePayments();
}

public interface Loan extends Payment {
         
void intiateLoanSettlement();
         
void initiateRePayment();
}

Такой подход позволяет избежать проблему неподдерживаемых методов, упрощает тестирование, защищает клиента, который использует код от нежелательных последствий и делает архитектуру более гибкой.

К слову, напомню, что наличие интерфейсов в программе само по себе это хорошая практика, потому что они уменьшают связность кода, что в свою очередь снижает риск превращения продукта в Big Ball of Mud. Да, можно сказать, что большое количество интерфейсов не есть хорошо, потому что это отнимает ресурсы памяти и все такое.

С одной стороны, это правда. Но, с другой стороны, в современном мире мы чаще страдаем не от нехватки железа, а от отсутствия общего понимания происходящего в программе спустя несколько лет активной разработки. Поэтому интерфейсы – это ключевой инструмент в поддержании чистоты и структурированности кода. А правильная организация интерфейсов (в том числе, следование принципу Interface Segregation) – это залог надежной архитектуры приложения.  

  1. Федорова О. Single Responsibility Principle. «Системный администратор». 2024. №1-2.
  2. Федорова О. Open Closed Principle. «Системный администратор». 2024. №1-2.
  3. Федорова О. Принцип подстановки Барбары Лисков, и почему наследующий класс должен дополнять, а не замещать поведение базового класса. «Системный администратор». 2024. №3.



Ключевые слова: SOLID, ISP, Interface Segregation Principle, Роберт Мартин


Подпишитесь на журнал

Комментарии отсутствуют

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

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru