Pull to refresh

Comments 22

Кхм. Вы предлагаете во втором совете писать абстракции от всех используемых фреймворков? Например (условно) возьмём Qt в качестве фреймворка. Так ведь чисто из-за того, что мы будем его использовать он быстро полезет изо всех щелей! И хрен мы его просто заменим. Либо придётся ваять абстракции чуть ли не на каждый чих.

Или я что-то неверно понял?
Идея в том, что хорошо иметь четкие уровни в архитектуре. И если вы используете что-то на UI, то это не значит, что доступ к базе данных или какая-то логика ядра системы должны зависить и вообще знать о существовании UI. Разрабатывая каждый уровень, все остальные компоненты системы, на других уровнях должны представляться абстракциями, почти черными ящиками с моделью «с этого уровня я буду дергать DAO за эти методы и получать вот такие данные».

Конечно, это все в идеале. Иногда проще, и к этому идут многие фреймворки для rapid development, довольно сильно сделать coupling уровней, примерно как в одном месте мы и из базы почитали и там же и view сгенерировали. Но стоит понимать намечающюся задницу, когда требования поменяются с «зеленых крокодилов» на «синие холодильники»
минимизация связей между компонентами (низкая связанность, low cohesion).

Наверное, loose coupling, имелось ввиду.
Да, конечно, очепятался.

На мой взгляд:

Наиболее плохо поддаются проектированию приложения, где главными компонентами выступают GUI и БД. Приложения баз данных трудно спроектировать так, чтобы их конечная архитектура была достаточно гибкой, — на это потребуется не одна мажорная версия кода. Тут проблема в изменчивости (БД), трудности тестирования (GUI) и местечковости (Business Logic + GUI + БД).

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

Хорошо проектируются приложения, в которых много компонентов, но все они достаточно равноправны. Это, например, компьютерные игры, какие-нибудь редакторы. И хотя на архитектуру придется потратить достаточно много усилий, но так как один ни компонент не будет перетягивать одеяло на себя, риски перекосов ощутимо сглаживаются.
Есть два подхода к проектированию ПО. Один из них называется «outside-in approach», описанный в частности в книге “Growing Object Oriented Software”. В нем предлагается начать разработку с UI и двигаться в сторону бизнес-логики. Есть и другой подход, который называется «inside-out», в котором проектирование начинается с бизнес-логики и уже от нее двигаться в сторону UI и базы данных.

Возможно есть приложения, в которых второй подход не работает, что-то типа сверх тонких клиентов, но в большинстве случаев, подход inside-out будет прекрасно работать. Да, проектированием UI приложения двигает именно UI, но даже если брать разработку сложного UI-приложения, то подход inside-out все еще возможен: бизнес-аналитик, архитектор, заказчик определяет желаемое поведение UI-й части. Затем, с помощью MVXXX паттернов дербанится на части представление и логика представления; после чего разрабатывается именно логика представления на которую уже натягивается сама вьюха.

В общем, большинство приложений нормально проектируются, хотя, конечно, некоторые типы приложений могут сопротивляться этому сильнее остальных
Есть два подхода к проектированию ПО. Один из них называется «outside-in approach», описанный в частности в книге “Growing
Object Oriented Software”. В нем предлагается начать разработку с UI и двигаться в сторону бизнес-логики. Есть и другой подход, который называется «inside-out», в котором проектирование начинается с бизнес-логики и уже от нее двигаться в сторону UI и базы данных.
___
Т.е. все то у чего нету UI ПО не является чтоли?
Смотря что под UI понимать. Иногда это набор опций командной строки, формат конфигурационного файла, или даже формат ввода/вывода. Не могу вспомнить какое-либо прикладное ПО вообще без интерфейса.
Рано или поздно UI на каком ни будь из концов всплывает, другое дело, что это может быть за пределами разрабатываемой системы, тогда речь идет о программных интерфейсах.

