Является ли предпочтительным дизайн сверху вниз или снизу вверх?

31

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

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

Процесс:

  • Создание спецификации требования
  • Создать спецификацию дизайна (с диаграммами)
  • Воплощать в жизнь
  • доставить
  • Повторите (в итеративной разработке, вместо того, чтобы делать целую порцию на каждом этапе, мы выполняем каждый раз по несколько штук и получаем ежедневные встречи, чтобы приспособиться к динамическим требованиям клиента)

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

Другой способ заключается в том, что каждая итерация представляет собой мини-водопад, где анализ выполняется за несколько дней (или неделю). То же самое относится и к дизайну. Остальное время уходит на реализацию. Есть ли что-то не так с подходом сверху вниз в сочетании с итеративной разработкой?

В своем эссе « Программирование снизу вверх» Пол Грэм, кажется, поощряет сборку полностью снизу вверх или программирует ее снизу вверх, но не на этапе анализа требований / проектирования:

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

Насколько я понимаю, он имел в виду, что Лиспер по-прежнему выполняет нисходящее проектирование, но программа снизу вверх, это правда? Еще один момент, он написал:

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

Означает ли это, что в период написания программы на Лиспе вы получаете универсальный инструмент?

Амуму
источник
Возможно, если вы предоставите ссылку на книгу, статью или статью, на которую вы ссылаетесь, другие могут сделать более обоснованное заявление в отношении вашего вопроса. Также неясно, о чем конкретно идет речь в вашем вопросе. Вы спрашиваете совета о некоторых особенностях дизайна, который вы хотите реализовать, или задаете вопросы о статье, на которую вы ссылаетесь. Возможно, это будет легче прочитать и ответить, если разбить его на пару вопросов, задаваемых отдельно.
С.Робинс
@ S.Robins Подводя итог, я продемонстрировал свой обычный подход сверху вниз для разработки программного обеспечения. Однако некоторые люди предпочитают снизу вверх, и одним из них является Пол Грэм, который является известным человеком. Я прошу понять, как дизайн снизу вверх помогает в целом, и особенно в Лиспе, поскольку у него есть особые возможности для поощрения (как предложил Пол).
Amumu
1
Amumu Я отредактировал пример, так как оба ответа, похоже, пропустили его. Пожалуйста, постарайтесь, чтобы ваши вопросы были краткими, и задавайте только один вопрос на каждый вопрос .
Яннис

Ответы:

35

Сверху вниз - отличный способ описать вещи, которые вы знаете, или восстановить вещи, которые вы уже создали.

Сверху вниз самая большая проблема в том, что довольно часто просто нет «вершины». Вы передумаете о том, что должна делать система при разработке системы и при изучении домена. Как может быть ваша отправная точка чего-то, чего вы не знаете (то есть, что вы хотите, чтобы система делала)?

«Локальный» нисходящий - это хорошо ... некоторые мысли об кодировании явно хороши. Но слишком много думать и планировать, потому что то, что вы представляете, не является реальным сценарием (если вы уже не были там раньше, то есть если вы не строите, а восстанавливаете). Глобальный нисходящий при создании новых вещей это просто глупость.

Подход «снизу вверх» должен быть (глобальным) подходом, если вы не знаете 100% проблемы, вам нужно просто закодировать известное решение, и вам не нужно искать возможные альтернативные решения.

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

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

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

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

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

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

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

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

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

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

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

Новые сложные программные системы выращиваются, а не проектируются. Время от времени кто-то начинает с нуля разрабатывать новую сложную плохо определенную программную систему (обратите внимание, что в большом сложном программном проекте есть только три возможности: а) спецификация нечеткая, б] спецификация неправильная и противоречивая. или с] оба ... и чаще всего [с] имеет место).

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

Значит ли это, что я думаю, что вы должны думать только о коде? Конечно нет. Но, по моему мнению, строительство должно начинаться снизу (кирпичи, конкретный код) и должно идти вверх ... и ваше внимание и внимание к деталям должны в некотором смысле «исчезать» по мере того, как вы становитесь дальше от того, что у вас есть. Нисходящий поток часто представляется так, как будто вы должны поместить один и тот же уровень детализации во всю систему сразу: просто делайте это, разделяя каждый узел, пока все не станет очевидным ... в реальных модулях подсистема "выросла" из подпрограмм. Если у вас нет опыта работы с конкретной проблемой, ваш дизайн подсистемы, модуля или библиотеки будет ужасным. Вы можете создать хорошую библиотеку, когда будете знать, какие функции вставлять, а не наоборот.

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

Например, параметры ключевого слова уже присутствуют, но если они отсутствуют, их можно добавить. Я сделал это (включая проверку ключевых слов во время компиляции) для игрушечного компилятора Lisp, с которым я экспериментировал, и он не занимал много кода.

