tag:blogger.com,1999:blog-7813008339240224582024-03-05T03:48:39.374-08:00Размышления о разработке сложных системAnonymoushttp://www.blogger.com/profile/00371996927894464838noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-781300833924022458.post-71137339686845311042017-02-02T19:17:00.002-08:002017-02-02T19:38:19.390-08:00"Контуры" сложности<div dir="ltr" style="text-align: left;" trbidi="on">
<h5>
Как понятие "сложность" может мигрировать из электрофизики<br />
или почему наложение контуров сложности друг на друга создает много дополнительных проблем?<br />
</h5>
<br />
В данном посте будет затронута тема, напрямую связанная с темой предыдущего поста - борьбой с зависимостями. Но с зависимостями в более узком смысле - с зависимостями на уровне отдельных модулей.<br />
<br />
Что такое "модуль"? Модулем обычно называют логически независимую и практически самодостаточную часть системы. Например, функционалы печати и управления БД, в совершенстве, находятся в двух обособленных друг от друга модулях. Здесь уместна аналогия с компьютерным железом - модуль ОЗУ, модуль видеокарты, сетевой карты и т.д. Аналогия хороша потому, что отчетливо демонстрирует "независимость" компонентов друг от друга. Так, любой современный модуль ОЗУ, хоть и подчиняется множеству конвенций и контрактов проектирования, принятым на уровне "железных" производителей всего мира, но, тем не менее, является относительно независимой частью, с которой все остальное оборудование работает по принятому контракту, прося<i>(отсылка к статье, предшествующей предыдущей)</i> ОЗУ(или любой другой похожий модуль) через шину данных об осуществлении каких-либо операций. О деталях же думает сам аппаратный модуль на своей внутренней кухне.<br />
<br />
Программный модуль, как ни странно, является абсолютной аналогией аппаратного модуля ОЗУ в "мягком" мире. Но важно сказать, что таким свойством обладает лишь хорошо спроектированный программный модуль, потому как нередко каскадные зависимости ломают границы обособленности, по началу, казалось бы, совершенно независимых модулей.<br />
<br />
Итак, что такое "контур сложности"? Предположим, у нас имеется некоторая сложная система, занимающаяся анализом вредоносного траффика.<br />
Вот ее часть:<br />
<br />
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidBevjv-5oj_NEwKs03RJMN_CHwrh-VIzutLd-y-r1NsQC29g5UA23w_Zyo5ybu5YGbMPoDkEEe_yhBWJxog9DVU0ZFjmkQIVYHTS6poS1gqNEoFHhhq47Zlq66F9TamU08hVXopyeWwNC/s1600/ip.png" imageanchor="1"><img border="0" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidBevjv-5oj_NEwKs03RJMN_CHwrh-VIzutLd-y-r1NsQC29g5UA23w_Zyo5ybu5YGbMPoDkEEe_yhBWJxog9DVU0ZFjmkQIVYHTS6poS1gqNEoFHhhq47Zlq66F9TamU08hVXopyeWwNC/s320/ip.png" width="320" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Контур здесь - это синий квадрат.<br />
Пробежавшись по названиям внутренних классов, можно увидеть примерные назначения модулей. </div>
<div style="text-align: left;">
Как можно заметить, пока что все близко к идеалу - зависимости инкапсулированы внутри модулей и в итоге образовались целостные модули без иерархичных зависимостей.<br />
<br /></div>
<div style="text-align: left;">
<b>Это модель, к которой всегда надо стремиться</b> и не выпускать из ума на протяжении всего процесса разработки архитектуры. Каждый раз, когда вы добавляете в свою систему новый элемент(класс), вступающий в зависимости с остальными частями системы, думайте о том, <b>как минимизировать эти зависимости</b> и инкапсулировать их в рамках единственного модуля(в совершенстве), поместив в наиболее правильное место.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
В процессе дальнейшего развития системы может произойти следующее:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDfYMlzg9-ewpiL3_pOYKpkdSbPXOhaNFNAktdQ_UB8NiBCgUWX_Y3ZMn-2BrGEQ6g0JA1yQbIPbJ1RdUzb7YHEgQTkK1PzACRJA6atAIqO3N_3YSi-BZdw_JlyLL4dLWKYbvFIAAeiwxu/s1600/ip+%25281%2529.png" imageanchor="1"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDfYMlzg9-ewpiL3_pOYKpkdSbPXOhaNFNAktdQ_UB8NiBCgUWX_Y3ZMn-2BrGEQ6g0JA1yQbIPbJ1RdUzb7YHEgQTkK1PzACRJA6atAIqO3N_3YSi-BZdw_JlyLL4dLWKYbvFIAAeiwxu/s320/ip+%25281%2529.png" width="256" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
или так:</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXIAzhsV293yPAzHiwKiv9bSu7yMz38QIacvPE1Jc5QcZa0em5Tzrok9eCtMhIv-Ul7VJaH8_LQ5e7F0Aflr9un8efUDWVVES1JzQeeicDde1mkzkTegTpjiOn9QmOdaVUz3L8mL8IQwnh/s1600/ip+%25282%2529.png" imageanchor="1"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXIAzhsV293yPAzHiwKiv9bSu7yMz38QIacvPE1Jc5QcZa0em5Tzrok9eCtMhIv-Ul7VJaH8_LQ5e7F0Aflr9un8efUDWVVES1JzQeeicDde1mkzkTegTpjiOn9QmOdaVUz3L8mL8IQwnh/s320/ip+%25282%2529.png" width="304" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Добавился новый класс, который <u>тянет иерархию зависимостей</u> соседнего модуля.<br />
Дело в том, что появились и новые контуры, перекрывающиеся со старыми - появились межмодульные зависимости. Что в этом плохого? Как минимум то, что модуль снизу теперь не формирует целостной абстракции - он зависит от внутренней кухни другого модуля, а значит использовать его независимо и обособленно уже не получится - не получится переиспользовать.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Возникает логичный вопрос: каким размером должен обладать идеальный модуль(сколько атомарных классов)? К сожалению, я не в силах дать ответ на этот вопрос, потому что его нет. Все зависит от того, какого уровня абстракции в вашей системе принято называть атомарными. Если они слабо похожи на абстракции и с ними нельзя работать как с абстракциями(просить что-либо сделать), то единым модулем вскоре может стать и вся система! Что плохо. </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<b>Как с этим бороться? </b>Ответ на этот вопрос уже частично присутствует в предыдущем посте, но лучше повториться. </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<b>Ответ: </b>следует использовать интерфейсное(абстрактное) проектирование, чтобы модули, зависимостей между которыми не устранить, превращались в <b>"водопроводные узлы"</b>.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Иллюстрация исправления ситуации с зависимостями выше:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4pPaK2hEV4z2gntxJHfoySq_jKGjGVUKX6rgxNIopMLzzocYZM_3fBFo0RY9n6-lsuLTjtiyoYGclfryl7BjxTIgHBGEZE2qbYSskMohXJlALtc3e8GHqAKVJoTRPt-1uljVPXWeNXM-A/s1600/ip+%25283%2529.png" imageanchor="1"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4pPaK2hEV4z2gntxJHfoySq_jKGjGVUKX6rgxNIopMLzzocYZM_3fBFo0RY9n6-lsuLTjtiyoYGclfryl7BjxTIgHBGEZE2qbYSskMohXJlALtc3e8GHqAKVJoTRPt-1uljVPXWeNXM-A/s320/ip+%25283%2529.png" width="235" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Теперь TrafficTable зависит от абстракции AbstractTraffic, а элемент ConcreteTraffic из другого модуля реализует эту абстракцию так, <u>как удобно его внутренней кухне</u>. В итоге все довольны.</div>
<div style="text-align: left;">
Зависимый модуль(нижний) <b>зависит от контракта</b>, а реализующий(верхний) - реализует этот контракт. О контрактном программировании мы еще как-нибудь поговорим.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Теперь нижний модуль <b>выглядит как деталь</b>, готовая к использованию(встраиванию):</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5nEH8vcKsoH0CUQRty04uZidWeKYHRrOkOeRGJ4uygJXugC3N4SInz19Ymq1yck24JmUnerAWJMkmpthQ3zbiOOg2zwcBDVx-X4ztihMGUsCluirQPfMv_6mqZ3su1MKdhydygeEyZSl6/s1600/ip+%25284%2529.png" imageanchor="1"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5nEH8vcKsoH0CUQRty04uZidWeKYHRrOkOeRGJ4uygJXugC3N4SInz19Ymq1yck24JmUnerAWJMkmpthQ3zbiOOg2zwcBDVx-X4ztihMGUsCluirQPfMv_6mqZ3su1MKdhydygeEyZSl6/s320/ip+%25284%2529.png" width="244" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<b>Побочным эффектом</b> устранения таких "межмодульных" контуров зависимости является то, что со временем появляется слишком много продуктов контрактного проектирования - абстрактных классов и интерфейсов. Но если вспомнить про окружающий мир, то это нормально(примеры приводить лень - пост уже и так разросся словно какая-то сложная система ;)).</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
P.S <b>Важно! </b>Лично у меня описанный подход получалось применять в полной мере только тогда, когда все тонкости учитывались с самого начала, т.е на этапе разработки архитектуры системы. Иными словами, взять и внедрить его в середине проекта, который игнорировал абстракции все предыдущее время будет очень сложно - слишком большой объем кода придется рефакторить.<br />
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Удачных вам абстракций!</div>
<div style="text-align: left;">
(ваш мастер абстракций)</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/00371996927894464838noreply@blogger.com0tag:blogger.com,1999:blog-781300833924022458.post-42745674635620363222017-01-30T08:13:00.001-08:002017-01-30T11:07:47.048-08:00Абстрактный код<div dir="ltr" style="text-align: left;" trbidi="on">
<h5>
Как абстракции высокого уровня могут помочь в борьбе со сложностью<br />
<div>
или почему модуль, похожий на "трубопроводный узел" чрезвычайно хорош?</div>
</h5>
<div>
<br /></div>
<div>
В этом посте речь пойдет о важности абстракций при разработке сложных систем. Сперва небольшой оффтоп: <b>зачем вообще нужны абстракции?</b> Часто работать с конкретикой гораздо удобнее, чем с некой абстрактной сущностью, детали которой размазаны и не ясны до конца. Но на самом деле, нет ничего кроме абстракций как в окружающем мире, так и в разработке ПО(тем более в разработке ПО). Вопрос лишь в том, на каком уровне абстракции рассматривается конкретный предмет. <b>Уровней абстракций может быть бесконечно много.</b> Все это звучит слишком аБсТрАкТнО, так что пора привести несколько примеров. </div>
<div>
<br /></div>
<div>
Взять к примеру, растение. Каждому ясен смысл этой абстракции, и у каждого в голове мелькнет картинка с элементами зеленого цвета и мысли о фотосинтезе, но детали будут отличаться. Но вопрос не в этом. Вопрос в том, какими уровнями абстракций обладает объект "растение". И эти уровни будут зависеть от цели. Если цель - "полить растение", то абстракция "растение" останется практически неизменной, если же "исследовать полезные свойства стволовых клеток", то абстракция "растение" растворится в изобилии деталей клеточного уровня абстракций. Между этими двумя уровнями, опять же, бесконечность других - <b>все зависит лишь от того, какой уровень позволит достичь той или иной цели максимально эффективно.</b></div>
<div>
<br /></div>
<div>
Теперь пора возвратиться к абстракциям, помогающим разрабатывать сложные системы. "Растения" там заменяются программными модулями. И все же еще не до конца ясно, как абстракции могут помочь. В дальнейшем под абстракцией я буду понимать интерфейсы и абстрактные классы.</div>
<div>
<br /></div>
<div>
Главная задача, которую выполняют абстракции - это борьба с множественными зависимостями. Вот как выглядит типичный модуль сложной системы со всеми своими зависимостями:</div>
<div>
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUiX-xCUrnlGFYxoeirHuGNGIrgFX-pGmlW7mcAPQkVzTT64AOj1wvSNLddnqZSKiEDGH1Tu6mc-9xHY66DZfYZnVQbe6l4TQinJldGRwtTUJlkd6orCn75O6nuW1ZWvvEWn7HCpQJo1cZ/s1600/module.png" imageanchor="1"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUiX-xCUrnlGFYxoeirHuGNGIrgFX-pGmlW7mcAPQkVzTT64AOj1wvSNLddnqZSKiEDGH1Tu6mc-9xHY66DZfYZnVQbe6l4TQinJldGRwtTUJlkd6orCn75O6nuW1ZWvvEWn7HCpQJo1cZ/s320/module.png" width="320" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Вот, что позволяет сделать абстракция (класс, интерфейс - не важно):</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLqGo3lY5mNuPu_LZUHjLr70tV-jpzNwHOzg5R2Idjt1gu33dn9y3Rc2pk2zNHYfRAzGV2RBwwiFogEy1i6ueV9IOWpH1VC4PGlA4-PRsXXrwYthM9AFY4TvVdNYkI-CSVVNXB6peDI0TI/s1600/module+%25281%2529.png" imageanchor="1"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLqGo3lY5mNuPu_LZUHjLr70tV-jpzNwHOzg5R2Idjt1gu33dn9y3Rc2pk2zNHYfRAzGV2RBwwiFogEy1i6ueV9IOWpH1VC4PGlA4-PRsXXrwYthM9AFY4TvVdNYkI-CSVVNXB6peDI0TI/s320/module+%25281%2529.png" width="320" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Обратная стрелка здесь - это реализация конкретной абстракции (имплементация интерфейса, наследование от абстрактного класса). А облако - сама абстракция. Обратите внимание, что изначальная связь как бы разорвалась.<br />
<br />
Таким образом количество зависимостей становится равным не количеству всех стрелок(18) в иерархии, а только количеству стрелок от модуля к абстракциям(3). А сложность модуля(системы) и определяется именно количеством таких зависимостей.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Теперь модуль можно рассматривать обособленно от всей остальной системы, держа в уме лишь зависимости от абстракций:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHYUKdsE9fI8EWXdDTFiaNj4gMZtF9ShAukoM1oSxRFb-x0-lxkWd6FnKyVjktqsrUrR9iEoq05r7OIJ9UkkEzmXs2bIdBAL8wQ_alvN0JEcz4Iro-shas4Lrle1a7KjJLzSvR_7ZNlmI1/s1600/module+%25282%2529.png" imageanchor="1"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHYUKdsE9fI8EWXdDTFiaNj4gMZtF9ShAukoM1oSxRFb-x0-lxkWd6FnKyVjktqsrUrR9iEoq05r7OIJ9UkkEzmXs2bIdBAL8wQ_alvN0JEcz4Iro-shas4Lrle1a7KjJLzSvR_7ZNlmI1/s320/module+%25282%2529.png" width="320" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Это напоминает устройство компьютерного железа(или водопроводного узла) - слот(абстракция) один, а конкретных объектов, подходящих к слоту - сколь угодно много. А все потому, что на уровне железа умные люди уже давно реализовали все зависимости на уровне абстракций. Детали для механических устройств(самолетов, автомобилей) - тоже абстракции. Но вот при построении сложной системы большинство специфичных абстракций приходится изобретать архитектору почти с нуля.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
В общем, <b>программируйте на уровне интерфейсов</b> и зависимостей между ними и лишь потом переходите к разработке специфичных деталей. Может показаться, что это работа чисто архитектора, но на самом деле архитектура и кодирование неразрывно связаны, так что слепо писать код получается редко. </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<b>Таким образом, управление абстракциями - это управление сложностью системы.</b></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/00371996927894464838noreply@blogger.com0tag:blogger.com,1999:blog-781300833924022458.post-73030569030471465202017-01-25T06:34:00.003-08:002017-01-26T14:58:48.441-08:00Использование "черных ящиков" в борьбе со сложностью<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: center;">
<div style="text-align: left;">
<h2 style="text-align: left;">
</h2>
<h6>
Почему "черные ящики" - это хорошо.<br />Или в чем суть инкапсуляции.
</h6>
</div>
</h2>
В этом небольшом сообщении я хотел бы рассмотреть проблему сокрытия данных в масштабе крупной системы. Причем под "сокрытием" я понимаю не использование приватных полей/методов, а сокрытие логики, сокрытие того "как" что-то делается.<br />
<br />
Если вы можете провести точную грань между тем "что" и "как", то это хорошая предпосылка создания качественного интерфейса, формирующего целостную абстракцию (о важности абстракций мы еще как-нибудь поговорим).<br />
<br />
Почему вообще важен качественный интерфейс? Качественный интерфейс - это сигнал о том, что для конкретного элемента вашей сложной системы вы грамотно и, скорее всего, правильно определили область ответственности. Элемент(класс, если угодно, хотя и необязательно) делает одну вещь и делает это хорошо - вот это истинная цель, которая достойна преследования.<br />
<br />
Но вернемся к тому "что" и "как". Именно это разделение открывает перед разработчиком компонента системы возможности сокрытия всех деталей, оставляя пользователю элемента лишь вкусную абстракцию, с которой приятно работать. Иными словами,<b> "что" инкапсулирует все шаги, известные "как"</b>.<br />
<br />
Но здесь есть один нюансик. Нередко, особенно на начальных порах, возникает иллюзия, что вы нашли границу между "что" и "как". Эта иллюзия в самом худшем случае приводит к появлению <b>множественных геттеров</b>(гет-методы) для защищенных данных(приватных полей) объекта.<br />
<br />
<b>Почему это плохо? </b><br />
Представьте себе: вы пришли в кабинет к стоматологу, который обладает всем необходимым для качественного лечения зуба. Но вместо того, чтобы садиться в удобное(хотя не всегда) кресло и не думать о том, что происходит в вашей ротовой полости, вы просите у стоматолога все инструменты, которые только можно запросить(через гет-методы), после чего стоматолог сам по себе(пошел пить чай), а вы с грудой инструментов и больным зубом - сами по себе.<br />
Ситуация не из приятных. Было бы гораздо удобнее "попросить" стоматолога все сделать за вас, не вникая в детали его работы, ведь он в этой сфере мастер.<br />
<br />
Именно таким образом в сложной системе и стоит создавать связи между слабозависимыми компонентами - <i>один должен просить другого </i>"что"-либо сделать, не вникая в детали того, "как" он это сделает, ведь главное - результат(а не участие в процессе).<br />
<br />
В дальнейшем инкапсуляция деталей(информационных полей и большинства методов) поможет добавлять объектам новую ответственность, менять и улучшать старую, оставаясь при этом в пределах ответственного объекта.<br />
Вы можете прийти к стоматологу, и он полечит ваш зуб разными способами, которым он научился где-то и как-то, но ваш запрос будет при этом оставаться неизменным - "полечите мне зуб!".<br />
<br />
Грамотная инкапсуляция имеет мало общего с приватными и защищенными спецификаторами на уровне конкретных языков. Поэтому используйте инкапсуляцию грамотно.</div>
Anonymoushttp://www.blogger.com/profile/00371996927894464838noreply@blogger.com0