Это разновидность функционального подхода — определяем что на входе и на выходе как более или менее данность, выводим из этого то, что в центре.
Вариант «outside-in approach» хорошо описан в Getting Real от 37signals и он отлично работает для проектов с тонкой моделью. Но для продуктов со сложной бизнес-логикой его использовать затруднительно.
«где главными компонентами выступают GUI и БД» — это справедливо почти для любого веб-приложения. А они вполне хорошо проектируется и покрываются тестами (как модульными, так и функциональными), особенно если используют MVC.
В каком-то софтварном журнале читал такое определение архитектуры: это то, что мы хотим сделать правильно с самого начала.
Т.е. по идее это такие вещи, которые после начала кодирования изменить сложно.
Итого, чтобы получить близкую к идеальной систему, надо хорошо продумать базовые компоненты (используемые фреймворки, интернализацию, логирования, слои) и на базе этих компонентов с помощью TDD строить оставшееся. Таким образом сложно изменяемые части скорее всего будут сделаны правильно от начала, а остальное через TDD с большей вероятностью будет сделано также качественно.
Полностью согласен с применением тестов в качестве «лакмусовой бумажки». Это прямой путь к защите слоев архитектуры от взаимопроникновения и к минимизации связности компонентов системы.
Лично мне досталась в наследство некая система с интегрированным скриптовым языком. Так вот, она была настолько монолитна, что бизнес-логика, которая по задумке должна была быть реализована скриптами, дублировалась. Фактически, было написано две параллельные системы, в скриптах и в бинарнике, именно из-за жесткой связки бизнес-логики, логики работы с БД и GUI. За три года поддержки я смог «расцепить» UI и бизнес лишь частично, и то благодаря тем самым бесконечным тестам.
В то же время новый функционал подмешивался в виде автономных (ну, почти) модулей, которые безо всяких изменений могли работать и как отдельное тестовое приложение, и как часть системы. При разработке тестируемость была основными критерием.
Итог: подход работает. В новой части изменения проходят легко и непринужденно, в старой — каждый раз кровавое месиво. Конечно, в моем случае имеет место постепенное перепроектирование системы, а не разработка с нуля, но не думаю, что это принципиально.
А по поводу разработки архитектуры имею удовольствие доложить: очень многое, чуть ли не всё, зависит от хорошо продуманных программных интерфейсов. Даже самую мерзкую пакость можно обернуть приятным API и жить спокойно, при условии автономности оной пакости, разумеется.
В таких случаях рекомендуют потратить время и зафиксировать поведение хотя бы наиболее критичных участков системы функциональными тестами. Это позволит сделать месиво менее кровавым :)
Ещё бы добавил, что «правильный подход к проектированию» даёт ещё один значимый бонус, о котором часто забывают: масштабируемость процесса разработки. Т.е. грубо говоря — возможность сократить срок разработки за счёт добавления к нему людей. Это происходит за счёт простой вещи: наше ПО это 100500 микро-ситуаций «клиент»->«сервис». И если «сервис» будет не конкретным классом а интерфейсом, то разделить работу между 2 сотрудниками: одному разрабатывать «клиент»и тестироваться с помощью мока сервиса, а другому — разрабатывать реализацию сервиса и тестировать его тестом на основ контракта. Ясно что скорость увеличится не в 2 раза из-за необходимости дополнительно создать 2 комплекта для тестирования, но если в команде и так пишутся автотесты — то не так важно. Так же очевидно, что эффективность такого подхода тем выше, чем ближе наш клиент->сервис к ситуации взаимодействия 2х крупных компонент.
Хороший подход, на мой взгляд, а двух компектов тестов для приложений с пользовательским интерфейсом все равно не избежать.
Вставлю свои 5 копеек. Основанные на личных фантазиях в меру далекого от программирования человека.

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

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

Тоесть, если риск ошибки (человеческой, программной etc) в одном модуле равен 0.1% (мало, правда),
то в случае если у нас 100 таких модулей, то риск всей системы возрастет до 10%.

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

Я к этому пришел через абсолютно экономические науки. Даже напишу статью об этом, так как оно затрагивает очень много чего в мире IT. Такое проектирование систем это частное применение одной большей современной эконом. теории.

И вот, гуру программирования подтверждает что это работает на практике и так должно быть. Только пришел к этому с другой стороны

* философски *

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

Достаточно много авторов уже писали в этом ключе: Дейкстра, Брукс, Грэм и т.д., но, слава богу, еще много неохваченных тем и есть что сказать.
Интересно. Ищу только где на вас можно подписаться (:
В свое время я писал достаточно обширную заметку по типам связанности, и всяких тонких нюансах, например в нетипизованных языках любой параметр при вызове метода стоит воспринимать как более широкий канал для образования связанности. Отдельная тема — мониторинг связанности через глобалы и god-классы. В таком случае все, например, может прекрасно тестироваться пока не был затронут god-класс или глобалсы, а после их изменения вылететь в трубу.

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

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

Cohension я перевожу как «сопряженность», больше смысловой нагрузки, как бы все части класса или подсистемы работают в одной упряжи. А сцепленность и связанность без шпаргалки и при размышлениях просто перепутать.
Sign up to leave a comment.

Articles