С C ++ вместо этого вы можете получить всего лишь кучу экспертов C ++, которые скажут вам, что параметры ключевых слов не так уж полезны, или невероятно сложную, сломанную, полуобеспеченную реализацию шаблона, которая действительно не так полезна. Являются ли классы C ++ первоклассными объектами? Нет, и с этим ничего не поделаешь. Можете ли вы провести самоанализ во время выполнения или во время компиляции? Нет, и с этим ничего не поделаешь.

Гибкость языка в Лиспе делает его идеальным для построения снизу вверх. Вы можете создавать не только подпрограммы, но также синтаксис и семантику языка. И в каком-то смысле сам Lisp является восходящим.

6502
источник
Да, я имел в виду местный сверху вниз. Вы продолжаете совершенствовать требование и развивать его через итерации. Смысл сверху вниз в том, что мы хотим, чтобы структура кода была максимально правильной. Постоянно рефакторинг. Например, сначала вы хотите передать строку функции, но позже вам потребуется целый класс, чтобы удовлетворить изменения, и рефакторинг отнимает много времени, если функция уже используется повсеместно.
Amumu
Чтобы восстановить что-то, что хорошо известно, ясно известным способом, тогда все в порядке. Ваш веб-браузер является примером этого. Даже использование языка, подобного C ++, который заставляет вас предвидеть и формально указывать (используя ужасный синтаксис) все типы значений, хранящихся во всех переменных вашей программы, все еще остается жизнеспособным вариантом.
6502
Я не думаю, что мой пример является хорошо известным способом, потому что я предполагаю, что он сделан кем-то с базовыми знаниями в области передачи данных, а код C ++ является результатом очень простого проектирования из области знаний (в данном случае, сети) , Это пример эволюции дизайна. На следующей итерации разработчик узнает больше о знаниях предметной области, поэтому он расширяет свой дизайн, чтобы соответствовать развитым знаниям (таким как дополнительные аспекты безопасности на каждом уровне веб-сервера). Затем он уточняет реализацию в соответствии с новым дизайном.
Amumu
Дело в том, что реальное решение, полученное из независимого от домена языка (например, естественный язык, математический язык ... зависит от домена). Акт кодирования - это просто подробное отображение сгенерированного решения в код.
Amumu
1
Привет, Mr.6502. Через год, когда я узнал больше вещей, я начал видеть, что то, что ты сказал, стало более правдивым. Я сделал другой поток: programmers.stackexchange.com/questions/179000/… . Если у вас будет время, я надеюсь, вы еще раз проясните мои мысли: D.
Amumu
6

Я не уверен, как этот ответ будет применяться к Лиспу, но я только что закончил читать Agile Принципы, Шаблоны и Практики , и автор, дядя Боб , решительно поддерживает нисходящий подход для C # (также применимый к C ++), с которым я полностью дать согласие.

Однако, в отличие от некоторых других ответов, в которых сделан вывод о том, что подход «сверху вниз» означает, что при первом взаимодействии вы предоставляете только документы и общий дизайн, книга указывает на другой метод: TDD в сочетании с эволюционным дизайном.

Идея состоит в том, что вы начинаете с вершины и определяете свои наивысшие уровни абстракции (или локальные наивысшие), и как только они определены, вы заставляете их выполнять полезную работу, чтобы функция # 1 сразу работала. Затем, когда вы добавляете все больше и больше функций, вы реорганизуете свой код и развиваете дизайн по мере необходимости, всегда оставаясь в курсе принципов SOLID., Таким образом, вы не получите слишком много уровней абстракции и не получите низкоуровневый дизайн, который не соответствует общей архитектуре. Если вы не уверены, что это значит, в упомянутой выше книге есть целая глава с примером, в котором разработчики берут концепции и начинают с UML-диаграмм и низкоуровневых классов, но только для того, чтобы понять, что половина этих классов не нужна, когда кодирование действительно начинается. В сущности, при таком подходе низкоуровневый код естественным образом отталкивается, так как в проекте определяются более высокоуровневые детали.

И, наконец, если вы практикуете SOLID, вы не должны сталкиваться с ситуацией, когда у вас определены высокоуровневые абстракции, а затем попадаете в детали и вдруг обнаруживаете, что нет OOD или абстракций. Это не вина дизайн сверху вниз, но ленивый инжиниринг.

Если вам интересно узнать больше о XP и эволюционном дизайне, вот хорошее прочтение Мартина Фаулера, другого великого автора: «Является ли Design Dead?»

DXM
источник
Отлично. Об этом я и говорю. Также приятно знать о принципе SOLID, и Agile поддерживает нисходящий подход (я думал, что с TDD и XP люди переходят к коду как можно скорее и избегают документации).
Amumu
@Amumu: я добавил ссылку на другую статью, написанную Фаулером. Вы можете прочитать больше о разнице между ковбойским кодированием без дизайна / документации и тем, что на самом деле продвигает XP / Agile. Хотя лично я твердо верю, что документация имеет ценность, и я активно продвигаю свою команду, чтобы не отстать от наших дизайнерских документов, я постепенно меняю свои взгляды. Теперь все наши «задачи по дизайну» относятся к типу досок / салфеток, а фактические документы обновляются только после написания истории. Мы также сократили то, что на самом деле задокументировано, поэтому документы охватывают только вопросы высокого уровня / архитектуры
DXM
3

