Файл: 1 Что такое дизайн и архитектура За долгие годы вокруг понятий дизайн иархитектура накопилось много путаницы.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.11.2023
Просмотров: 17
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
1 1. Что такое дизайн и архитектура? За долгие годы вокруг понятий «дизайн» и «архитектура» накопилось много путаницы.
Что такое дизайн? Что такое архитектура? Чем они различаются? Цель?
В чем состоит цель таких решений, цель хорошего дизайна программного обеспечения? Главная цель – не что иное, как мое утопическое описание:
Цель архитектуры программного обеспечения – уменьшить человеческие трудозатраты на создание и сопровожде- ние системы.
Мерой качества дизайна может служить простая мера трудозатрат, необходимых для удовлетворения потребностей кли- ента. Если трудозатраты невелики и остаются небольшими в течение эксплуатации системы, система имеет хороший ди- зайн. Если трудозатраты увеличиваются с выходом каждой новой версии, система имеет плохой дизайн. Вот так все про- сто.
Пример из практики В качестве примера рассмотрим результаты исследований из практики. Они основаны на реальных данных, предоставленных реальной компанией, пожелавшей не разглашать своего названия. Сначала рассмотрим график роста численности инженерно-технического персонала. Вы наверняка согласитесь, что тенденция впечатляет. Рост числен- ности, как показано на рис. 1.1, должен служить признаком успешного развития компании!
Теперь взгляните на график продуктивности компании за тот же период, измеряемой в количестве строк кода (рис. 1.2).
Рис. 1.1. Рост численности инженерно-технического персонала. Воспроизводится с разрешения автора презентации Джейсона Гормана (Jason Gorman)
Рис. 1.2. Продуктивность за тот же период
Очевидно, что здесь что-то не так. Даже при том, что выпуск каждой версии поддерживается все большим количеством разработчиков, похоже, что количество строк кода приближается к своему пределу. А теперь взгляните на по-настоящему удручающий график: на рис. 1.3 показан рост стоимости строки кода с течением времени.
Рис. 1.3. Изменение стоимости строки кода с течением времени
Рис. 1.4. Изменение продуктивности с выпуском новых версий
Эта тенденция говорит о нежизнеспособности. Какой бы рентабельной ни была компания в настоящее время, растущие накладные расходы поглотят прибыль и приведут ее к застою, если не к краху.
Чем обусловлено такое значительное изменение продуктивности? Почему строка кода в версии 8 продукта стоит в 40 раз дороже, чем в версии 1?
Причины неприятностей Причины неприятностей перед вашими глазами. Когда системы создаются второпях, когда уве- личение штата программистов – единственный способ продолжать выпускать новые версии и когда чистоте кода или ди- зайну уделяется минимум внимания или не уделяется вообще, можно даже не сомневаться, что такая тенденция рано или поздно приведет к краху.
На рис. 1.4 показано, как выглядит эта тенденция применительно к продуктивности разработчиков. Сначала разработчики показываю продуктивность, близкую к 100 %, но с выходом каждой новой версии она падает. Начиная с четвертой версии, как нетрудно заметить, их продуктивность приближается к нижнему пределу – к нулю. С точки зрения разработчиков, такая ситуация выглядит очень удручающе, потому все они продолжают трудиться с полной отдачей сил. Никто не отлы- нивает от работы. И все же, несмотря на сверхурочный труд и самоотверженность, они просто не могут произвести больше.
Все их усилия теперь направлены не на реализацию новых функций, а на борьбу с беспорядком. Большую часть времени
2 они заняты тем, что переносят беспорядок из одного места в другое, раз за разом, чтобы получить возможность добавить еще одну мелочь.
Точка зрения руководства
Если вы думаете, что ситуация скверная, взгляните на нее с точки зрения руководства! На рис. 1.5 изображен график из- менения месячного фонда оплаты труда разработчиков за тот же период.
Когда была выпущена версия 1, месячный фонд оплаты труда составлял несколько сотен тысяч долларов. К выпуску второй версии фонд увеличился еще на несколько сотен тысяч. К выпуску восьмой версии месячный фонд оплаты труда составил
20 миллионов долларов и тенденция к увеличению сохраняется. Этот график выглядит не просто скверно, он пугает. Оче- видно, что происходит что-то ужасное. Одна надежда, что рост доходов опережает рост затрат, а значит, оправдывает расходы. Но с любой точки зрения эта кривая вызывает беспокойство. А теперь сравните кривую на рис. 1.5 с графиком роста количества строк кода от версии к версии на рис. 1.2. Сначала, всего за несколько сотен тысяч долларов в месяц, удалось реализовать огромный объем функциональности, а за 20 миллионов в последнюю версию почти ничего не было добавлено! Любой менеджер, взглянув на эти два графика, придет к выводу, что необходимо что-то предпринять, чтобы предотвратить крах.
Но что можно предпринять? Что пошло не так? Что вызвало такое невероятное снижение продуктивности? Что могут сде- лать руководители, кроме как топнуть ногой и излить свой гнев на разработчиков?
Что не так? Примерно 2600 лет тому назад Эзоп сочинил басню о Зайце и Черепахе. Мораль той басни можно выразить по-разному:
• «медленный и постоянный побеждает в гонке»;
• «в гонке не всегда побеждает быстрейший, а в битве – сильнейший»;
• «чем больше спешишь, тем меньше успеваешь».
Притча подчеркивает глупость самонадеянности. Заяц был настолько уверен в своей скорости, что не отнесся всерьез к состязанию, решил вздремнуть и проспал, когда Черепаха пересекла финишную черту. Современные разработчики также участвуют в похожей гонке и проявляют похожую самонадеянность. О нет, они не спят, нет. Многие современные разра- ботчики работают как проклятые. Но часть их мозга действительно спит – та часть, которая знает, что хороший, чистый, хорошо проработанный код играет немаловажную роль. Эти разработчики верят в известную ложь: «Мы сможем навести порядок потом, нам бы только выйти на рынок!» В результате порядок так и не наводится, потому что давление конкурен- ции на рынке никогда не ослабевает. Выход на рынок означает, что теперь у вас на хвосте висят конкуренты и вы должны стремиться оставаться впереди них и бежать вперед изо всех сил. Поэтому разработчики никогда не переключают режим работы. Они не могут вернуться и навести порядок, потому что должны реализовать следующую новую функцию, а потом еще одну, и еще, и еще. В результате беспорядок нарастает, а продуктивность стремится к своему пределу около нуля. Так же как Заяц был излишне уверен в своей скорости, многие разработчики излишне уверены в своей способности оставаться продуктивными. Но ползучий беспорядок в коде, иссушающий их продуктивность, никогда не спит и не никогда бездей- ствует. Если впустить его, он уменьшит производительность до нуля за считаные месяцы.
Самая большая ложь, в которую верят многие разработчики, – что грязный код поможет им быстро выйти на рынок, но в действительности он затормозит их движение в долгосрочной перспективе. Разработчики, уверовавшие в эту ложь, про- являют самонадеянность Зайца, полагая, что в будущем смогут перейти от создания беспорядка к наведению порядка, но они допускают простую ошибку. Дело в том, что создание беспорядка всегда оказывается медленнее, чем неуклонное соблюдение чистоты, независимо от выбранного масштаба времени. Рассмотрим на рис. 1.6 результаты показательного эксперимента, проводившегося Джейсоном Горманом в течение шести дней. Каждый день он писал от начала до конца простую программу преобразования целых чисел из десятичной системы счисления в римскую. Работа считалась закон- ченной, когда программа успешно проходила предопределенный комплект приемочных тестов. Каждый день на решение
Рис. 1.5. Изменение фонда оплаты труда разработчиков с выпуском новых версий
Рис. 1.6. Время на выполнение итерации с использова- нием и без использования методики
TDD хорошо известную методику разработки через тестиро- вание (Test-Driven Development; TDD). В остальные дни он писал код, не ограничивая себя рамками этой мето- дики.
3 поставленной задачи затрачивалось чуть меньше 30 минут. В первый, второй и третий дни Джейсон использовал
Прежде всего обратите внимание на кривую обучения, заметную на рис. 1.6. Каждый раз на решение задачи затрачива- лось меньше времени. Отметьте также, что в дни, когда применялась методика TDD, упражнение выполнялось примерно на 10 % быстрее, чем в дни без применения TDD, и что даже худший результат, полученный с TDD, оказался лучше самого лучшего результата, полученного без TDD. Кто-то, взглянув на этот результат, может посчитать его удивительным. Но для тех, кто не поддался обману самонадеянности Зайца, результат будет вполне ожидаемым, потому что они знают простую истину разработки программного обеспечения: Поспешай не торопясь. И она же является ответом на дилемму, стоящую перед руководством. Единственный способ обратить вспять снижение продуктивности и увеличение стоимости – заста- вить разработчиков перестать думать как самонадеянный Заяц и начать нести ответственность за беспорядок, который они учинили. Разработчики могут подумать, что проблему можно исправить, только начав все с самого начала и перепро- ектировав всю систему целиком, – но это в них говорит все тот же Заяц. Та же самонадеянность, которая прежде уже при- вела к беспорядку, теперь снова говорит им, что они смогут построить лучшую систему, если только вновь вступят в гонку.
Однако в действительности все не так радужно: Самонадеянность, управляющая перепроектированием, приведет к тому же беспорядку, что и прежде.
Заключение Любой организации, занимающейся разработкой, лучше всего избегать самонадеянных решений и с самого начала со всей серьезностью отнестись к качеству архитектуры ее продукта.
Серьезное отношение к архитектуре программного обеспечения подразумевает знание о том, что такое хорошая архитек- тура. Чтобы создать систему, дизайн и архитектура которой способствуют уменьшению трудозатрат и увеличению продук- тивности, нужно знать, какие элементы архитектуры ведут к этому. Именно об этом рассказывается в данной книге. В ней рассказывается, как выглядит добротная, чистая архитектура и дизайн, чтобы разработчики могли создавать системы, спо- собные приносить прибыль долгое время.
2. История о двух ценностях Всякая программная система имеет две разные ценности: поведение и структуру. Разработ- чики отвечают за высокий уровень обеих этих ценностей. Но, к сожалению, они часто сосредоточиваются на чем-то одном, забывая про другое. Хуже того, они нередко сосредоточиваются на меньшей из двух ценностей, что в конечном итоге обесценивает систему.
Поведение Первая ценность программного обеспечения – его поведение. Программистов нанимают на работу, чтобы они заставили компьютеры экономить деньги или приносить прибыль заинтересованной стороне. Для этого мы помогаем за- интересованным сторонам разработать функциональную спецификацию или документ с требованиями. Затем пишем код, заставляющий компьютеры заинтересованных сторон удовлетворять этим требованиям.
Когда компьютер нарушает требования, программисты вынимают свои отладчики и исправляют проблему. Многие про- граммисты полагают, что этим их работа ограничивается. Они уверены, что их задача – заставлять компьютеры соответ- ствовать требованиям и исправлять ошибки. Они жестоко ошибаются.
Архитектура Вторая ценность программного обеспечения заключена в самом названии «программное обеспечение».
Слово «обеспечение» означает «продукт»; а слово «программное»… Как раз в нем и заключается вторая ценность.
Идея программного обеспечения состоит в том, чтобы дать простую возможность изменять поведение компьютеров. По- ведение компьютеров в некоторых пределах можно также изменять посредством аппаратного обеспечения, но этот путь намного сложнее.
Для достижения этой цели программное обеспечение должно быть податливым – то есть должна быть возможность легко изменить его. Когда заинтересованные стороны меняют свое мнение о некоторой особенности, приведение ее в соответ- ствие с пожеланиями заинтересованной стороны должно быть простой задачей. Сложность в таких случаях должна быть пропорциональна лишь масштабу изменения, но никак не его форме.
Именно эта разница между масштабом и формой часто является причиной роста стоимости разработки программного обеспечения. Именно по этой причине стоимость растет пропорционально объему требуемых изменений. Именно по- этому стоимость разработки в первый год существенно ниже, чем во второй, а во второй год ниже, чем в третий.
С точки зрения заинтересованных сторон они просто формируют поток изменений примерно одинакового масштаба. С точки зрения разработчиков, заинтересованные стороны формируют поток фрагментов, которые они должны встраивать в мозаику со все возрастающей сложностью. Каждый новый запрос сложнее предыдущего, потому что форма системы не соответствует форме запроса.
Я использовал здесь слово «форма» не в традиционном его понимании, но, как мне кажется, такая метафора вполне уместна. У разработчиков программного обеспечения часто складывается ощущение, что их заставляют затыкать круглые отверстия квадратными пробками.
Проблема, конечно же, кроется в архитектуре системы. Чем чаще архитектура отдает предпочтение какой-то одной форме, тем выше вероятность, что встраивание новых особенностей в эту структуру будет даваться все сложнее и сложнее. По- этому архитектуры должны быть максимально независимыми от формы.
Наибольшая ценность Функциональность или архитектура? Что более ценно? Что важнее – правильная работа системы или простота ее изменения?
Если задать этот вопрос руководителю предприятия, он наверняка ответит, что важнее правильная работа. Разработчики часто соглашаются с этим мнением. Но оно ошибочно. Я могу доказать ошибочность этого взгляда простым логическим инструментом исследования экстремумов.
Матрица Эйзенхауэра Рассмотрим матрицу президента Дуайта Дэвида Эйзенхауэра для определения приоритета между важностью и срочностью (рис. 2.1). Об этой матрице Эйзенхауэр говорил так:
4
У меня есть два вида дел, срочные и важные. Срочные дела, как правило, не самые важные, а важные – не самые сроч- ные
[9]
Рис. 2.1. Матрица Эйзенхауэра
Это старое изречение несет много истины. Срочное действительно редко бывает важным, а важное – срочным.
Первая ценность программного обеспечения – поведение – это нечто срочное, но не всегда важное.
Вторая ценность – архитектура – нечто важное, но не всегда срочное.
Конечно, имеются также задачи важные и срочные одновременно и задачи не важные и не срочные. Все эти четыре вида задач можно расставить по приоритетам.
1. Срочные и важные.
2. Не срочные и важные.
3. Срочные и не важные.
4. Не срочные и не важные.
Обратите внимание, что архитектура кода – важная задача – оказывается в двух верхних позициях в этом списке, тогда как поведение кода занимает первую и третью позиции.
Руководители и разработчики часто допускают ошибку, поднимая пункт 3 до уровня пункта 1. Иными словами, они непра- вильно отделяют срочные и не важные задачи от задач, которые по-настоящему являются срочными и важными. Эта оши- бочность суждений приводит к игнорированию важности архитектуры системы и уделению чрезмерного внимания не важному поведению.
Разработчики программного обеспечения оказываются перед проблемой, обусловленной неспособностью руководите- лей оценить важность архитектуры. Но именно для ее решения они и были наняты. Поэтому разработчики должны всякий раз подчеркивать приоритет важности архитектуры перед срочностью поведения.
Битва за архитектуру Эта обязанность означает постоянную готовность к битве – возможно, в данном случае лучше ис- пользовать слово «борьба». Честно говоря, подобная ситуация распространена практически повсеместно. Команда разра- ботчиков должна бороться за то, что, по их мнению, лучше для компании, так же как команда управленцев, команда мар- кетинга, команда продаж и команда эксплуатации. Это всегда борьба. Эффективные команды разработчиков часто выхо- дят победителями в этой борьбе. Они открыто и на равных вступают в конфликт со всеми другими заинтересованными сторонами. Помните: как разработчик программного обеспечения вы тоже являетесь заинтересованной стороной. У вас есть свой интерес в программном обеспечении, который вы должны защищать. Это часть вашей роли и ваших обязан- ностей. И одна из основных причин, почему вас наняли. Важность этой задачи удваивается, если вы выступаете в роли архитектора программного обеспечения. Архитекторы, в силу своих профессиональных обязанностей, больше сосредото- чены на структуре системы, чем на конкретных ее особенностях и функциях. Архитекторы создают архитектуру, помогаю- щую быстрее и проще создавать эти особенности и функции, изменять их и дополнять.Просто помните, что если поместить архитектуру на последнее место, разработка системы будет обходиться все дороже, и в конце концов внесение изменений в такую систему или в отдельные ее части станет практически невозможным. Если это случилось, значит, команда разра- ботчиков сражалась недостаточно стойко за то, что они считали необходимым.
II. Начальные основы: парадигмы программирования Архитектура программного обеспечения начинается с кода, по- этому начнем обсуждение архитектуры с рассказа о самом первом программном коде. Основы программирования зало- жил Алан Тьюринг в 1938 году. Он не первый, кто придумал программируемую машину, но он первым понял, что про- граммы – это всего лишь данные. К 1945 году Тьюринг уже писал настоящие программы для настоящих компьютеров, используя код, который мы смогли бы прочитать (приложив определенные усилия). В своих программах он использовал циклы, конструкции ветвления, операторы присваивания, подпрограммы, стеки и другие знакомые нам структуры.
Тьюринг использовал двоичный язык. С тех пор в программировании произошло несколько революций. Одна из самых известных – революция языков. Во-первых, в конце 1940-х появились ассемблеры. Эти «языки» освободили программи- стов от тяжкого бремени трансляции их программ в двоичный код. В 1951 году Грейс Хоппер изобрела первый компилятор
A0. Именно она фактически ввела термин компилятор. В 1953 году был изобретен язык Fortran (через год после моего рождения). Затем последовал непрерывный поток новых языков программирования: COBOL, PL/1, SNOBOL, C, Pascal, C++,
Java и так до бесконечности. Другая, еще более важная, как мне кажется, революция произошла в парадигмах програм- мирования. Парадигма – это способ программирования, не зависящий от конкретного языка. Парадигма определяет, ка- кие структуры использовать и когда их использовать. До настоящего времени было придумано три такие парадигмы. По причинам, которые мы обсудим далее, едва ли стоит ожидать каких-то других, новых парадигм.
3. Обзор парадигм В этой главе дается общий обзор следующих трех парадигм: структурное программирование, объ- ектно-ориентированное программирование и функциональное программирование.
Структурное программирование Первой, получившей всеобщее признание (но не первой из придуманных), была пара- дигма структурного программирования, предложенная Эдсгером Вибе Дейкстрой в 1968 году. Дейкстра показал, что