Страница:
Подводя итоги: установите предположительные признаки группы пользователей. Гораздо лучше ошибаться, но выражаться ясно, чем выражаться туманно.
Как насчет эффекта второй системы? Один наблюдательный ученый заметил, что «Мифический человеко-месяц» рекомендовал на случай неудачи для всякой новой системы планировать поставку второй версии (см. глава 11), которая в главе 5 характеризуется как таящая наибольшие опасности. Я вынужден был признать, что он меня «поймал».
Противоречие скорее лингвистическое, чем реальное. «Вторая» система, описываемая в главе 5, — это вторая система, выпускаемая в развитие предыдущей с привлечением дополнительных функций и украшений. «Вторая» система в главе 11 — это вторая попытка разработки первой выпускаемой системы. Она разрабатывается в условиях всех ограничений, накладываемых графиком, способностями и неведением, характерными для новых проектов — ограничений, навязывающих дисциплину умеренности.
Триумф интерфейса WIMP
Одним из наиболее впечатляющих явлений в программировании за последние двадцать лет был триумф интерфейса, состоящего из окон, значков, меню и указателей (Windows, Icons, Menus, Pointers — WIMP). Сегодня он настолько широко известен, что не требует описания. Впервые эту идею представили публике Дуг Энглебарт (Doug Englebart) с группой коллег из Стэндфордского научно-исследовательского института на Объединенной компьютерной конференции Запада в 1968 году. [5] Оттуда идеи перекочевали в исследовательский центр Xerox в Пало-Альто, где они реализовались на персональной рабочей станции Alto, разработанной Бобом Тейлором (Bob Taylor) с сотрудниками. Их подхватил Стив Джобс для компьютера Apple Lisa — слишком медленного для осуществления своих восхитительных концепций простоты использования. Эти концепции Джобс затем воплотил в коммерчески успешном Apple Macintosh в 1985 году. Позднее они были приняты в Microsoft Windows для IBM PC и его клонов. Мой пример будет базироваться на версии для Мака. [6]
Концептуальная целостность через метафору. WIMP является отличным примером пользовательского интерфейса, обладающего концептуальной целостностью, достигаемой принятием знакомой идеальной модели — метафоры рабочего стола, и ее тщательного последовательного развития для использования воплощения в компьютерной графике. Например, из принятой метафоры непосредственно следует сложно осуществимое, но правильное решение о перекрытии окон вместо расположения их одно рядом с другим. Возможность менять размер и форму окон является последовательным расширением, дающим пользователю новые возможности, обеспечиваемые носителем — компьютерной графикой. У реальных бумаг на столе нельзя так же легко менять размер и форму. Буксировка непосредственно вытекает из метафоры; выбор значков с помощью курсора является прямой аналогией захвата предметов рукой. Значки и вложенные папки являются точными аналогами документов на столе, как и мусорная корзина. Идеи вырезания, копирования и вставки точно имитируют операции, которые мы обычно осуществляем с документами на столе. Следование метафоре столь буквально, а развитие настолько последовательно, что пользователей-новичков решительно коробит, когда перетаскивание значка дискеты в мусорную корзину приводит к извлечению дискеты из дисковода. Если бы интерфейс не был столь единообразно последовательным, эта (довольно неприятная) непоследовательность так бы не раздражала.
В каких местах интерфейс WIMP вынужден далеко отойти от метафоры рабочего стола? Наиболее заметны два отличия: меню и работа одной рукой. На реальном рабочем столе с документами осуществляют действия, а не приказывают кому-то или чему-то осуществить их. А когда кому-то дается указание совершить действие, команда обычно не выбирается из списка, а письменно или устно подается в виде глагола в повелительном наклонении: «пожалуйста, подшейте это в папку», «пожалуйста, найдите предыдущие письма» или «пожалуйста, передайте это Мэри для принятия мер».
К сожалению, надежная интерпретация команд в свободном формате на английском языке, будь они в устном или письменном виде, находится за пределами наших сегодняшних возможностей. Поэтому проектировщики интерфейса на два шага отошли от непосредственных действий пользователя с документами. Они мудро взяли имевшийся на обычном рабочем столе образец выбора команд — отпечатанную «сопроводиловку», в которой пользователь производит выбор из ограниченного меню команд со стандартной семантикой. Эту идею они превратили в горизонтальное меню с вертикально опускающимися подменю.
Подача команд и проблема двух курсоров. Команды являются повелительными предложениями, в них всегда есть и обычно имеется прямое дополнение. Для любого действия нужно задать глагол и существительное. Метафора указания говорит, что для одновременного задания двух предметов нужно иметь на экране два разных курсора, управляемых своими мышами, одной — в правой руке, другой — в левой. В конце концов, на физическом столе мы обычно работаем двумя руками. (Однако одна рука часто придерживает вещи на месте, что на компьютерном рабочем столе происходит по умолчанию.) Мозг, конечно, приспособлен к действиям двумя руками: мы систематически используем две руки при вводе с клавиатуры, езде на автомобиле, приготовлении пищи. Увы, и одна мышь была большим достижением для изготовителей компьютеров. Коммерческих систем, поддерживающих
одновременные действия с двумя курсорами мышей, по одному для каждой руки, нет. [7]
Разработчики интерфейса смирились с реалиями и сделали проект для одной мыши, приняв синтаксическое соглашение, что первым отмечается (выбирается) существительное. Затем указывают на глагол, пункт меню. При этом в значительной мере утрачивается простота использования. Когда я наблюдаю за пользователями, просматриваю видеозапись их действий или зарегистрированные компьютером перемещения курсора, то всегда обращаю внимание на то, что одному курсору приходится выполнять работу двух: выбрать объект в окне на рабочем столе; выбрать глагол в меню; найти другой объект или вновь отыскать прежний; снова опустить меню (часто, то же самое) и выбрать глагол. Курсор мечется взад-вперед, от пространства данных к пространству меню, всякий раз теряя полезную информацию о том, где он находился в этом пространстве в прошлый раз — в целом, неэффективный процесс.
Великолепное решение. Даже если бы электроника и программы могли без труда работать одновременно с двумя активными курсорами, остаются сложности с топологией пространства. На рабочем столе в метафоре WIMP в действительности есть пишущая машинка, и в физическом пространстве реального стола необходимо поместить реальную клавиатуру. Клавиатура плюс два коврика для мышей займут немалую часть пространства в пределах досягаемости рук. Так почему бы проблему клавиатуры не обратить себе на пользу, почему не использовать обе руки — одной рукой задавая на клавиатуре глаголы, а другой рукой выбирая существительные с помощью мыши? Теперь курсор будет оставаться в пространстве данных и пользоваться тем, что последовательные существительные выбираются близко одно от другого. Реальная эффективность, реально большие возможности пользователя.
Мощность функций или простота использования. Однако при таком решении теряется то, что делает использование меню таким простым для новичков: меню представляет список альтернативных глаголов, допустимых в каждом конкретном состоянии. Можно купить коробку, принести ее домой и начать работать, не читаю инструкцию, зная лишь, для чего она куплена и экспериментируя с различными глаголами в меню.
Одна из сложнейших задач, стоящих перед архитекторами — это найти соотношение между мощностью функций и простотой использования. Нужно ли проектировать программу в расчете на новичка и случайного пользователя или строить ее с мощными функциями для профессионала? Идеальное решение – обеспечить и то, и другое концептуально согласованным образом, что достигается при помощи интерфейса WIMP. У часто используемых глаголов меню есть клавишные эквиваленты из одной клавиши + командной клавиши, которые обычно легко ввести левой рукой одним аккордом. Например, в Маке командная клавиша ( ) находится как раз под клавишами Z и X, поэтому самые частые действия кодируются как
Постепенный переход от новичка к опытному пользователю. Такая двойная система задания командных глаголов не только отвечает потребности новичка в легком обучении и потребности опытного пользователя в эффективном использовании, но и позволяет каждому пользователю плавно перейти из одного режима в другой. Буквенные обозначения, называемые клавишами сокращенного набора, показываются в меню рядом с глаголами, поэтому в случае неуверенности пользователь может раскрыть меню, чтобы проверить буквенный эквивалент, вместо выбора пункта меню. Каждый новичок запоминает сначала сокращенный набор для своих частых операций. Он может попробовать любое сокращение, в котором не уверен, поскольку отменяет любое ошибочное одиночное действие. С другой стороны, он может справиться в меню относительно допустимых команд. Новички очень часто опускают меню, опытные пользователи — редко, а тем, которые находятся посередине, лишь от случая к случаю понадобится выбирать из меню, поскольку они уже знают клавиши, которые вызывают большинство осуществляемых ими операций. Мы, проектировщики программного обеспечения, слишком привыкли к этому интерфейсу, чтобы оценить его элегантность и мощь.
Успех прямого включения как средства навязывания архитектуры. Интерфейс Мака примечателен еще в одном отношении. Без всякого принуждения разработчики сделали его стандартом для разных приложений, включая большинство из тех, которые описаны сторонними организациями. Поэтому пользователь приобретает концептуальную согласованность на уровне интерфейса не только для программ, поставляемых вместе с машиной, но и для всех других приложений.
Этот подвиг создатели Мака осуществили, встроив интерфейс в ПЗУ, в результате чего разработчикам проще и быстрее пользоваться существующим, чем создавать свои идиосинкразические интерфейсы. Это естественное стремление к единообразию возобладало настолько широко, что стало стандартом де-факто. Естественные стремления были поддержаны полной приверженностью со стороны
менеджеров и существенным принуждением со стороны Apple. Независимые рецензенты в журналах, поняв огромное значение межпрограммной концептуальной целостности, также подкрепили естественные стремления, безжалостно критикуя продукты, не удовлетворяющие этому интерфейсу.
Это отличительный пример технологии, рекомендованной в главе 6 для достижения единообразия путем поощрения других сторон непосредственно включать в свои продукты имеющийся код вместо разработки новых программ согласно имеющимся спецификациям.
Судьба WIMP: устаревание. Несмотря на все достоинства, по моему мнению, интерфейс WIMP через поколение станет достоянием истории. Указание курсором останется способом задания существительных при управлении нашими компьютерами. Для выражения глаголов станет использоваться речь. Такие инструменты, как Voice Navigator для Маков и Dragon для PC, уже предоставляют такую возможность.
Не разрабатывайте программ на выброс, каскадная модель неверна!
В главе 11 дается радикальный совет: «планируйте выбросить первую программу, вам все равно придется это сделать». Сейчас я считаю это ошибочным — не в силу чрезмерного радикализма, но в силу чрезмерной упрощенности.
Самой большой ошибкой этой концепции является косвенное принятие классической последовательности — в виде каскада — модели создания программы. Эта модель происходит от структуры диаграммы Гранта для поэтапного процесса, которую часто изображают, как на рисунке 19.1. В классической статье 1970 года Винтон Ройс (Winton Royce) усовершенствовал последовательную модель путем:
• добавления некоторой обратной связи с предшествующими этапами;
• ограничения обратной связи только непосредственными предшественниками, чтобы исключить вызываемые ими издержки и задержки в выполнении графика.
Он предвосхитил «МЧ-М», рекомендовав разработчикам «делать работу дважды» [8] . Глава 11 — не единственная, на которую повлияла каскадная модель. Она проходит через всю книгу, начиная с правила планирования в главе 2. Это практическое правило отводит 1/3 времени на планирование, 1/6 — на написание программ, 1/4 — на тестирование компонентов и 1/4 — на системное тестирование.
Рис. 19.1 Каскадная модель создания программы
Основное заблуждение каскадной модели состоит в предположениях, что проект проходит через весь процесс один раз, архитектура хороша и проста в использовании, проект осуществления разумен, а ошибки в реализации устраняются по мере тестирования. Иными словами, каскадная модель исходит из того, что все ошибки будут сосредоточены в реализации, а потому их устранение происходит равномерно во время тестирования компонентов и системы.
«Планируйте на выброс» действительно резко нападает на это заблуждение. Ошибка не в диагнозе, а в лекарстве. Сейчас я предположил, что первую систему можно отбросить или перепроектировать не всю целиком, а по частям. Хорошо, если это так, но при этом не затрагивается корень проблемы. В каскадной модели системное тестирование, а следовательно и тестирование пользователем, отодвигается в конец процесса создания программы. Поэтому есть шанс обнаружить крайние неудобства для пользователя, или неприемлемые технические характеристики, или опасную уязвимость к ошибкам или злонамеренным действиям пользователя лишь в самом конце разработки. Изучение спецификаций при альфа-тестировании нацелено на раннее обнаружение таких ошибок, но ничто не может заменить непосредственных пользователей.
Вторым недостатком каскадной модели является предположение, что система строится сразу вся целиком для тестирования с начала до конца после того, как завершены все проектные разработки, большая часть написания программ и значительная часть тестирования компонентов.
К несчастью, каскадная модель, это преобладавшее в 1975 году представление о программных проектах, была включена в DOD-STD-2167 — спецификацию Министерства обороны для любого военного программного обеспечения. По этой причине она просуществовала долгое время после того, как большая часть думающих практиков осознала ее неадекватность и отказалась от нее. К счастью, в МО позднее поняли истину. [9]
Необходимо двигаться против течения. Опыт и идеи из каждой расположенной ниже по течению части процесса создания программы, как энергичный лосось, должны прыгать вверх по течению, иногда сразу через несколько этапов, и воздействовать на деятельность наверху.
Проектные разработки покажут, что некоторые предусмотренные архитектурой возможности ухудшают технические характеристики, и потому архитектура должна быть переработана. Программирование при реализации выявит, что некоторые функции непомерно увеличивают требования к памяти, поэтому нужно внести изменения в архитектуру и разработку.
Поэтому вполне может потребоваться осуществить несколько итераций цикла архитектура-разработка, прежде чем начать какую-либо программную реализацию.
Модель пошагового создания лучше: последовательное уточнение
Построение каркаса с начала до конца. Гарлан Миллз (Harlan Mills), работающий в системе с разделением времени, давно уже рекомендует строить основной цикл опроса системы реального времени с вызовами подпрограмм (заглушками) для всех функций (рис. 19.2), но пустыми подпрограммами. Откомпилируйте его, протестируйте, и он будет идти цикл за циклом, буквально ничего не делая, но делая это без ошибок. [10]
Рис. 19.2
На следующем шаге мы навешиваем модуль ввода (возможно, примитивный) и модуль вывода. Voila! Работающая система, делающая нечто, возможно, неинтересное. Теперь, функция за функцией, мы строим и добавляем модули. На каждом шаге мы имеем работающую систему. При достаточном трудолюбии на каждом шаге мы имеем отлаженную, протестированную систему. (По мере роста системы растет и тяжесть повторного тестирования всех новых модулей по всем прежним контрольным примерам.)
После того как на примитивном уровне каждая функция работает, мы улучшаем или переписываем один модуль за другим, пошагово наращивая систему. Иногда для надежности мы переписываем исходный движущий цикл, а возможно, и его интерфейсы с модулями.
Поскольку в любой момент времени у нас есть работающая система:
• можно очень рано начать тестирование пользователями;
• можно принять стратегию разработки в соответствии с бюджетом, полностью защищающую от перерасхода времени или средств (возможно, за счет сокращения функциональности).
В течение 22 лет я преподавал в лаборатории программной инженерии Университета штата Северная Каролина, иногда вместе с Дэвидом Парнасом. На этих занятиях бригады, обычно состоявшие из четырех человек, в течение одного семестра должны были построить некоторую реальную прикладную программную систему. Примерно посередине этого срока я стал преподавать инкрементную разработку. Я был поражен, какой возбуждающий эффект на моральный дух группы оказывает первая картинка на экране, первая работающая система.
Семейства Парнаса. Дэвид Парнас был главным властителем дум в программотехнике в течение всего этого 20-летнего периода. Всем известна его идея скрытия информации. Менее известна очень важная идея Парнаса о проектировании программного продукта как семейства взаимосвязанных продуктов. [11] Он требует, чтобы проектировщик имел в виду как дальнейшее развитие, так и новые версии продукта и определял их функциональные или межплатформенные различия так, чтобы строить дерево родственных продуктов (рис. 19.3).
Рис. 19.3
Фокус при проектировании такого дерева состоит в том, чтобы ближе к корню поместить те решения, изменение которых наименее вероятно.
Такая стратегия проектирования повышает повторную используемость модулей. Еще важнее, что ее можно расширить, включая не только поставляемые продукты, но и последовательные промежуточные версии, созданные по стратегии инкрементируемых сборок. В этом случае продукт развивается через промежуточные стадии с минимумом возврата назад.
Подход Microsoft: «ежевечерняя сборка». Джеймс Маккарти (James McCarthy) описал мне процесс, использовавшийся им и другими в Microsoft. Это пошаговое наращивание, доведенное до логического конца. Он пишет:
Сделав первую поставку, новые версии мы поставляем с дополнительными функциями, по сравнению с существующим работающим продуктом. Почему должен быть иным процесс первоначальной сборки? С момента достижения нами первой вехи (у первой поставки три промежуточных вехи) мы каждый вечер заново собираем разрабатываемую систему (и прогоняем контрольные примеры). Цикл сборки становится ритмом жизни проекта. Каждый вечер одна или более бригад программистов, проводящих тестирование, регистрируют модули с новыми функциями. После каждой сборки у нас есть работающая система. Если сборка оказывается неудачной, мы останавливаем весь процесс, пока ошибка не будет найдена и исправлена. В любой момент все в группе знают положение дел.
Это действительно тяжело. Требуется выделение больших ресурсов, но это управляемый процесс, прослеживаемый и понятный. Он вызывает у команды доверие к себе. А доверие определяет мораль, эмоциональное состояние.
Такой процесс удивляет и даже шокирует разработчиков в других организациях. Один из них говорит: «Я взял себе за правило делать сборку каждую неделю, но ежедневная сборка, я думаю, потребует слишком много труда.» И это, возможно, верно. Например, в Bell Northern Research собирают систему, состоящую из 12 миллионов строк, раз в неделю.
Инкрементная сборка и быстрое макетирование. Поскольку инкрементная разработка позволяет рано начать тестирование реальными пользователями, в чем ее отличие от быстрого макетирования? Мне кажется, что они связаны, но различаются. Одним можно пользоваться без другого.
Харел дает полезное определение макета:
(версия программы, которая) отражает только проектные решения, принятые в процессе подготовки концептуальной модели, а не решения, вызванные соображениями реализации. [12]
Можно построить макет, который вовсе не является частью продукта, развивающегося в направлении поставки. Например, можно создать макет интерфейса, за которым стоит не реально работающая программа, а лишь конечный автомат, заставляющий его имитировать прохождение состояний. Можно даже макетировать и тестировать интерфейсы методом волшебника изумрудного города, когда спрятанный человек имитирует отклик системы. Такое макетирование может быть весьма полезным для быстрого получения обратной связи от пользователя, но оно находится совершенно в стороне от тестирования продукта, который готовится к поставке.
Аналогично, разработчики могут попробовать построить вертикальный срез продукта, в котором полностью реализован весьма ограниченный набор функций, чтобы заранее пролить свет на те места, где могут таиться опасности для производительности продукта. В чем состоит различие между сборкой на первой вехе в процедуре Microsoft и быстрым макетом? В функциях. Продукт с первой вехи может иметь такую ограниченную функциональность, что ни для кого не будет представлять интереса. Готовность продукта к поставке определяется завершенностью в предоставлении полезного набора функций и своим качеством, уверенностью в надежной работе.
Парнас был прав, а я — нет в отношении сокрытия информации
В главе 7 я противопоставляю две точки зрения на то, в какой мере каждый участник команды может иметь право или поощряться к знанию проектов и текстов программ, созданных коллегами. Во время проекта Operating System/360 мы решили, что все программисты должны видеть весь материал, т.е. у каждого программиста была рабочая тетрадь проекта, которая к концу насчитывала свыше 10000 страниц. Харлан Миллз убедительно доказывал, что «программирование должно быть открытым процессом», что предоставление всей работы на общее обозрение способствует контролю качества как благодаря давлению со стороны коллег, заставляющему работать хорошо, так и благодаря тому, что коллеги действительно находят промахи и ошибки.
Этот взгляд резко противоречит мнению Дэвида Парнаса о том, что программные модули должны быть инкапсулированы, с хорошо определенными интерфейсами, а внутренность таких модулей должна быть частной собственностью программиста, невидимой снаружи. Программисты эффективнее всего работают, будучи ограждены от внутренностей чужих модулей. [13]
В главе 7 я заклеймил идею Парнаса как «рецепт катастрофы». Парнас был прав, а я ошибался. Сегодня я убежден, что ограничение информации, часто осуществляемое теперь методами объектного программирования, является единственным способом поднять уровень программных разработок.
Используя другие методы, можно действительно попасть в беду. Согласно методике Миллза программисты могут получить подробности семантики интерфейсов, с которыми они работают, узнав, что находится «по ту сторону». Укрывание этой семантики может быть причиной системных ошибок. С другой стороны, методика Парнаса способствует устойчивости при внесении изменений и больше подходит к стратегии проектирования, предполагающей изменения в будущем.
В главе 16 утверждается следующее:
• большая часть роста производительности разработки программного обеспечения обеспечена устранением второстепенных трудностей, таких как неудобные языки программирования и медленная оборачиваемость пакетной обработки;
• легких решений в этом направлении практически не осталось;
• радикального прогресса можно добиться, разрешив существенные сложности моделирования сложных концептуальных конструкций.
Самое очевидное на этом пути — признать, что программы составляются из концептуальных блоков, значительно более крупных, чем отдельные операторы языков высокого уровня: подпрограмм, или модулей, или классов. Если мы сумеем ограничить проектирование и построение программ задачей соединения вместе и параметризации таких блоков из ранее созданных наборов, то радикально повысим концептуальный уровень и избавимся от огромного объема работ и широких возможностей для ошибок, существующих на уровне отдельных операторов.
Данное Парнасом определение модулей с сокрытием информации было первым открытым шагом в этой критически важной программе исследований и идейным провозвестником объектно-ориентированного программирования. Он определил модуль как программный объект с собственной моделью данных и собственным набором операций. Доступ к его данным может быть осуществлен только через имеющиеся в нем операции. Следующий шаг явился вкладом нескольких исследователей: развитие модулей Парнаса в абстрактный тип данных, из которого можно производить много объектов. Абстрактный тип данных обеспечивает единообразный способ представления и задания интерфейсов модулей, а также дисциплину доступа, которую легко осуществлять.
Как насчет эффекта второй системы? Один наблюдательный ученый заметил, что «Мифический человеко-месяц» рекомендовал на случай неудачи для всякой новой системы планировать поставку второй версии (см. глава 11), которая в главе 5 характеризуется как таящая наибольшие опасности. Я вынужден был признать, что он меня «поймал».
Противоречие скорее лингвистическое, чем реальное. «Вторая» система, описываемая в главе 5, — это вторая система, выпускаемая в развитие предыдущей с привлечением дополнительных функций и украшений. «Вторая» система в главе 11 — это вторая попытка разработки первой выпускаемой системы. Она разрабатывается в условиях всех ограничений, накладываемых графиком, способностями и неведением, характерными для новых проектов — ограничений, навязывающих дисциплину умеренности.
Триумф интерфейса WIMP
Одним из наиболее впечатляющих явлений в программировании за последние двадцать лет был триумф интерфейса, состоящего из окон, значков, меню и указателей (Windows, Icons, Menus, Pointers — WIMP). Сегодня он настолько широко известен, что не требует описания. Впервые эту идею представили публике Дуг Энглебарт (Doug Englebart) с группой коллег из Стэндфордского научно-исследовательского института на Объединенной компьютерной конференции Запада в 1968 году. [5] Оттуда идеи перекочевали в исследовательский центр Xerox в Пало-Альто, где они реализовались на персональной рабочей станции Alto, разработанной Бобом Тейлором (Bob Taylor) с сотрудниками. Их подхватил Стив Джобс для компьютера Apple Lisa — слишком медленного для осуществления своих восхитительных концепций простоты использования. Эти концепции Джобс затем воплотил в коммерчески успешном Apple Macintosh в 1985 году. Позднее они были приняты в Microsoft Windows для IBM PC и его клонов. Мой пример будет базироваться на версии для Мака. [6]
Концептуальная целостность через метафору. WIMP является отличным примером пользовательского интерфейса, обладающего концептуальной целостностью, достигаемой принятием знакомой идеальной модели — метафоры рабочего стола, и ее тщательного последовательного развития для использования воплощения в компьютерной графике. Например, из принятой метафоры непосредственно следует сложно осуществимое, но правильное решение о перекрытии окон вместо расположения их одно рядом с другим. Возможность менять размер и форму окон является последовательным расширением, дающим пользователю новые возможности, обеспечиваемые носителем — компьютерной графикой. У реальных бумаг на столе нельзя так же легко менять размер и форму. Буксировка непосредственно вытекает из метафоры; выбор значков с помощью курсора является прямой аналогией захвата предметов рукой. Значки и вложенные папки являются точными аналогами документов на столе, как и мусорная корзина. Идеи вырезания, копирования и вставки точно имитируют операции, которые мы обычно осуществляем с документами на столе. Следование метафоре столь буквально, а развитие настолько последовательно, что пользователей-новичков решительно коробит, когда перетаскивание значка дискеты в мусорную корзину приводит к извлечению дискеты из дисковода. Если бы интерфейс не был столь единообразно последовательным, эта (довольно неприятная) непоследовательность так бы не раздражала.
В каких местах интерфейс WIMP вынужден далеко отойти от метафоры рабочего стола? Наиболее заметны два отличия: меню и работа одной рукой. На реальном рабочем столе с документами осуществляют действия, а не приказывают кому-то или чему-то осуществить их. А когда кому-то дается указание совершить действие, команда обычно не выбирается из списка, а письменно или устно подается в виде глагола в повелительном наклонении: «пожалуйста, подшейте это в папку», «пожалуйста, найдите предыдущие письма» или «пожалуйста, передайте это Мэри для принятия мер».
К сожалению, надежная интерпретация команд в свободном формате на английском языке, будь они в устном или письменном виде, находится за пределами наших сегодняшних возможностей. Поэтому проектировщики интерфейса на два шага отошли от непосредственных действий пользователя с документами. Они мудро взяли имевшийся на обычном рабочем столе образец выбора команд — отпечатанную «сопроводиловку», в которой пользователь производит выбор из ограниченного меню команд со стандартной семантикой. Эту идею они превратили в горизонтальное меню с вертикально опускающимися подменю.
Подача команд и проблема двух курсоров. Команды являются повелительными предложениями, в них всегда есть и обычно имеется прямое дополнение. Для любого действия нужно задать глагол и существительное. Метафора указания говорит, что для одновременного задания двух предметов нужно иметь на экране два разных курсора, управляемых своими мышами, одной — в правой руке, другой — в левой. В конце концов, на физическом столе мы обычно работаем двумя руками. (Однако одна рука часто придерживает вещи на месте, что на компьютерном рабочем столе происходит по умолчанию.) Мозг, конечно, приспособлен к действиям двумя руками: мы систематически используем две руки при вводе с клавиатуры, езде на автомобиле, приготовлении пищи. Увы, и одна мышь была большим достижением для изготовителей компьютеров. Коммерческих систем, поддерживающих
одновременные действия с двумя курсорами мышей, по одному для каждой руки, нет. [7]
Разработчики интерфейса смирились с реалиями и сделали проект для одной мыши, приняв синтаксическое соглашение, что первым отмечается (выбирается) существительное. Затем указывают на глагол, пункт меню. При этом в значительной мере утрачивается простота использования. Когда я наблюдаю за пользователями, просматриваю видеозапись их действий или зарегистрированные компьютером перемещения курсора, то всегда обращаю внимание на то, что одному курсору приходится выполнять работу двух: выбрать объект в окне на рабочем столе; выбрать глагол в меню; найти другой объект или вновь отыскать прежний; снова опустить меню (часто, то же самое) и выбрать глагол. Курсор мечется взад-вперед, от пространства данных к пространству меню, всякий раз теряя полезную информацию о том, где он находился в этом пространстве в прошлый раз — в целом, неэффективный процесс.
Великолепное решение. Даже если бы электроника и программы могли без труда работать одновременно с двумя активными курсорами, остаются сложности с топологией пространства. На рабочем столе в метафоре WIMP в действительности есть пишущая машинка, и в физическом пространстве реального стола необходимо поместить реальную клавиатуру. Клавиатура плюс два коврика для мышей займут немалую часть пространства в пределах досягаемости рук. Так почему бы проблему клавиатуры не обратить себе на пользу, почему не использовать обе руки — одной рукой задавая на клавиатуре глаголы, а другой рукой выбирая существительные с помощью мыши? Теперь курсор будет оставаться в пространстве данных и пользоваться тем, что последовательные существительные выбираются близко одно от другого. Реальная эффективность, реально большие возможности пользователя.
Мощность функций или простота использования. Однако при таком решении теряется то, что делает использование меню таким простым для новичков: меню представляет список альтернативных глаголов, допустимых в каждом конкретном состоянии. Можно купить коробку, принести ее домой и начать работать, не читаю инструкцию, зная лишь, для чего она куплена и экспериментируя с различными глаголами в меню.
Одна из сложнейших задач, стоящих перед архитекторами — это найти соотношение между мощностью функций и простотой использования. Нужно ли проектировать программу в расчете на новичка и случайного пользователя или строить ее с мощными функциями для профессионала? Идеальное решение – обеспечить и то, и другое концептуально согласованным образом, что достигается при помощи интерфейса WIMP. У часто используемых глаголов меню есть клавишные эквиваленты из одной клавиши + командной клавиши, которые обычно легко ввести левой рукой одним аккордом. Например, в Маке командная клавиша ( ) находится как раз под клавишами Z и X, поэтому самые частые действия кодируются как
Постепенный переход от новичка к опытному пользователю. Такая двойная система задания командных глаголов не только отвечает потребности новичка в легком обучении и потребности опытного пользователя в эффективном использовании, но и позволяет каждому пользователю плавно перейти из одного режима в другой. Буквенные обозначения, называемые клавишами сокращенного набора, показываются в меню рядом с глаголами, поэтому в случае неуверенности пользователь может раскрыть меню, чтобы проверить буквенный эквивалент, вместо выбора пункта меню. Каждый новичок запоминает сначала сокращенный набор для своих частых операций. Он может попробовать любое сокращение, в котором не уверен, поскольку отменяет любое ошибочное одиночное действие. С другой стороны, он может справиться в меню относительно допустимых команд. Новички очень часто опускают меню, опытные пользователи — редко, а тем, которые находятся посередине, лишь от случая к случаю понадобится выбирать из меню, поскольку они уже знают клавиши, которые вызывают большинство осуществляемых ими операций. Мы, проектировщики программного обеспечения, слишком привыкли к этому интерфейсу, чтобы оценить его элегантность и мощь.
Успех прямого включения как средства навязывания архитектуры. Интерфейс Мака примечателен еще в одном отношении. Без всякого принуждения разработчики сделали его стандартом для разных приложений, включая большинство из тех, которые описаны сторонними организациями. Поэтому пользователь приобретает концептуальную согласованность на уровне интерфейса не только для программ, поставляемых вместе с машиной, но и для всех других приложений.
Этот подвиг создатели Мака осуществили, встроив интерфейс в ПЗУ, в результате чего разработчикам проще и быстрее пользоваться существующим, чем создавать свои идиосинкразические интерфейсы. Это естественное стремление к единообразию возобладало настолько широко, что стало стандартом де-факто. Естественные стремления были поддержаны полной приверженностью со стороны
менеджеров и существенным принуждением со стороны Apple. Независимые рецензенты в журналах, поняв огромное значение межпрограммной концептуальной целостности, также подкрепили естественные стремления, безжалостно критикуя продукты, не удовлетворяющие этому интерфейсу.
Это отличительный пример технологии, рекомендованной в главе 6 для достижения единообразия путем поощрения других сторон непосредственно включать в свои продукты имеющийся код вместо разработки новых программ согласно имеющимся спецификациям.
Судьба WIMP: устаревание. Несмотря на все достоинства, по моему мнению, интерфейс WIMP через поколение станет достоянием истории. Указание курсором останется способом задания существительных при управлении нашими компьютерами. Для выражения глаголов станет использоваться речь. Такие инструменты, как Voice Navigator для Маков и Dragon для PC, уже предоставляют такую возможность.
Не разрабатывайте программ на выброс, каскадная модель неверна!
В главе 11 дается радикальный совет: «планируйте выбросить первую программу, вам все равно придется это сделать». Сейчас я считаю это ошибочным — не в силу чрезмерного радикализма, но в силу чрезмерной упрощенности.
Самой большой ошибкой этой концепции является косвенное принятие классической последовательности — в виде каскада — модели создания программы. Эта модель происходит от структуры диаграммы Гранта для поэтапного процесса, которую часто изображают, как на рисунке 19.1. В классической статье 1970 года Винтон Ройс (Winton Royce) усовершенствовал последовательную модель путем:
• добавления некоторой обратной связи с предшествующими этапами;
• ограничения обратной связи только непосредственными предшественниками, чтобы исключить вызываемые ими издержки и задержки в выполнении графика.
Он предвосхитил «МЧ-М», рекомендовав разработчикам «делать работу дважды» [8] . Глава 11 — не единственная, на которую повлияла каскадная модель. Она проходит через всю книгу, начиная с правила планирования в главе 2. Это практическое правило отводит 1/3 времени на планирование, 1/6 — на написание программ, 1/4 — на тестирование компонентов и 1/4 — на системное тестирование.
Рис. 19.1 Каскадная модель создания программы
Основное заблуждение каскадной модели состоит в предположениях, что проект проходит через весь процесс один раз, архитектура хороша и проста в использовании, проект осуществления разумен, а ошибки в реализации устраняются по мере тестирования. Иными словами, каскадная модель исходит из того, что все ошибки будут сосредоточены в реализации, а потому их устранение происходит равномерно во время тестирования компонентов и системы.
«Планируйте на выброс» действительно резко нападает на это заблуждение. Ошибка не в диагнозе, а в лекарстве. Сейчас я предположил, что первую систему можно отбросить или перепроектировать не всю целиком, а по частям. Хорошо, если это так, но при этом не затрагивается корень проблемы. В каскадной модели системное тестирование, а следовательно и тестирование пользователем, отодвигается в конец процесса создания программы. Поэтому есть шанс обнаружить крайние неудобства для пользователя, или неприемлемые технические характеристики, или опасную уязвимость к ошибкам или злонамеренным действиям пользователя лишь в самом конце разработки. Изучение спецификаций при альфа-тестировании нацелено на раннее обнаружение таких ошибок, но ничто не может заменить непосредственных пользователей.
Вторым недостатком каскадной модели является предположение, что система строится сразу вся целиком для тестирования с начала до конца после того, как завершены все проектные разработки, большая часть написания программ и значительная часть тестирования компонентов.
К несчастью, каскадная модель, это преобладавшее в 1975 году представление о программных проектах, была включена в DOD-STD-2167 — спецификацию Министерства обороны для любого военного программного обеспечения. По этой причине она просуществовала долгое время после того, как большая часть думающих практиков осознала ее неадекватность и отказалась от нее. К счастью, в МО позднее поняли истину. [9]
Необходимо двигаться против течения. Опыт и идеи из каждой расположенной ниже по течению части процесса создания программы, как энергичный лосось, должны прыгать вверх по течению, иногда сразу через несколько этапов, и воздействовать на деятельность наверху.
Проектные разработки покажут, что некоторые предусмотренные архитектурой возможности ухудшают технические характеристики, и потому архитектура должна быть переработана. Программирование при реализации выявит, что некоторые функции непомерно увеличивают требования к памяти, поэтому нужно внести изменения в архитектуру и разработку.
Поэтому вполне может потребоваться осуществить несколько итераций цикла архитектура-разработка, прежде чем начать какую-либо программную реализацию.
Модель пошагового создания лучше: последовательное уточнение
Построение каркаса с начала до конца. Гарлан Миллз (Harlan Mills), работающий в системе с разделением времени, давно уже рекомендует строить основной цикл опроса системы реального времени с вызовами подпрограмм (заглушками) для всех функций (рис. 19.2), но пустыми подпрограммами. Откомпилируйте его, протестируйте, и он будет идти цикл за циклом, буквально ничего не делая, но делая это без ошибок. [10]
Рис. 19.2
На следующем шаге мы навешиваем модуль ввода (возможно, примитивный) и модуль вывода. Voila! Работающая система, делающая нечто, возможно, неинтересное. Теперь, функция за функцией, мы строим и добавляем модули. На каждом шаге мы имеем работающую систему. При достаточном трудолюбии на каждом шаге мы имеем отлаженную, протестированную систему. (По мере роста системы растет и тяжесть повторного тестирования всех новых модулей по всем прежним контрольным примерам.)
После того как на примитивном уровне каждая функция работает, мы улучшаем или переписываем один модуль за другим, пошагово наращивая систему. Иногда для надежности мы переписываем исходный движущий цикл, а возможно, и его интерфейсы с модулями.
Поскольку в любой момент времени у нас есть работающая система:
• можно очень рано начать тестирование пользователями;
• можно принять стратегию разработки в соответствии с бюджетом, полностью защищающую от перерасхода времени или средств (возможно, за счет сокращения функциональности).
В течение 22 лет я преподавал в лаборатории программной инженерии Университета штата Северная Каролина, иногда вместе с Дэвидом Парнасом. На этих занятиях бригады, обычно состоявшие из четырех человек, в течение одного семестра должны были построить некоторую реальную прикладную программную систему. Примерно посередине этого срока я стал преподавать инкрементную разработку. Я был поражен, какой возбуждающий эффект на моральный дух группы оказывает первая картинка на экране, первая работающая система.
Семейства Парнаса. Дэвид Парнас был главным властителем дум в программотехнике в течение всего этого 20-летнего периода. Всем известна его идея скрытия информации. Менее известна очень важная идея Парнаса о проектировании программного продукта как семейства взаимосвязанных продуктов. [11] Он требует, чтобы проектировщик имел в виду как дальнейшее развитие, так и новые версии продукта и определял их функциональные или межплатформенные различия так, чтобы строить дерево родственных продуктов (рис. 19.3).
Рис. 19.3
Фокус при проектировании такого дерева состоит в том, чтобы ближе к корню поместить те решения, изменение которых наименее вероятно.
Такая стратегия проектирования повышает повторную используемость модулей. Еще важнее, что ее можно расширить, включая не только поставляемые продукты, но и последовательные промежуточные версии, созданные по стратегии инкрементируемых сборок. В этом случае продукт развивается через промежуточные стадии с минимумом возврата назад.
Подход Microsoft: «ежевечерняя сборка». Джеймс Маккарти (James McCarthy) описал мне процесс, использовавшийся им и другими в Microsoft. Это пошаговое наращивание, доведенное до логического конца. Он пишет:
Сделав первую поставку, новые версии мы поставляем с дополнительными функциями, по сравнению с существующим работающим продуктом. Почему должен быть иным процесс первоначальной сборки? С момента достижения нами первой вехи (у первой поставки три промежуточных вехи) мы каждый вечер заново собираем разрабатываемую систему (и прогоняем контрольные примеры). Цикл сборки становится ритмом жизни проекта. Каждый вечер одна или более бригад программистов, проводящих тестирование, регистрируют модули с новыми функциями. После каждой сборки у нас есть работающая система. Если сборка оказывается неудачной, мы останавливаем весь процесс, пока ошибка не будет найдена и исправлена. В любой момент все в группе знают положение дел.
Это действительно тяжело. Требуется выделение больших ресурсов, но это управляемый процесс, прослеживаемый и понятный. Он вызывает у команды доверие к себе. А доверие определяет мораль, эмоциональное состояние.
Такой процесс удивляет и даже шокирует разработчиков в других организациях. Один из них говорит: «Я взял себе за правило делать сборку каждую неделю, но ежедневная сборка, я думаю, потребует слишком много труда.» И это, возможно, верно. Например, в Bell Northern Research собирают систему, состоящую из 12 миллионов строк, раз в неделю.
Инкрементная сборка и быстрое макетирование. Поскольку инкрементная разработка позволяет рано начать тестирование реальными пользователями, в чем ее отличие от быстрого макетирования? Мне кажется, что они связаны, но различаются. Одним можно пользоваться без другого.
Харел дает полезное определение макета:
(версия программы, которая) отражает только проектные решения, принятые в процессе подготовки концептуальной модели, а не решения, вызванные соображениями реализации. [12]
Можно построить макет, который вовсе не является частью продукта, развивающегося в направлении поставки. Например, можно создать макет интерфейса, за которым стоит не реально работающая программа, а лишь конечный автомат, заставляющий его имитировать прохождение состояний. Можно даже макетировать и тестировать интерфейсы методом волшебника изумрудного города, когда спрятанный человек имитирует отклик системы. Такое макетирование может быть весьма полезным для быстрого получения обратной связи от пользователя, но оно находится совершенно в стороне от тестирования продукта, который готовится к поставке.
Аналогично, разработчики могут попробовать построить вертикальный срез продукта, в котором полностью реализован весьма ограниченный набор функций, чтобы заранее пролить свет на те места, где могут таиться опасности для производительности продукта. В чем состоит различие между сборкой на первой вехе в процедуре Microsoft и быстрым макетом? В функциях. Продукт с первой вехи может иметь такую ограниченную функциональность, что ни для кого не будет представлять интереса. Готовность продукта к поставке определяется завершенностью в предоставлении полезного набора функций и своим качеством, уверенностью в надежной работе.
Парнас был прав, а я — нет в отношении сокрытия информации
В главе 7 я противопоставляю две точки зрения на то, в какой мере каждый участник команды может иметь право или поощряться к знанию проектов и текстов программ, созданных коллегами. Во время проекта Operating System/360 мы решили, что все программисты должны видеть весь материал, т.е. у каждого программиста была рабочая тетрадь проекта, которая к концу насчитывала свыше 10000 страниц. Харлан Миллз убедительно доказывал, что «программирование должно быть открытым процессом», что предоставление всей работы на общее обозрение способствует контролю качества как благодаря давлению со стороны коллег, заставляющему работать хорошо, так и благодаря тому, что коллеги действительно находят промахи и ошибки.
Этот взгляд резко противоречит мнению Дэвида Парнаса о том, что программные модули должны быть инкапсулированы, с хорошо определенными интерфейсами, а внутренность таких модулей должна быть частной собственностью программиста, невидимой снаружи. Программисты эффективнее всего работают, будучи ограждены от внутренностей чужих модулей. [13]
В главе 7 я заклеймил идею Парнаса как «рецепт катастрофы». Парнас был прав, а я ошибался. Сегодня я убежден, что ограничение информации, часто осуществляемое теперь методами объектного программирования, является единственным способом поднять уровень программных разработок.
Используя другие методы, можно действительно попасть в беду. Согласно методике Миллза программисты могут получить подробности семантики интерфейсов, с которыми они работают, узнав, что находится «по ту сторону». Укрывание этой семантики может быть причиной системных ошибок. С другой стороны, методика Парнаса способствует устойчивости при внесении изменений и больше подходит к стратегии проектирования, предполагающей изменения в будущем.
В главе 16 утверждается следующее:
• большая часть роста производительности разработки программного обеспечения обеспечена устранением второстепенных трудностей, таких как неудобные языки программирования и медленная оборачиваемость пакетной обработки;
• легких решений в этом направлении практически не осталось;
• радикального прогресса можно добиться, разрешив существенные сложности моделирования сложных концептуальных конструкций.
Самое очевидное на этом пути — признать, что программы составляются из концептуальных блоков, значительно более крупных, чем отдельные операторы языков высокого уровня: подпрограмм, или модулей, или классов. Если мы сумеем ограничить проектирование и построение программ задачей соединения вместе и параметризации таких блоков из ранее созданных наборов, то радикально повысим концептуальный уровень и избавимся от огромного объема работ и широких возможностей для ошибок, существующих на уровне отдельных операторов.
Данное Парнасом определение модулей с сокрытием информации было первым открытым шагом в этой критически важной программе исследований и идейным провозвестником объектно-ориентированного программирования. Он определил модуль как программный объект с собственной моделью данных и собственным набором операций. Доступ к его данным может быть осуществлен только через имеющиеся в нем операции. Следующий шаг явился вкладом нескольких исследователей: развитие модулей Парнаса в абстрактный тип данных, из которого можно производить много объектов. Абстрактный тип данных обеспечивает единообразный способ представления и задания интерфейсов модулей, а также дисциплину доступа, которую легко осуществлять.