Для меня наиболее важные замечания, которые Пол Грэм делает в своей статье:

[...] программисты на Лиспе [...] следуют принципу, который можно назвать восходящим дизайном - меняя язык в соответствии с проблемой. В Лиспе вы не просто записываете свою программу в соответствии с языком, вы также создаете язык в соответствии с вашей программой.

Или, как известно в кругах C ++: дизайн библиотеки - это дизайн языка (Бьярне Страуструп)

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

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

pillmuncher
источник
2

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

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

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

Обратите внимание, что в отличие от реальных автомобилей и настоящих колес, если вы создали одно программное «колесо», вы создали их бесконечное количество.

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

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

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

RalphChapin
источник
Ничего себе я получил ответ от 16 февраля, и в папке «Входящие» ничего не показывалось Я согласен с вами, всегда нужно иметь план, даже очень легкий, а затем немного выстроить и уточнить план. Это совершенно разумно. Тем не менее, я не понимаю, почему многие разработчики любят все планировать, когда пишут код. Можно сэкономить значительное количество времени, и они могут сосредоточиться на работе над реальной проблемой, а не на непрерывном рефакторинге архитектуры кода в процессе реализации.
Amumu
@Amumu Одна из причин планирования во время написания: некоторые программисты так много знают о том, что они делают с клавиатурой и мышью, это их узкое место в коде, а не процесс планирования. (Повторно используемый код позволил бы им выполнять новую, творческую работу, которая позволила бы мышлению снова стать трудной частью.)
RalphChapin
@Amumu Еще одна причина для планирования во время написания: с некоторыми комбинациями язык / программист / проблема, язык - лучший способ думать о проблеме. Язык - это самый простой способ выразить свои мысли на «бумаге». Я только что прочитал кое-что о Лиспе, но я подозреваю, что это особенно хорошо для этого. (Некоторые языки были впервые созданы как способ выражения проблемы, а не как машина, которая решает ее.) Если перекомпоновать ваш код так же легко, как и ваши мысли, также можно написать код. Но программист, язык и проблема должны хорошо сочетаться, чтобы это работало.
RalphChapin
Это надежно. Идея планирования заключается в том, чтобы отделить знания о предметной области и идентификаторы отношений между понятиями предметной области с фактическим процессом программирования. Представьте, что если класс используется дюжиной других классов, без тщательного планирования создания интерфейса для взаимодействия с другими классами, мы можем легко попасть в ад интеграции и рефакторинга. В этом случае время, потраченное на ввод и реструктуризацию кода, значительно превышает время, затрачиваемое на идентификацию на бумаге.
Amumu
Давайте рассмотрим пример: предположим, мы пишем приложение клиент / сервер. Первым делом мы должны идентифицировать протокол, определив обмен сообщениями, как сообщение размещается в памяти (т. Е. Заголовок 16 байтов, идентификатор 4 байта ....). Диаграмма не равна моделированию / планированию: abstratt.com/blog/2009/05/03/on-code-being-model . Нам не нужно использовать UML или диаграмму для моделирования, так как UML в значительной степени фокусируется только на ООП, и время, затрачиваемое на ввод в диаграмму классов UML, не меньше, чем ввод фактического кода, если не больше.
Amumu
1

Что не так с подходом сверху вниз в сочетании с итеративной разработкой?

Использование нисходящего подхода с итеративной разработкой не дает никакого рабочего кода на первых итерациях. Он предоставляет проектную документацию и такую ​​информацию, о которой клиенту будет сложно дать отзыв. Ответы типа «да, я думаю (это слишком абстрактно для меня)» не помогут вам в дальнейшем уточнении специфики желаемой системы клиентом.

Вместо этого вы создаете только общий обзор того, что запрашивается. (Требования), чтобы использовать в качестве общего руководства для того, что определяет готовый продукт и что заслуживает приоритет для реализации. После этого вы создаете рабочие продукты для каждой итерации, с которыми клиент может поиграть, чтобы увидеть, имелось ли это в виду. Если нет, то это не будет проблемой, поскольку на разработку системы, которая работает не так, как того требует клиент, не потрачены сотни часов.

Пол Грэм поощрял сборку снизу вверх полностью? Или просто запрограммировать его снизу вверх, но не на этапе анализа требований / разработки?

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

Это означает, что в период написания программы на Лиспе вы получаете универсальный инструмент, который можно использовать для написания похожих программ оригинальной, не так ли?

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

Beanow
источник
1
Жирным шрифтом, на мой взгляд, нет необходимости, когда вы уже цитируете блок…
mk12