Страница 8 из 20
Не все письма были корректными. Некоторые из них были резко отрицательными; другие выражали решительную поддержку. И эта битва продолжалась почти десять лет.
В конце концов спор утих по одной простой причине: Дейкстра победил. По мере развития языков программирования инструкция goto все больше сдавала свои позиции, пока, наконец, не исчезла. Большинство современных языков программирования не имеют инструкции goto, а некоторые, такие как LISP, никогда ее не имели.
В настоящее время все программисты используют парадигму структурного программирования, хотя и не всегда осознанно. Просто современные языки не дают возможности неограниченной прямой передачи управления.
Некоторые отмечают сходство инструкции break с меткой и исключений в Java с инструкцией goto. В действительности эти структуры не являются средствами неограниченной прямой передачи управления, имевшимися в старых языках, таких как Fortran или COBOL. Кроме того, даже языки, сохранившие ключевое слово goto, часто ограничивают возможность переходов границами текущей функции.
Функциональная декомпозиция
Структурное программирование дает возможность рекурсивного разложения модулей на доказуемые единицы, что, в свою очередь, означает возможность функциональной декомпозиции. То есть решение большой задачи можно разложить на ряд функций верхнего уровня. Каждую из этих функций в свою очередь можно разложить на ряд функций более низкого уровня, и так до бесконечности. Кроме того, каждую из таких функций можно представить с применением ограниченного набора управляющих структур, предлагаемых парадигмой структурного программирования.
Опираясь на этот фундамент, в конце 1970-х годов и на протяжении 1980-х годов приобрели популярность такие дисциплины, как структурный анализ и структурное проектирование. В тот период многие, например Эд Йордан, Ларри Константин, Том Демарко и Меилир Пейдж-Джонс, продвигали и популяризовали эти дисциплины. Следуя им, программисты могли разбивать большие системы на модули и компоненты, которые затем можно было разбить на маленькие и доказуемые функции.
Формальные доказательства отсутствуют
Но доказательства так и не появились. Евклидова иерархия теорем не была построена. И программисты не увидели преимуществ использования трудоемкого процесса формального доказательства правильности каждой, даже самой маленькой функции. В конечном итоге мечта Дейкстры рассеялась как дым. Не многие из современных программистов считают, что формальные доказательства являются подходящим способом производства высококачественного программного обеспечения.
Конечно, формальный евклидов стиль математических доказательств не единственная стратегия создания чего-то правильного. Другой, более успешной стратегией является научный метод.
Наука во спасение
Принципиальное отличие науки от математики заключается в том, что правильность научных теорий и законов нельзя доказать. Я не смогу доказать верность второго закона движения Ньютона, F = ma, или закона гравитации, F = Gm1m2/r2. Я могу продемонстрировать действие этих законов и провести измерения, подтверждающие их правильность до многих знаков после запятой, но я не смогу доказать их в математическом смысле. Я могу провести массу экспериментов и собрать массу эмпирических подтверждений, но всегда остается вероятность, что какой-то эксперимент покажет, что эти законы движения и гравитации неверны.
Такова природа научных теорий и законов: их можно сфальсифицировать, но нельзя доказать.
Тем не менее мы верим в эти законы. Каждый раз, садясь в автомобиль, мы ставим свою жизнь на то, что формула F = ma точно описывает окружающий мир. Каждый раз, делая шаг, мы ставим свои здоровье и безопасность на верность формулы F = Gm1m2/r2.
Наука не требует доказательства истинности утверждений, чаще она требует доказательства их ложности. Утверждения, доказать ложность которых не удается после многих усилий, мы считаем истинными.
Конечно, не все утверждения требуют доказательств. Например, утверждение «это – ложь» не является ни истинным, ни ложным. Это один из простейших примеров утверждений, не требующих доказательств.
Подводя итог, можно сказать, что математика – это дисциплина доказательства истинности утверждений, требующих доказательства. Наука, напротив, – дисциплина доказательства ложности утверждений, требующих доказательства.
Тестирование
Однажды Дейкстра сказал: «Тестирование показывает присутствие ошибок, а не их отсутствие». Иными словами, тестированием можно доказать неправильность программы, но нельзя доказать ее правильность. Все, что дает тестирование после приложения достаточных усилий, – это уверенность, что программа действует достаточно правильно.
Следствия из этого факта могут показаться ошеломляющими. Разработка программного обеспечения не является математической задачей, даже при том, что она связана с применением математических конструкций. Эта сфера деятельности больше похожа на науку. Мы убеждаемся в правильности, потерпев неудачу в попытке доказать неправильность.
Такие доказательства неправильности можно применить только к доказуемым программам. Недоказуемую программу – например, из-за неумеренного использования goto – нельзя считать правильной, сколько бы тестов к ней ни применялось.
Парадигма структурного программирования заставляет нас рекурсивно разбивать программы на множество мелких и доказуемых функций. В результате мы получаем возможность использовать тесты, чтобы попытаться доказать их неправильность. Если такие тесты терпят неудачу, тогда мы считаем функции достаточно правильными.
Заключение
Именно эта возможность создавать программные единицы, неправильность которых можно доказать, является главной ценностью структурного программирования. Именно поэтому современные языки обычно не поддерживают неограниченное применение инструкций goto. Кроме того, именно поэтому функциональная декомпозиция считается одним из лучших приемов на архитектурном уровне.
На всех уровнях, от маленьких функций до больших компонентов, разработка программного обеспечения напоминает науку, и поэтому в ней применяется правило опровергающих доказательств. Программные архитекторы стремятся определить модули, компоненты и службы, неправильность которых легко можно было бы доказать (протестировать). Для этого они используют ограничения, напоминающие ограничения в структурном программировании, хотя и более высокого уровня.
Именно эти ограничения мы будем подробно изучать в последующих главах.
5. Объектно-ориентированное программирование
Как мы увидим далее, для создания хорошей архитектуры необходимо понимать и уметь применять принципы объектно-ориентированного программирования (ОО). Но что такое ОО?
Один из возможных ответов на этот вопрос: «комбинация данных и функций». Однако, несмотря на частое цитирование, этот ответ нельзя признать точным, потому что он предполагает, что o.f() – это нечто отличное от f(o). Это абсурд. Программисты передавали структуры в функции задолго до 1966 года, когда Даль и Нюгор перенесли кадр стека функции в динамическую память и изобрели ОО.
Другой распространенный ответ: «способ моделирования реального мира». Это слишком уклончивый ответ. Что в действительности означает «моделирование реального мира» и почему нам может понадобиться такое моделирование? Возможно, эта фраза подразумевает, что ОО делает программное обеспечение проще для понимания, потому что оно становится ближе к реальному миру, но и такое объяснение слишком размыто и уклончиво. Оно не отвечает на вопрос, что же такое ОО.