понедельник, 30 января 2017 г.

Абстрактный код

Как абстракции высокого уровня могут помочь в борьбе со сложностью
или почему модуль, похожий на "трубопроводный узел" чрезвычайно хорош?

В этом посте речь пойдет о важности абстракций при разработке сложных систем. Сперва небольшой оффтоп: зачем вообще нужны абстракции? Часто работать с конкретикой гораздо удобнее, чем с некой абстрактной сущностью, детали которой размазаны и не ясны до конца. Но на самом деле, нет ничего кроме абстракций как в окружающем мире, так и в разработке ПО(тем более в разработке ПО). Вопрос лишь в том, на каком уровне абстракции рассматривается конкретный предмет. Уровней абстракций может быть бесконечно много. Все это звучит слишком аБсТрАкТнО, так что пора привести несколько примеров. 

Взять к примеру, растение. Каждому ясен смысл этой абстракции, и у каждого в голове мелькнет картинка с элементами зеленого цвета и мысли о фотосинтезе, но детали будут отличаться. Но вопрос не в этом. Вопрос в том, какими уровнями абстракций обладает объект "растение". И эти уровни будут зависеть от цели. Если цель - "полить растение", то абстракция "растение" останется практически неизменной, если же "исследовать полезные свойства стволовых клеток", то абстракция "растение" растворится в изобилии деталей клеточного уровня абстракций. Между этими двумя уровнями, опять же, бесконечность других - все зависит лишь от того, какой уровень позволит достичь той или иной цели максимально эффективно.

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

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



Вот, что позволяет сделать абстракция (класс, интерфейс - не важно):


Обратная стрелка здесь - это реализация конкретной абстракции (имплементация интерфейса, наследование от абстрактного класса). А облако - сама абстракция. Обратите внимание, что изначальная связь как бы разорвалась.

Таким образом количество зависимостей становится равным не количеству всех стрелок(18) в иерархии, а только количеству стрелок от модуля к абстракциям(3). А сложность модуля(системы) и определяется именно количеством таких зависимостей.

Теперь модуль можно рассматривать обособленно от всей остальной системы, держа в уме лишь зависимости от абстракций:


Это напоминает устройство компьютерного железа(или водопроводного узла) - слот(абстракция) один, а конкретных объектов, подходящих к слоту - сколь угодно много. А все потому, что на уровне железа умные люди уже давно реализовали все зависимости на уровне абстракций. Детали для механических устройств(самолетов, автомобилей) - тоже абстракции. Но вот при построении сложной системы большинство специфичных абстракций приходится изобретать архитектору почти с нуля.

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

Таким образом, управление абстракциями - это управление сложностью системы.


Комментариев нет:

Отправить комментарий