Девять фаз цикла программирования
Как мы видели, Форт интегрирует аспекты проектирования с вопросами реализации и поддержки. В результате этого упоминание о "типичном цикле разработки" звучит примерно так же, как и упоминание о "типичном шуме".
Однако любой подход лучше, чем отсутствие подхода и, разумеется, некоторые из подходов разработаны лучше других. Вот цикл разработки, представляющий некий "средний" из наиболее успешных способов, применяемых в программных проектах:
`Анализ`
`Конструирование`
`Использование`
В этой книге мы рассматриваем первые шесть стадий цикла, делая акцент на анализе, проектировании и реализации.
В Форт-проекте перечисленные фазы появляются на нескольких уровнях. Глядя на проект в самой широкой перспективе, можно сказать, что каждый из трех шагов может занимать месяц или более. Одна стадия сменяет другую, как времена года.
Но Форт-программисты применяют те же самые фазы при определении каждого слова. В этом случае цикл повторяется в течение минут.
Разработка программы с таким быстрым повторением программного цикла известна как использование "итеративного подхода".
Итеративный подход был красноречиво описан Кимом Харрисом [1]. Он начинает с определения научного метода:
... бесконечный цикл открытий и улучшения. Он вначале изучает естественную систему и собирает сведения о ее поведении. Затем наблюдения моделируются для выработки теории об естественной системе. Далее инструменты анализа применяются к модели, что позволяет выдать предсказания о поведении реальной системы. Проводятся эксперименты с тем, чтобы сравнить истинное поведение с предсказанным. Природная система вновь изучается, и модель пересматривается.
`Целью` метода является выработка модели, которая в точности предсказывает все обозримое поведение естественной системы.
Затем Харрис применяет научный метод к циклу разработки программ, проиллюстрированному на рис. 2-1:
1. Проблема анализируется для выявления функций, требуемых для ее решения. 2. Принимаются решения о том, как достигнуть реализации этих функций с использованием доступных ресурсов. 3. Пишется программа, с помощью которой предпринимается попытка реализации проекта. 4. Программа проверяется на предмет правильности реализации функций.
Рис.2-1. Итеративный подход к циклу разработки программ, из статьи "Философия Форта" Кима Харриса. ("The FORTH Philosophy," by Kim Harris, Dr. Dobb's Journal.)
НОВЫЕ ТРЕБОВАНИЯ _____ФУНКЦИИ_____ НОВЫЕ ОГРАНИЧЕНИЯ ~|~~~~~~~~~~~~~~ / \ ~~~~~~~~~~~~~|~~~ | / \ | | +------------+----+ +---\------------+ | --->| АНАЛИЗ ПРОБЛЕМЫ | | ПРОЕКТИРОВАНИЕ |
М-р Харрис добавляет:
Разработка программ на Форте в первую очередь направлена на поиск самого простого решения данной проблемы. Это достигается реализацией выбранных частей задачи раздельно и игнорированием возможно большего числа ограничений. Затем вводятся одно или несколько ограничений, и программа модифицируется.
Превосходным свидетельством в пользу модели проектирования вида разработка/проверка является эволюция. От протоплазмы к головастику и к человеку, каждый вид на этом пути состоит из функциональных, жизнеспособных существ. Создатель явно не выглядит проектировщиком "сверху-вниз".
------------------------------------------------------------ СОВЕТ Начинайте с простого. Пусть оно заработает. Поймите, что именно Вы пытаетесь получить. Добавляйте сложность понемногу, насколько это необходимо для выполнения требований и ограничений. Не бойтесь начинать сначала, запутавшись в мусоре. ------------------------------------------------------------
ОБЬЕМ ПЛАНИРОВАНИЯ
В девяти фазах, перечисленных в начале этой главы, мы выделили пять шагов, предпринимаемых `перед` "разработкой". Раньше в Главе 1 мы увидели, что чрезмерное увлечение планированием одновременно и сложно, и бесцельно.
Ясно, что нельзя предпринимать важный программный проект - независимо от языка - без некоторого количества планирования. Какое же количество оптимально?
----------------------------------------------------------------
Многие Форт-программисты выражали глубокую признательность дотошному подходу к планированию Дейва Джонсона. Джонсон работает супервизором в фирме Moore Products Co. в Спрингсхаузе, штат Пенсильвания. Фирма специализируется на приложениях промышленной автоматизации и управлении процессами. Дейв использует Форт с 1978 года.
Вот как он описывает свой подход:
По сравнению со многими другими пользователями Форта я полагаю, что необходим более формальный подход. Я научился этому, пройдя тяжелый путь. Моя недисциплинированность в первые годы ныне как призрак посещает меня.
Мы используем два инструмента для начала проектирования новых продуктов: функциональное описание и проектное описание. Наш отдел продаж и применений выдает функциональное описание в соответствии с контрактом заказчика.
Когда мы договорились о том, что мы собираемся делать, функциональное описание приходит в наш отдел. С этого момента мы прорабатываем проект и выдаем проектное описание.
До этого момента наш подход не отличается от программирования на любом другом языке. Но с Фортом мы подходим к проектированию несколько по-другому. На Форте не надо проделывать 95% проектной работы до начала кодирования, достаточно 60% перед входом в итеративный процесс.
Типичный проект может состоять в функциональном усовершенствовании одного из наших продуктов. К примеру, у нас есть интеллектуальный терминал с дисководами и нам нужно реализовать определенные протоколы для связи с другим устройством. Проектирование протоколов, связи с дисплеями, интерфейса с оператором и т.д. может занять несколько месяцев. Функциональное описание занимает месяц; проектное описание берет месяц; кодирование отнимает три месяца; сборка и проверка продлится еще месяц.
Это - типичный цикл. Один проект занимал почти два года, из которых шесть или семь месяцев - обоснованно.
Когда мы пять лет назад принялись за Форт, это было не так. Как только я получал функциональное описание, я немедленно начинал кодирование. Я использовал нечто среднее между разработкой сверху-вниз и снизу-вверх, в основном определяя структуру и, по мере необходимости, некоторые низкоуровневые слова, затем вновь возвращаясь к структуре. Причиной такого подхода было огромное давление в сторону демонстрации чего-нибудь начальству. Мы крутились, как заведенные, никогда не описывая того, что делали. Через три года нам понадобилось вернуться назад и пытаться модифицировать этот код, без какой-либо документации. Форт стал недостатком, поскольку позволил нам начать слишком рано. Было забавно зажигать огоньки и заставлять жужжать дисководы. Но мы не проходили через горнило проектной работы. Как я сказал, наши "свободные духи" вернулись к нам в виде призраков.
Ныне для новых программистов мы установили требование: полное проектное описание, которое определяет в деталях все высокоуровневые Форт-слова - задачи, которые Ваш проект должен выполнять. Нет больше чтения нескольких страниц функционального описания, ответа на них, чтения еще нескольких, ответа на них и т.д.
Ни один из живущих программистов не любит документирование. Имея предварительный проект, мы имеем возможность просмотреть все через несколько лет и вспомнить, что мы делали. Я должен заметить, что во время проектной фазы надо написать некоторое количество кода для проверки конкретных мыслей. Но этот код может и не быть частью законченного продукта. Идея состоит в том, чтобы обдумать Ваш проект.
----------------------------------------------------------------
Джонсон советует нам составлять полное проектное описание до начала кодирования, за исключением необходимых предварительных тестов. Следующее интервью поддерживает эту точку зрения и добавляет новые аргументы.
----------------------------------------------------------------
Джон Телеска стал независимым программистом-консультантом с 1976 года, специализируясь на традиционных для академического исследовательского оборудования задачах.
Ему нравится снабжать исследовательские инструменты "как раз тем, что способна дать новейшая технология". Телеска работает в Рочестере, штат Нью-Йорк:
Я вижу две фазы в процессе разработки программ. Первая состоит в том, чтобы убедиться в моем понимании проблемы. Вторая заключается в реализации, включая отладку, верификацию и т.д.
Моя цель в фазе первой - операционное описание. Я начинаю с описания задачи, и по мере работы над ним получаю операционное описание - мое понимание того, как проблема преобразуется в решение. Чем лучше понимание, тем полнее решение. Я добиваюсь завершенности; чувства того, что больше нет вопросов, на которых нет ответа на бумаге.
Я понял, что чем больше времени в каждом проекте я трачу на первую фазу, тем больше беспокойство большинства моих клиентов. Ограничивающим фактором является то количество необходимого для этого периода времени, на которое мне удастся убедить клиента. Потребители обычно не знают характеристик работы, которую они хотят получить. И у них нет денег - или у них нет чувства того, что они есть - для траты на хорошие описания. Частью моей работы является убедить их в том, что не иметь их в конце концов обойдется дороже и по деньгам, и по времени.
Часть первой фазы тратится на изучение возможных решений. При написании спецификаций всплывают неопределенности. Я пытаюсь быть настолько неопределенным по отношению к неопределенностям, насколько это возможно. Например, им хочется собирать 200000 отсчетов в секунду с определенной точностью. Я в первую очередь должен проверить, что это вообще возможно на имеющимся у них оборудовании. При этом мне нужно для проверки возможностей написать заплатку из кода.
Другим аргументом в пользу описаний является прикрытие самого себя. В случае, если задача работает по спецификации, но не полностью удовлетворяет потребителя, то за это несет ответственность он. Если ему захочется большего, то мы должны договориться заново. Но я думаю, разработчик отвечает за то, чтобы сделать все возможное для выдачи операционного описания, которое будет работать к удовлетворению покупателя.
Я думаю, что есть консультанты, уступающие давлению клиента и ограничивающие время, затрачиваемое на описания из страха потерять работу. Но в таких ситуациях ни для кого не бывает счастливого конца.
----------------------------------------------------------------
Мы вернемся к интервью с Телеской через мгновение.
ОГРАНИЧЕНИЯ ПЛАНИРОВАНИЯ
Опыт научил нас намечать свой путь перед началом кодирования. Но у планирования есть и определенные ограничения. Следующие интервью дают различные оценки для количества планирования.
----------------------------------------------------------------
Несмотря на предпочтение Телеской хорошо спланированного проектирования, он рекомендует выбирать между подходом сверху-вниз и снизу-вверх в зависимости от ситуации:
В двух недавних проектах, содержавших много технической стыковочной работы, я проделал все снизу-вверх. Я перемолол кипу бумаг с данными и техническими описаниями мелких щелей операционной системы, с которой я имел дело. Большую часть времени я чувствовал себя потерянным, не понимая, зачем я вообще взялся за эту работу. Затем я достиг некоторой критической массы известного вида и начал составлять вместе маленькие программы, что приводило к возникновению маленьких вещей. Я продолжал, снизу-вверх, пока не достиг уровня целевого приложения.
Мой дух проектировщика "сверху-вниз" был напуган этой процедурой. Но я столько раз наблюдал себя успешно проходящим через этот процесс, что не могу сбросить его со счетов по какой-нибудь педагогической причине. И всегда бывает эта трудная стадия, которую, кажется, не преодолеет никакое количество линейного мышления. Программирование кажется гораздо более интуитивным, чем мы, им занимающиеся, склонны говорить друг другу. Я думаю, что когда задача порождает такое чувство потерянности, мне надо работать снизу-вверх. Если я ощущаю себя на своей территории, то тогда, по-видимому, изберу более традиционный, книжный подход.
----------------------------------------------------------------
А вот еще одна точка зрения:
----------------------------------------------------------------
Во время нашего интервью Майкл Старлинг из фирмы Union Carbide делал последние штрихи в двух программах, касающихся конфигурируемых пользователем лабораторных и производственных систем автоматизации. Для опытной фабричной системы Старлинг разработал как аппаратуру, так и программное обеспечение в соответствии с известными требованиями; на лабораторной системе он также сам определял требования.
Его усилия увенчались полным успехом. Для одного проекта новая система стоит лишь 20% от цены аналогичной системы и требует дней, а не месяцев, для установки и конфигурирования.
Я спросил его, какую технику он применял для поддержки проекта.
Для обеих задач требовалось большое количество проектирования. Тем не менее я не следовал традиционным методам анализа. Я осуществил следующие шаги: Во-первых, я четко отследил ограничения задачи. Во-вторых, я определил, какими должны быть минимальные функциональные куски - программные подсистемы. В-третьих, я сделал каждый кусочек, составил их вместе, и система стала работать.
Затем я спросил пользователей: "Это удовлетворяет вашим требованиям?" В чем-то оно не удовлетворяло, причем таким образом, каким ни пользователи, ни разработчики проекта не могли предугадать.
К примеру, разработчики не осознавали, что начальное описание не предусматривало приятные, ориентированные на человеческое восприятие графические картинки. При работе с интерактивной графикой первой версии пользователи применяли фиксированные масштабы графики и получали странные картинки.
Поэтому, даже уже после того, как был разработан базовый алгоритм графики, мы осознали, что необходимо было делать автоматическое масштабирование. Мы вернулись назад, изучили, как человеческие существа чертят графики, и написали функцию нижнего уровня, которая подсчитывают данные по осям X и Y и как они разместятся на графике. После этого мы осознали, что не все взятые данные будут представлять интерес для экспериментаторов.
Поэтому мы добавили возможность увеличения отдельных кусков.
Такой итеративный подход позволил получить более четкий и лучше продуманный код. Мы выявили основную линию целей и построили минимальную систему, отвечающую известным требованиям пользователя. Затем мы покопали наш программистский опыт для его улучшения и установили, что из нужного заказчики позабыли при составлении спецификации. Пользователи, в основном, новых идей не придумали. Это сделали программисты, и они отделяют эти идеи от идей пользователей. Постановка задачи оказалась улицей с двусторонним движением. В некоторых случаях они получали такие вещи, о которых и не думали, что они возможны на таком маленьком компьютере - например, применение цифровых фильтров и процессоров сигналов к данным.
Одним из свойств Форта, сделавших возможным такой подход, является то, что примитивы легко тестируются. Требуется некоторый опыт работы с Фортом, чтобы получать от этого преимущества. Ребята с традиционным воспитанием хотят написать за своим столом десять страниц кода, потом сесть и ввести их, и ожидают, что это будет работать.
Вот вкратце мой подход: я пытаюсь установить, что нужно пользователям, но в то же время осознаю неполность этих сведений. Затем я держу их вовлеченными в проект во время реализации, поскольку они должны выполнять роль экспертов. Когда они видят результат, это им приятно, поскольку известно, что их идеи использованы.
Итеративный подход позволяет достичь наивысших результатов при создании хорошего решения для реальной проблемы. Он может не всегда дать Вам объявленную заранее стоимость программного обеспечения. Путь решения может зависить от Ваших приоритетов. Запомните:
Хорошее Быстрое Дешевое
Выбирайте любые два качества!
----------------------------------------------------------------
Как говорит Старлинг, Вы как не знаете толком, что делаете, до тех пор, пока один раз это не пройдете. Мой опыт показывает, что наилучший способ написать программу - это написать ее дважды. Выкиньте первую версию, приняв ее за набросок.
----------------------------------------------------------------
Питер Кожж входит в основной технический состав подразделения федеральных систем фирмы IBM, Освего, штат Нью-Йорк:
Одним из ключевых преимуществ, которые я нахожу в Форте, является то, что он позволяет мне быстро создавать прототипы задачи без колокольного звона и свистопляски, зачастую с существенными ограничениями, однако в виде, достаточном для запуска "человеческого интерфейса" из подручных средств.
Когда я строю прототип, я делаю это с твердой уверенностью в том, что не использую ни строчки из текста прототипа в конечной программе. Эта вынужденная "переделка" почти всегда приводит к значительно более простым и более элегантным законченным программам, даже если последние написаны на чем-то, отличном от Форта.
----------------------------------------------------------------
Каковы наши выводы? В окружении Форта планирование необходимо. Но оно должно быть коротким. Тестирование и построение прототипов - наилучшие пути для понимания того, что именно действительно нужно.
Одно слово в предостережение руководителям проектов: если Вы наблюдаете за любым опытным Форт-программистом, то не надо беспокоиться насчет того, что он тратит слишком много времени на планирование. Так что следующий совет имеет две версии:
------------------------------------------------------------ СОВЕТ Для новичков в Форте (с "традиционным" воспитанием): Сокращайте до минимума фазу анализа. Для приверженцев Форта (без "традиционной" подготовки): Воздерживайтесь от кодирования столь долго, сколько сможете выдержать. ------------------------------------------------------------
Или, как мы упоминали в первой главе:
------------------------------------------------------------ СОВЕТ Планируйте изменения (проектируя компоненты, которые могут быть изменены). ------------------------------------------------------------
Или просто:
------------------------------------------------------------ СОВЕТ Делайте прототипы. ------------------------------------------------------------
ФАЗА АНАЛИЗА
В оставшейся части этой главы мы обсудем фазу анализа. Анализ - это организованный путь к пониманию и документированию того, что должна делать программа.
Для простой программы, которую Вы пишете для себя меньше чем за час, фаза анализа может занять около 250 микросекунд. В другой крайности некоторые проекты займут много человеко-годов. В таком проекте фаза анализа определяет успех всего проекта.
Мы указывали на три части фазы анализа:
1. Установка требований и ограничений 2. Построение концептуальной модели решения 3. Оценка цены/графика работ/производительности
Давайте вкратце опишем каждую часть:
УСТАНОВКА ТРЕБОВАНИЙ.
Первым шагом является выяснение того, что должна делать задача. Покупатель или вообще тот, кто хочет иметь систему, должен предоставить "описание требований". Это - честный документ, перечисляющий минимальные возможности конечного продукта.
Аналитик может также делать дальнейшие пробы, проводя беседы и рассылая вопросники пользователям.
УСТАНОВКА ОГРАНИЧЕНИЙ.
Следующиий шаг заключается в определении ограничивающих факторов. Насколько важна скорость? Сколько доступно памяти? Как быстро нужно получить разработку?
Независимо от утонченности нашей технологии, программисты всегда будут биться об ограничения. Возможности системы необъяснимым образом уменьшаются со временем. Дисководы с двойной плотностью записи, однажды послужившие ответом на мои молитвы, ныне не удовлетворяют меня. Двусторонние, с двойной плотностью дисководы, которые я заведу следующими, покажутся безграничными - на время. Я слышал жалобы на тесноту от людей с 10-ю мегабайтовыми жесткими дисками.
Где бы ни ощущалась нехватка чего-либо - а она всегда будет - следует делать компромиссы. Лучше использовать фазу анализа для противостояния большинству ограничений и принятия решений о том, какие нужны компромиссы.
С другой стороны, во время анализа Вы `не` должны принимать во внимание другие типы ограничений, преодолевая их постепенно во время реализации по типу того, как растирают комки в тесте.
Во время анализа следует принимать во внимание те типы ограничений, которые могут повлиять на подход в целом. Отложить следует те, которые могут быть учтены во время итеративного улучшения спланированного программного проекта.
Как мы слышали в предыдущих интервью, выявление `аппаратных` ограничений часто требует написания некоторого количества пробного кода и испытаний. Выявление ограничений со стороны `покупателя` обычно сводится к заданию ему вопросов или получению письменных обзоров. "Насколько быстро Вам необходимо то-то и то-то, в размере от одного до десяти?" и т.д.
ПОСТРОЕНИЕ КОНЦЕПТУАЛЬНОЙ МОДЕЛИ РЕШЕНИЯ.
Концептуальная модель - это воображаемое решение проблемы. Это - взгляд на то, как система `должна` работать. Это - ответ на все требования и ограничения.
Если определение требований звучит как "нечто, на чем можно стоять при покраске потолка", то описанием концептуальной модели будет "устройство, стоящее свободно (так, что можно красить в середине комнаты), с несколькими ступенями, отделенными друг от друга одинаковыми интервалами (так, что можно забираться вверх и вниз) и имеющее маленькую полку на вершине (для банки с краской)".
Концептуальная модель, однако - это не совсем проект. Проект начинается с описания того, как система `в действительности` работает. В проекте должен был бы начать появляться образ лестницы со ступеньками.
Форт несколько размывает это различие, поскольку все определения пишутся в концептуальных терминах, используя лексиконы компонентов нижних уровней. На самом деле, позже в этой главе мы будем использовать `псевдокод` на Форте для описания реализаций концептуальной модели.
Несмотря на это, полезно делать различие. Концептуальная модель более гибка, чем проект. Легче войти в рамки требований и ограничений в модели, чем в проекте.
------------------------------------------------------------ СОВЕТ Старайтесь построить солидную концептуальную модель перед началом проектирования. ------------------------------------------------------------
Анализ состоит из расширения описания требований до уровня концептуальной модели. Технология включает двухстороннюю связь с покупателем в попытках достичь успеха в описании модели.
Как и весь цикл разработки, в фазе анализа лучше всего итеративный подход. Каждое новое требование будет пытаться добавить что-то в Вашу мысленную модель. Ваша работа состоит в жонглировании всеми этими требованиями и ограничениями до тех пор, пока Вы не сплетете основу, которая им удовлетворяет.
Рисунок 2-2 иллюстрирует итеративный подход к фазе анализа. Последний шаг является одним из наиболее важных: показать задокументированную модель покупателю. Используйте любые необходимые средства связи - диаграммы, таблицы или картинки - для доведения Вашего понимания до заказчика и получайте необходимую обратную связь. Даже если Вы проделаете весь цикл сто раз, эти усилия не потеряют свою ценность.
Рис.2-2. Итеративный подход к анализу.
ПОЖЕЛАНИЯ ЗАКАЗЧИКА ____ТРЕБОВАНИЯ___ ~|~~~~~~~~~~~~~~~~~ / \ | / \ | +------------+-----+ +---\----------+ --->| ПОСТАВИТЬ ЗАДАЧУ | | ВЗВЕСИТЬ | +---------/--------+ | ТРЕБОВАНИЯ И | / | ОГРАНИЧЕНИЯ | ОЦЕНКА/ПРИЕМКА +--------+-----+ ЗАКАЗЧИКА / \ УТОЧНЕННЫЕ ТРЕБОВАНИЯ +---------+----------+ +-----------/------------+ | ПРОДЕМОНСТРИРОВАТЬ | | ОПИСАТЬ КОНЦЕПТУАЛЬНУЮ | | МОДЕЛЬ ЗАКАЗЧИКУ | | МОДЕЛЬ | +------------\-------+ +--------+---------------+ \ / \_ЗАДОКУМЕНТИРО-_/ ВАННАЯ МОДЕЛЬ
В следующих двух разделах мы исследуем три вида техники для определения и описания концептуальной модели:
1. определение интерфейсов 2. определение правил 3. определение структур данных.
ОПРЕДЕЛЕНИЕ ИНТЕРФЕЙСОВ
------------------------------------------------------------ СОВЕТ Первое и самое важное: концептуальная модель должна описывать интерфейсы системы. ------------------------------------------------------------
----------------------------------------------------------------
Телеска:
"Описание" обычно имеет дело с понятием ЧТО. В самом лучшем случае оно должно показывать то, на что система будет похожа для пользователя - можно назвать это руководством пользователя.
Мне кажется, что я пишу больше замечаний по взаимодействию с человеком - как это выглядит снаружи - чем по той части, которая в действительности выполняет работу. К примеру, я составляю полное описание обработки ошибок для того, чтобы показать, что происходит при определенных событиях. К несчастью, эта часть заодно отнимает и большую часть времени при реализации.
В настоящее время я работаю над твердотельным таймером для промышленной стиральной машины. В данном случае интерфейс с пользователем не так сложен. Что сложно, так это интерфейс со стиральной машиной, в котором я должен придерживаться требований заказчика и той документации, которую он может предоставить.
Интерфейс - это руки и ноги для продукта. На ранней стадии я не делаю различия между аппаратурой и программным обеспечением. Они оба могут взаимозаменяться при реализации.
Процесс проектирования аппаратуры и процесс проектирования программ аналогичны. Путь, по которому я разрабатываю аппаратуру - это представление ее в образе черного ящика. Передняя панель - это вход и выход. То же самое можно сделать и с программным обеспечением.
Я использую всякие приемы, диаграммы и т.п. для того, чтобы показать покупателю, как выглядят входы/выходы, использую описания того, что продукт должен делать. Но, параллельно, в воображении, я представляю как он должен быть реализован. Вычисляю, насколько эффективно я могу это сделать. Так что для меня это не черный ящик, это серый ящик. Проектировщик должен быть способен видеть внутри черных ящиков.
При проектировании системы получаются различные модули, я стараюсь сделать их взаимосвязь настолько рациональной и настолько маленькой, насколько возможно. Но всегда где-то находишь, а где-то теряешь, поскольку изменяешь идеалу с компромиссами.
Для самого документа я использую ДПД [диаграммы потоков данных, которые мы обсудим позже] и любые другие виды представления, которые я могу продемонстрировать клиентам. Я показываю им сколько возможно много диаграмм для пояснения своего понимания.
Я обычно не использую их, когда дело доходит до реализации. Текст должен быть полноценен даже и без ссылок на диаграммы.
----------------------------------------------------------------
------------------------------------------------------------ СОВЕТ Решайте вопросы обработки ошибок и исключительных случаев заранее, при определении интерфейсов. ------------------------------------------------------------
Верно то, что при написании программ для себя программист зачастую может сконцентрироваться на правильности работы кода при `нормальных` условиях и побеспокоиться об обработке ошибок позже. Однако при работе на других обработка ошибок должна быть проработана в первую очередь. Эта та область, которая часто выпадает из поля зрения начинающих программистов.
Причина важности принятия решений по обработке ошибок на этой стадии состоит в широком спектре методов их обработки. Ошибка может быть:
* игнорируемая * устанавливающая флаг индикации ошибки при продолжении процесса * немедленно останавливающая процесс * вызывающая процедуру для корректировки задачи и продолжения работы.
Простор для возникновения серьезной коммуникационной бреши открывается в том случае, если уровень сложности при обработке ошибок заранее не отслежен. Очевидно, что этот выбор чрезвычайно сильно отражается на проекте и реализации задачи.
------------------------------------------------------------ СОВЕТ Разрабатывайте концептуальную модель, представляя себе, как данные проходят, и какие действия над ними производятся в частях модели. ------------------------------------------------------------
Дисциплина, называемая `структурный анализ` [2], предлагает некоторые способы описания интерфейсов таким образом, что их легко поймут Ваши клиенты. Один из таких способов, упоминавшихся Телеской, называется "диаграммой потоков двнных" (ДПД).
Диаграмма потоков данных, типа изображенной на рис. 2-3, подчеркивает преобразования данных при их прохождении через систему. Круги представляют собой "преобразования", функции, воздействующие на информацию.
Стрелки показывают входы и выходы преобразований.
Рис.2-3. Диаграмма потоков данных.
-->\ (из производственной линии) \ ЗАПРОС НА ЗАПРОС НА МАТЕРИАЛЫ ЗАКУПКУ \ / \ \ +-----------+/ +\--------------------+ \| ПРОВЕРИТЬ + | ПОЛУЧИТЬ РАЗРЕШЕНИЕ | \ НАЛИЧИЕ | | НА ЗАКУПКУ | +------+----+ +------+--------------+
Диаграмма живописует застывший момент в работе системы. Она оставляет без внимания инициализацию, структуры циклов и другие детали программирования, которые зависят от времени.
При использовании ДПД достигаются три плюса:
Во-первых, они дают сведения заказчику при помощи простых, прямых терминов. Если он соглашается с содержимым Вашей диаграммы потоков данных, то Вы знаете, что понимаете проблему.
Во-вторых, они помогают Вам думать в терминах логических "ЧТО", без углубления в процедурные "КАК", что согласуется с философией упрятывания информации, обсужденной нами в предыдущей главе.
В-третьих, они фокусируют Ваше внимание на интерфейсах к системе и между модулями.
Несмотря на это, Форт-программисты редко используют ДПД иначе как для заказчика. Форт способствует Вашему мышлению в терминах концептуальной модели, а скрытое использование стека данных в Форте делает передачу данных между модулями настолько простой, что ее обычно можно принимать как само собой разумеющуюся. Это происходит потому, что Форт, будучи использован правильно, приближается к уровню функционального языка.
Даже для тех, кто всего несколько дней знаком с Фортом, простые определения содержат по крайней мере столько же смысла, сколько диаграммы:
: ЗАПРОС ( количество наименование -- ) ПОД-РУКОЙ? IF ВЫДАТЬ ELSE ПЕРЕАДРЕСОВАТЬ THEN ; : ПЕРЕАДРЕСОВАТЬ ЗАВЕРЕНО? IF ПОКУПКА THEN ; : ПОКУПКА АРХИВНАЯ КОПИЯ СКЛАДСКАЯ КОПИЯ ЗАКУПОЧНАЯ КОПИЯ ;
Это - псевдокод на Форте. Не было сделано никаких попыток конкретизировать, какие величины в действительности передаются через стек, поскольку это - детали реализации. Стековый комментарий для слова ЗАПРОС использован только для указания двух видов данных, необходимых для начала процесса.
(Если бы я проектировал эту задачу, я бы посоветовал, чтобы пользовательский интерфейс был представлен словом НУЖНО со следующим синтаксисом:
НУЖНО 50 ПОДШИПНИКИ
НУЖНО преобразует число в величину на стеке, переводит строку ПОДШИПНИКИ в номер запасной части, также оставляя его на стеке, затем вызывает ЗАПРОС. Такая команда могла бы быть определена только на самом удаленном от нижнего уровне.)
----------------------------------------------------------------
Джонсон из фирмы Moore Products Co. сказал несколько слов насчет Форт-псевдокода:
IBM использует строго документированный PDL (ЯПП - язык проектирования программ). Мы также используем PDL, хотя и называем его FDL, т.е. язык проектирования на Форте. Наверное, расточительно иметь все эти стандарты, однако, если Вы знакомы с Фортом, то Форт сам может быть языком проектирования. Надо только отбросить так называемые "шумовые" слова: C@, DUP, OVER и т.п. и показывать только основной поток. Большинство работающих на Форте делают это интуитивно. Мы же делаем это целенаправленно.
----------------------------------------------------------------
Во время одного из наших интервью я спросил Мура, использовал ли он какой-нибудь вид диаграмм для планирования концептуальной модели или кодировал прямо на Форте. Вот его ответ:
Концептуальной моделью `является` Форт. Я годами учился думать в этом духе.
Может ли кто-нибудь научиться так думать?
У меня есть нечестное преимущество. Я закодировал свой стиль программирования, и другие люди восприняли его. Я был поражен тем, что это случилось. И я чувствую, что это приятное преимущество, поскольку ведь это мой стиль другие пытаются скопировать. Могут ли они научиться думать так же, как я? Мне кажется, что могут. Это - всего лишь вопрос практики, а у меня практики больше.
----------------------------------------------------------------
ОПРЕДЕЛЕНИЕ ПРАВИЛ
Большая часть наших усилий при описании задачи концентрируется вокруг описания интерфейса. Но некоторые задачи требуют, чтобы Вы также определили набор правил для их решения.
Любое программирование опирается на правила. Обычно они настолько просты, что почти не играет роли, как Вы их выражаете: "если кто-либо нажмет кнопку, то зазвонит колокольчик".
Однако некоторые задачи содержат правила настолько сложные, что их нельзя выразить в нескольких фразах. Несколько формальных приемов могут быть Вам полезны для понимания и документирования таких сложных правил.
Вот пример. Имеющиеся требования описывают систему для вычисления платы за дальние телефонные переговоры. Вот объяснения заказчика о структуре платежей. (Я сам это придумал; не имею никакого представления о том, как в действительности телефонная компания вычисляет эти тарифы, знаю только, что всегда приходится переплачивать.)
Все тарифы исчисляются по-минутно, в соответствии с расстоянием в милях, плюс постоянная плата. Постоянная плата для прямого вызова по рабочим дням между 8 утра и 5 вечера составляет .30 (0.3 доллара) за 1-ю минуту и .20 за каждую последующую; кроме того, каждая минута увеличивается на .12 за 100 миль. Постоянная плата в будни между 5 вечера и 11 вечера составляет .22 за первую минуту и .15 за каждую дополнительную; плата за расстояние - .10 на минуту за 100 миль. Постоянная плата для прямых вызовов поздно в будни от 11 вечера или в любое время в субботу, воскресенье или праздники составляет .12 за первую минуту и .09 для каждой следующей; наценка за расстояние на минуту .06 за 100 миль. Если вызов требует помощи оператора, постоянная плата увеличивается на .90 назависимо от времени.
Это описание составлено на старом добром русском языке, и оно крайне многословно. Его трудно отслеживать и, смахивая на чердак, заваленный старым хламом, оно может даже содержать несколько ошибок.
При возведении концептуальной модели такой системы мы должны описать структуру платежей однозначным, удобным образом. Первым шагом на пути к расчистке завала является вычленение независимых кусков информации - следуя применению правила ограниченной связности. Мы может сильно улучшить этот текст, разбив его на утверждения.
Во-первых, это правило времени дня:
Вызовы в будни между 8 утра и 5 вечера идут по "полной" оплате. Вызовы в будни между 5 вечера и 11 вечера идут по "среднему" тарифу. Вызовы в будни от 11 вечера или по субботам, воскресеньям и праздникам оплачиваются по "низшему" тарифу.
Затем следует сама структура платежей, которая должна быть описана в терминах "тарифа за первую минуту", "тарифа за дополнительную минуту", "тарифа за расстояние" и "тарифа за работу оператора".
------------------------------------------------------------ СОВЕТ Разделите фрукты. (Не путайте яблоки с апельсинами.) ------------------------------------------------------------
Однако эти эпистолярные утверждения все еще трудно читать. Системные аналитики используют несколько способов для упрощения таких выражений: структурированный английский (русский) язык, деревья решений и таблицы решений. Давайте изучим каждую из этих технологий и прикинем их полезность в среде Форта.
СТРУКТУРИРОВАННЫЙ АНГЛИЙСКИЙ.
Структурированный английский (у нас - русский) - это вид структурированного псевдокода, при котором наше выражение для платежей будет читаться как-то вроде:
IF полный тариф IF прямой вызов IF первая минута .30 + .12/100миль ELSE ( дополн. минута) .20 + .12/100миль ENDIF ELSE ( оператор) IF первая минута 1.20 + .12/100миль ELSE ( дополн. минута) .20 + .12/100миль ENDIF ENDIF ELSE ( не полный тариф) IF средний тариф IF прямой вызов IF первая минута .22 + .10/100миль ELSE ( дополн. минута) .15 + .10/100миль ENDIF ELSE ( оператор) IF первая минута 1.12 + .10/100миль ELSE ( дополн. минута) .15 + .10/100миль ENDIF ENDIF ELSE ( низкий тариф) IF прямой вызов IF первая минута .12 + .06/100миль ELSE ( дополн. минута) .09 + .06/100миль ENDIF ELSE ( оператор) IF первая минута 1.02 + .06/100миль ELSE ( дополн. минута) .09 + .06/100миль ENDIF ENDIF ENDIF ENDIF
Это совершенно ужасно. Это трудно читать, еще труднее понимать и труднее всего писать.
И, мало того, совершенно бесполезно для реализации. Я более не хочу об этом даже говорить.
ДЕРЕВО РЕШЕНИЙ.
Рисунок 2-4 представляет телефонные тарифы при помощи дерева решений. Дерево решений - простейший из всех метод "прослеживания" результата при некоторых условиях. По этой причине он может оказаться наилучшим представлением для демонстрации заказчику.
Рис.2-4. Пример дерева решений.
1-я мин. .30 + .12/100м / Прям.вызов / \ / Доп.мин. .20 + .12/100м Полный тариф / \ 1-я мин. 1.20 + .12/100м / \ / / Оператор / \ / Доп.мин. .20 + .12/100м | | 1-я мин. .22 + .10/100м | / | Прям.вызов | / \ | / Доп.мин. .15 + .10/100м ТАРИФЫ --- Средний тариф | \ 1-я мин. 1.12 + .10/100м | \ / | Оператор | \ | Доп.мин. .15 + .10/100м | \ 1-я мин. .12 + .06/100м \ / \ Прям.вызов \ / \ \ / Доп.мин. .09 + .06/100м Низкий тариф \ 1-я мин. 1.02 + .06/100м \ / Оператор \ Доп.мин. .09 + .06/100м
К сожалению, по дереву решений трудно "двигаться назад" для определения того, какие определенные условия приводят к каким результатам. Это затрудняет поиск путей для упрощения задачи. Дерево закрывает тот факт, что дополнительные минуты стоят одинаково, независимо от помощи оператора. Этот факт не виден на дереве.
ТАБЛИЦА РЕШЕНИЙ.
Таблица решений, описанная далее, дает наиболее приемлемое графическое представление сложных правил для программиста, и, возможно, также и для заказчика. Рисунок 2-5 показывает нашу структуру платежей в форме таблицы решений.
На рис.2-5 присутствуют три измерения: тарифная скидка, присутствие оператора и начальная/дополнительная минута.
Рис.2-5. Таблица решений.
+-----------------|-----------------|-----------------+ | ПОЛНЫЙ ТАРИФ | СРЕДНИЙ ТАРИФ | НИЗКИЙ ТАРИФ | |-----------------|-----------------|-----------------| | 1-я м. | доп.м. | 1-я м. | доп.м. | 1-я м. | доп.м. | ---------|--------|--------|--------|--------|--------- Прямой | .30 + | .20 + | .22 + | .15 + | .12 + | .09 + | вызов |.12/100м|.12/100м|.10/100м|.10/100м|.06/100м|.06/100м| ---------|--------|--------|--------|--------|--------- Опера- | 1.20 + | .20 + | 1.12 + | .15 + | 1.02 + | .09 + | тор |.12/100м|.12/100м|.10/100м|.10/100м|.06/100м|.06/100м| |--------+--------|--------+--------|--------+--------|
Описание задачи более чем в двух измерениях может быть немного замысловатым. Как можно видеть, эти дополнительные измерения могут быть нарисованы на бумаге как подизмерения внутри наружных. Все условия этих подизмерений появляются внутри каждого из условий наружных измерений. Как мы увидим, в программе может быть легко реализовано любое количество измерений.
Все описанные нами приемы заставляют Вас анализировать, какие условия соответствуют каким измерениям. Во время такого разбиения применяются два правила:
Первое, все элементы каждого измерения должны быть взаимно исключающими. Вы не должны ставить "1-ю минуту" в то же измерение, что и "прямой вызов", поскольку они не взаимоисключают друг друга.
Второе, в каждом измерении должны быть перечислены все возможности. Если бы существовал иной тариф для звонков с 2 до 2.05 ночи, то таблицу пришлось бы увеличить.
Но наши таблицы решений имеют свои индивидуальные преимущества. Таблица решений не только хорошо читается клиентом, но также помогает разработчику несколькими способами:
`Преобразуемость в реальный код`. Это особенно верно для Форта, в котором таблицы решений легко реализуются в форме, очень близкой к рисунку.
`Способность к отслеживанию логики в обратную сторону`. Найдите условие и смотрите, какие факторы его создали.
`Более ясное графическое представление`. Таблицы решений служат лучшим инструментом для понимания как для аналитика, так и для разработчика.
В отличие от деревьев решений, таблицы решений группируют вместе `результаты` с помощью осмысленной графики. Визуализация идей помогает в понимании проблем, особенно тех, которые слишком сложны для выражения линейным образом.
К примеру, на рисунке 2-5 ясно видно, что плата за дополнительные минуты не зависит от вмешательства оператора. С осознанием этого мы можем нарисовать упрощенную таблицу, как показано на рис. 2-6.
Рис.2-6. Упрощенная таблица решений.
+-----------------|-----------------|-----------------+ | ПОЛНЫЙ ТАРИФ | СРЕДНИЙ ТАРИФ | НИЗКИЙ ТАРИФ | |-----------------|-----------------|-----------------| | 1-я м. | доп.м. | 1-я м. | доп.м. | 1-я м. | доп.м. | ---------|--------|--------|--------|--------|--------- Прямой | .30 + | | .22 + | | .12 + | | вызов |.12/100м| .20 + |.10/100м| .22 + |.06/100м| .09 + | ---------| |--------| |--------| | Опера- | 1.20 + |.12/100м| 1.12 + |.10/100м| 1.02 + |.06/100м| тор |.12/100м| |.10/100м| |.06/100м| | |--------+--------|--------+--------|--------+--------|
Легко так увлечься каким- нибудь аналитическим инструментом, что позабыть о самой задаче. Аналитик должен не только извлечь все возможности проблемы до N-го измерения, как рекомендуют некоторые из виденных мною авторов по структурному анализу. Такой подход только увеличивает количество участвующих деталей. Решающий задачу должен также пытаться упростить ее.
------------------------------------------------------------ СОВЕТ Вы не понимаете проблему до тех пор, пока не можете упростить ее. ------------------------------------------------------------
Если целью анализа является не только понимание, но и упрощение, тогда нам, быть может, следует проделать дополнительную работу.
Наша пересмотренная таблица решений (рис. 2-6) показывает, что плата за милю зависит только от того, является ли тариф полным, средним или низким. Другими словами, в таблице играет роль только одно из трех измерений. Что случится, если мы разобьем таблицу на две, как на рис.2-7?
Рис.2-7. Разбиение таблицы решений.
+-----------------|-----------------|-----------------+ | ПОЛНЫЙ ТАРИФ | СРЕДНИЙ ТАРИФ | НИЗКИЙ ТАРИФ | |-----------------|-----------------|-----------------|
ПЛАТА ЗА СОЕДИНЕНИЕ
| 1-я м. | доп.м. | 1-я м. | доп.м. | 1-я м. | доп.м. | ---------|--------|--------|--------|--------|--------- Прямой | .30 | | .22 | | .12 | | вызов | | | | | | | ---------| .20 |--------| .15 |--------| .09 | Опера- | 1.20 | | 1.12 | | 1.02 | | тор | | | | | | |
ПЛЮС ПЛАТА ЗА РАССТОЯНИЕ
|-----------------|-----------------|-----------------| | .12/100миль | .10/100миль | .06/100миль | +-----------------+-----------------+-----------------+
Теперь мы получаем ответ из комбинации просмотра таблицы с вычислениями. Формула для по-минутной платы может быть выражена как определение на псевдоФорте:
: ПО-МИНУТНЫЙ-ТАРИФ ( -- плата-за-минуту) ПЛАТА-ЗА-СОЕДИНЕНИЕ ПЛАТА-ЗА-РАССТОЯНИЕ + ;
Знак "+" теперь появляется один раз в определении, а не девять раз в таблице.
Приняв принцип вычисления как следующий шаг, отметим (или вспомним из первоначального описания проблемы), что оператор просто добавляет одноразовую плату в размере .90 к суммарной.
В этом случае плата за оператора не является функцией ни одного из трех измерений. Это наиболее точно может быть выражено в виде "логического расчета"; т.е. функции, комбинирующей логику с арифметикой:
: ?ПОМОЩЬ ( плата-за-прямой-вызов -- суммарная-плата) ОПЕРАТОР? IF .90 + THEN ;
(Но помните, такая плата относится только к первой минуте.)
Это дает нам упрощенную таблицу, показанную на рисунке 2-8, и большую степень использования арифметических выражений.
Рис.2-8. Таблица решений без изображения вмешательства оператора.
+-----------------|-----------------|-----------------+ | ПОЛНЫЙ ТАРИФ | СРЕДНИЙ ТАРИФ | НИЗКИЙ ТАРИФ | |-----------------|-----------------|-----------------|
ПЛАТА ЗА СОЕДИНЕНИЕ
| | | | 1-я | .30 | .22 | .12 | минута | | | | |-----------------|-----------------|-----------------| Дополн. | | | | минута | .20 | .15 | .09 | | | | |
ПЛЮС ПЛАТА ЗА РАССТОЯНИЕ
| | | | | .12/100миль | .10/100миль | .06/100миль | +-----------------+-----------------+-----------------+
Давайте вернемся к нашему определению "ПО-МИНУТНЫЙ-ТАРИФ":
: ПО-МИНУТНЫЙ-ТАРИФ ( -- плата-за-минуту) ПЛАТА-ЗА-СОЕДИНЕНИЕ ПЛАТА-ЗА-РАССТОЯНИЕ + ;
Углубимся в правила вычисления платы за соединение и платы за расстояние.
Плата за соединение зависит от того, первая идет минута или последующая. Поскольку имеется два вида по-минутной оплаты, быть может, было бы проще переписать ПО-МИНУТНЫЙ-ТАРИФ как два разных слова.
Давайте положим, что мы построим компонент, который получает соответствующие величины из таблицы. Слово 1МИНУТА будет давать плату за первую минуту; +МИНУТЫ - за каждую дополнительную. Работа обоих слов будет зависеть от времени дня для получения полного, среднего или низкого тарифа.
Теперь мы можем определить пару слов для замены одного слова ПО-МИНУТНЫЙ-ТАРИФ:
: ПЕРВАЯ ( -- плата) 1МИНУТА ?ПОМОЩЬ ПЛАТА-ЗА-РАССТОЯНИЕ + ; : ЗА-ДОПОЛНИТЕЛЬНУЮ ( -- плата) +МИНУТЫ ПЛАТА-ЗА-РАССТОЯНИЕ + ;
Каково правило для платы за расстояние? Оно очень простое.
Это плата за сотню миль, помноженная на расстояние (в сотнях миль). Договоримся, что мы можем составить слово ПЛАТА-ЗА-100МИЛЬ, которое будет выдавать эту величину из таблицы:
: ПЛАТА-ЗА-РАССТОЯНИЕ ( -- плата) #МИЛИ @ ПЛАТА-ЗА-100МИЛЬ * ;
Наконец, если нам известно общее число минут в вызове, можно вычислить общую плату за вызов:
: СУММА ( -- суммарная-плата) ПЕРВАЯ ( плата за 1-ю минуту) ( #минут) 1- ( дополнительные минуты) ЗА-ДОПОЛНИТЕЛЬНУЮ * ( умножить на их стоимость) + ; ( сложить вместе)
Мы выразили правила для данной задачи через комбинацию простых таблиц и логических вычислений.
(Некоторые замечания в конце этого примера: мы написали нечто, очень похожее на действительную программу на Форте. Но это - всего-навсего псевдокод. Мы остерегались манипуляций со стеком, предполагая, что величины как-то попадают на стек в тех местах, в которых это показано комментариями. Мы также использовали чрезвычайно длинные имена, поскольку они должны быть удобочитаемы для заказчика. В реальной программе предпочтительны короткие имена - см. главу 5.)
Мы развернем законченную программу для этого примера в главе 8.
ОПРЕДЕЛЕНИЕ СТРУКТУР ДАННЫХ
По завершению определения интерфейсов, а иногда и правил, порой возникает необходимость также определить и некоторые структуры данных. Мы будем рассматривать не здесь вопросы реализации таких структур, но лишь описание их концептуальной модели.
Если, например, Вы автоматизируете библиотечную картотеку, то основная часть Ваших усилий будет касаться разработки логической структуры данных. Вам нужно решить, какую информацию следует хранить для каждой книги: название, имя автора, тему и т.д. Эти "атрибуты" будут составлять "сущность" (набор связанных записей) под названием КНИГИ. Далее Вам надо уточнить, какие другие структуры данных потребуются пользователям для эффективного поиска в списке КНИГИ. Вам может понадобиться другая сущность, состоящая из имен авторов в алфавитном порядке вместе с "указателями атрибутов" тех книг, которые написал каждый автор.
Повлияют на концептуальную модель структуры данных также некоторые ограничения. В примере с библиотечным каталогом Вам нужно знать не только `какая` информация интересует пользователей, но и как долго они расчитывают `ожидать` ее.
Например, пользователи могут запросить список тем по годам их публикаций - скажем, все по дамской галантерее между 1900 и 1910 годом. Если они хотят получать информацию молниеносно, то Вам следует проводить список по годам и темам. Если они могут ждать сутки, Вы можете позволить компьютеру перерыть все подряд книги в библиотеке.
ДОСТИЖЕНИЕ ПРОСТОТЫ
------------------------------------------------------------ СОВЕТ Пусть будет просто. ------------------------------------------------------------
Во время первых решающих шагов к пониманию проблемы не забывайте про старое высказывание:
Если дано два решения проблемы, то правильное - более простое.
Это особенно верно для разработки программ. Более простое решение часто труднее найти, но если это случилось, то оно:
* легче для понимания * легче для реализации * легче для изменения и отладки * легче для поддержки * более компактно * более эффективно * доставляет больше удовольствия
----------------------------------------------------------------
Одним из наиболее рьяных защитников простоты является Мур:
Надо иметь чувство размера задачи. Как много потребуется кода для ее реализации? Один блок? Три? Я думаю, что это очень полезный инструмент для разработки. У Вас должно быть внутреннее чувство того, тривиальная или важная это задача, сколько времени и сил Вы можете на нее потратить.
Когда Вы закончили, оглянитесь и скажите: "Обоснованно ли я подошел к решению этой проблемы?" Если Ваше решение занимает шесть экранов, может оказаться, что Вы использовали кувалду для того, чтобы убить комара. Ваш мысленный образ непропорционален важности задачи.
Я видел программы физиков-ядерщиков с сотнями тысяч строк на Фортране. Что бы ни делал этот код, это не оправдывает сотни тысяч строк кода.
Видимо, их авторы чрезмерно обобщили задачу. Они решили большую задачу, подмножество в которой составляют их собственные нужды. Они пренебрегли принципом того, что решение должно отвечать задаче.
----------------------------------------------------------------
------------------------------------------------------------ СОВЕТ Обобщенность обычно приводит к сложности. Не обобщайте свое решение более, чем это необходимо; вместо этого предусматривайте возможность изменения. ------------------------------------------------------------
----------------------------------------------------------------
Мур продолжает:
Если перед Вами поставлена задача, Вы можете написать ее решение. После того, как Вы это сделали и обнаружили в нем некоторые неприятности, можете вернуться назад и изменить задачу, получив в конце концов более простое решение.
Существует такой вид оптимизации устройств - минимизация числа вентилей в схеме, где можно использовать преимущества "безразличных" ситуаций. Они возникают или вследствие того, что данный случай на практике не встретится, или потому что действительно все равно. Однако исходные данные зачастую пишутся людьми, ничего не понимающими в программировании. Разработчик может аккуратно описать все случаи, но при этом не сказать Вам, программисту, какие из них действительно важны.
Если Вы вольны вернуться назад и поспорить с ним и использовать преимущества "безразличных" случаев, то можете получить более простое решение.
Возьмем инженерное приложение, такое, как 75-тонный пресс, работающий с металлическим порошком и выдавливающий разные штуки. Некто желает установить компьютер для управления клапанами в тех местах, где сейчас применяется гидравлическое управление. Какого типа спецификацию получите Вы от инженера? Наиболее вероятно, что датчики были расположены с точки зрения электомеханических удобств. Теперь же их могли расположить где-нибудь еще, но где - инженер позабыл. Если Вам хочется получить объяснения, то Вы должны приблизиться к реальному миру и удалиться от их модели этого мира.
Другим примером может послужить алгоритм ПИД (пропорционального интегрирования и дифференцирования) для сервоприводов. У Вас есть один термин для интегрирования, один - для дифференцирования и один - для сглаживания. Вы комбинируете 30% интегрирования с 10%-ми дифференцирования или что-то вроде этого. Но ведь это - всего-навсего цифровой фильтр. Он был удобен во времена аналоговой техники для того, чтобы можно было отбросить некоторые выражения из области цифровой фильтрации и заявить: "Это - интегратор, а то - дифференциатор. Я сделаю это с помощью конденсатора, а то - с помощью катушки индуктивности". И вновь авторы спецификаций будут моделировать аналоговое решение проблемы, которое моделировало электромеханическое решение и будут находиться за несколько моделей от реальности. На самом деле Вы можете заменить все это с помощью двух или трех коэффициентов в цифровом фильтре для получения куда более чистого, простого и эффективного решения.
----------------------------------------------------------------
------------------------------------------------------------ СОВЕТ Вернитесь к тому, что представляла собой задача до того, как заказчик пытался ее решить. Используйте "безразличные" ситуации. ------------------------------------------------------------
----------------------------------------------------------------
Мур продолжает:
Иногда возможности для упрощения сразу не видны. Вот, кстати, проблема увеличения изображения на цифровом графическом дисплее, к примеру, для САПР. На экране имеется картинка, и Вы хотите увеличить ее кусок для рассматривания деталей. Я реализовывал это так: Вы подводили курсор к интересующей позиции, затем нажимали кнопку, и изображение увеличивалось до тех пор, пока у Вас не образовывалось окно желаемого размера. Так я делал всегда. До тех пор, пока не осознал, что это глупо. Мне никогда не нужно было менять увеличение с таким мелким шагом.
Поэтому вместо предвижения курсора на один пиксел за раз я сделал перепрыгивание сразу, скажем, на десять позиций.
И вместо плавного увеличения размера окна я сделал скачкообразное увеличение. У Вас нет больше выбора масштаба. Увеличение производится в четыре раза. Промежуточные размеры не представляют интереса, прыжки же можно делать сколько угодно раз.
Безжалостно квантуя разные вещи, Вы можете облегчить работу с ними, сделать их более надежными и простыми.
----------------------------------------------------------------
------------------------------------------------------------ СОВЕТ Для упрощения - квантуйте. ------------------------------------------------------------
----------------------------------------------------------------
Мур подводит итоги:
Это большая смелость - взять и сказать: "Вы на самом деле не то имели в виду" или "Вы не будете возражать, если я изыму эту страницу и заменю ее таким вот выражением?" Это вызывает раздражение. От Вас хотят, чтобы Вы делали то, что Вам сказано.
ЛяФарр Стюарт пошел этим путем, когда перепроектировал Форт [3]. Ему не нравился буфер входного потока, поэтому он сделал Форт без него и понял, что буфер ему не нужен. Если Вы можете улучшить постановку задачи, следует не упускать такую возможность. Гораздо приятнее перепроектировать мир, нежели реализовывать его "в лоб".
----------------------------------------------------------------
Опытные программисты учат быть тактичными и проводить свой подход так, чтобы не чувствовалось угрозы: "Какими могли бы быть последствия от замены этого на то?" и т.д.
Еще один путь к упрощению проблемы:
------------------------------------------------------------ СОВЕТ Упрощая, не доставляйте беспокойства пользователям. ------------------------------------------------------------
Предположим, Вы проектируете часть текстового процессора, которая показывает каталог хранимых документов на экране, один документ в строке. Задумано, что пользователь будет подводить курсор к имени документа, а затем вводить однобуквенную команду для выбранного действия - "П" для печати, "Р" для редактирования и т.д.
Вначале кажется приемлемым позволить пользователю перемещать курсор по всему экрану. Это означает, что те места, где уже имеется текст, должны быть защищены от порчи. Это приводит к концепции "защищенных полей" и специальной обработки. Более простой подход привязывает курсор к определенным полям, возможно с использованием инверсии цвета для того, чтобы выделить для пользователя величину доступного поля.
Другой случай возникает, когда задача предлагает пользователю ввести числовое значение. Зачастую такие задачи не делают проверку ввода до тех пор, пока Вы не нажмете , после чего система ответит сообщением об ошибке типа "неверное число". Столь же просто - скорее всего, проще - проверять каждую нажатую клавишу и просто не позволять появляться нецифровым символам.
------------------------------------------------------------ СОВЕТ Для упрощения извлекайте выгоду из знания того, что возможно. ------------------------------------------------------------
----------------------------------------------------------------
Майкл ЛаМанна, Форт-программист из Лонг-Айленда, штат Нью-Йорк, комментирует:
Я всегда пытаюсь проектировать задачу на самом мощном процессоре, на который могу наложить руку. Если у меня есть выбор между разработкой на системе с процессором 68000 либо с процессором 6809, то я буду делать ее на первой системе. Процессор сам по себе настолько мощен, что берет на себя заботу о множестве деталей, которые иначе мне пришлось бы решать самому.
Если мне затем придется вернуться и переписать задачу для более простого процессора, то это нормально. По крайней мере, я не терял своего времени напрасно.
----------------------------------------------------------------
Небольшое предупреждение: Если Вы используете существующий компонент для упрощения своего прототипа, не позволяйте этому компоненту влиять на Ваш проект. Нежелательно, чтобы проект зависел от внутреннего устройства компонента.
СОБЛЮДЕНИЕ БЮДЖЕТА И ГРАФИКА
Другим важным аспектом фазы анализа является распределение стоимости.
Опять же, этот процесс сложнее, чем может показаться. Если Вы не знаете проблему до тех пор, пока ее не решите, то как, спрашивается, вы можете узнать, сколько времени она отнимет?
Тщательное планирование обязательно, поскольку все всегда длится дольше, чем Вы расчитываете. У меня по этому поводу есть теория, основанная на законах вероятности:
------------------------------------------------------------ СОВЕТ Среднее время доработки задачи "за пару часов" составляет около 12-ти часов. ------------------------------------------------------------
Представьте себе следующий сценарий: Вы находитесь посередине разработки большой программы, когда неожиданно Вас осеняет добавить туда относительно маленькую функцию. Вы думаете, что это займет часа два, поэтому, без дальнейшего планирования, Вы ее делаете. Полагаем: два часа на время кодирования. Время проектирования Вы не считаете, поскольку постигли необходимость - и реализацию - функции в мгновение ока при работе над задачей. Поэтому Вы кладете два часа.
Но допустим следующие возможности:
1. Ваша реализация содержит ошибку. Через два часа это не работает. Поэтому Вы проводите еще два часа на переделку. (Всего 4 часа.) 2. ИЛИ еще до реализации Вы поняли, что Ваш начальный проект не работал бы. Вы тратите два часа на перепроектирование. `Эти` два часа считаются. Плюс два часа на кодирование. (Всего 4 часа.) 3. ИЛИ Вы реализуете первый проект до того, как понимаете, что он неработоспособен. Поэтому Вы перепроектируете (еще два часа) и перекодируете (еще два). (Всего 6 часов.) 4. ИЛИ Вы кодируете первый вариант, находите ошибку, переписываете код, находите слабость в проекте, перепроектируете, перекодируете, находите ошибку в новом коде, перекодируете опять. (Всего 10 часов.)
Вы видите, как растет снежный ком?
5. Теперь Вам надо задокументировать новую функцию. Добавьте два часа к вышеуказанному. (Всего 12 часов.) 6. После того, как Вы потратитли что-то между 2-мя и 12-ю часами, устанавливая и отлаживая свою новую функцию, Вы неожиданно обнаруживаете, что элемент Y в Вашей задаче перестал работать.
Хуже всего то, что Вы не понимаете, почему. Вы проводите два часа за чтением дампов памяти, пытаясь выяснить причину. Когда она найдена, Вы тратите целых 12 дополнительных часов, переделывая элемент Y. (Всего 26 часов.) Затем Вам надо задокументировать синтаксические изменения по элементу Y. (Всего 27 часов.)
Это в сумме составляет более трех человеко-дней. Конечно, редко бывает так, чтобы все эти несчастья навалились на Вас сразу, однако все решительно `против` того, чтобы быть таким легким, как кажется.
Как можно улучшить свои шансы по правильной оценке временных запросов? На эту тему написано много хороших книг, отмечу `Мифический человеко-месяц` Фредерика П. Брукса, Мл. [4]. У меня есть мало что добавить к этому кладезю знаний, за исключением некоторых личных наблюдений.
1. Не занимайтесь целым. Разбейте проблему на минимально возможные куски, затем посчитайте время на каждый кусок. Сумма частей всегда больше того, что Вы дали бы на целое. (Целое кажется меньшим, чем сумма его частей.) 2. При определении кусков отделяйте те из них, которые Вы настолько хорошо понимаете, что можете рискнуть угадать их содержимое, от тех, для которых этого сделать нельзя. Для второй категории обрисуйте пределы затрат. 3. Немного психологии: всегда предлагайте Вашему клиенту разные варианты. Клиенты `любят` варианты. Если вы скажете: "Это обойдется Вам в 6000 долларов", клиент наверное ответит: "Я реально потратил бы 4000". Это ставит Вас в такое положение, при котором надо либо соглашаться, либо оставаться без работы.
Однако, если Вы скажете: "У Вас есть выбор: за 4000 долларов я заставлю это `проходить` через обруч. За 6000 я заставлю это `прыгать` через обруч. За 8000 я сделаю так, что оно будет `танцевать` через обруч с флагами, разбрасывая конфетти и распевая "Шумел камыш". Большинство заказчиков сойдутся на прыжках через обруч.
------------------------------------------------------------ СОВЕТ Все отнимает больше времени, чем Вы думаете, включая размышления. ------------------------------------------------------------
СМОТРИНЫ ДЛЯ КОНЦЕПТУАЛЬНОЙ МОДЕЛИ
Последний прямоугольник на нашей итеративной аналитической карусели обозначен как "Демонстрация модели заказчику". С помощью выделенных в этой главе инструментов эта работа должна быть легко выполнима.
При документировании требований помните, что спецификации сходны со снежным человеком. Ныне они могут быть заморожены, но будут двигаться, скользить и растаивать когда пригреет. Избрали ли Вы диаграммы потоков данных или прямой Форт-псевдокод, готовьтесь к великой оттепели, не забывая применять концепции ограниченной связности.
Покажите задокументированную модель покупателю. Когда заказчик, наконец, удовлетворен, Вы готовы к следующему большому шагу: созданию проекта!
ЛИТЕРАТУРА
1. Kim Harris, "The FORTH Philosophy," `Dr. Dobb's Journal`, Vol. 6, Iss. 9, No. 59 (Sept. 81), pp. 6-11. 2. Victor Weinberg, `Structured Analysis`, Englewood Cliffs, N.J.: Prentice-Hall, Inc., 1980. 3. LaFarr Stuart, "LaFORTH", 1980 FORML Proceedings, p. 78. 4. Frederick P. Brooks, Jr., `The Mythical Man-Month`, Reading, Massachusetts, Addison-Wesley, 1975.