Это понимание теоретически обосновывает набор практических приемов, которые всегда были важной частью инструментария Unix-программиста — языки очень высокого уровня, программы, управляемые данными, генераторы кода и узкоспециальные мини-языки. Объединяет их то, что все они являются способами, позволяющими поднять генерацию кода на несколько уровней выше, чтобы спецификации могли быть меньше. Ранее отмечалось, что плотность дефектов стремится к почти постоянному значению в различных языках программирования. Все указанные практические приемы означают сокращение количества строк и, соответственно, уменьшение вероятности появления ошибок.
В главе 8 описывалось использование специализированных мини-языков. В главе 14 представлены аргументы в пользу языков очень высокого уровня. В данной главе рассматривается несколько примеров конструкции программ, управляемых данными, а также ряд примеров генерации особого кода. Некоторые средства генерации кода описаны в главе 15. Как и мини-языки, данные методы позволяют радикально сократить количество строк кода в программах и соответственно уменьшить время отладки и затраты на сопровождение.
9.1. Создание программ, управляемых данными
При создании программ,
Создание программ, управляемых данными, иногда путают с объектно-ориентированным программированием — еще одним стилем программирования, в котором самое важное значение имеет организация данных. Между данными стилями есть как минимум два отличия. Одно из них заключается в том, что в первом случае данные представляют собой не просто состояние некоторого объекта, а фактически определяют управляющую логику программы. Тогда как основной проблемой в ОО- программировании является инкапсуляция, основная проблема при создании программ, управляемых данными, заключается в написании как можно меньшего количества фиксированного кода. Операционная система Unix имеет более устойчивые традиции в создании программ, управляемых данными, чем в ОО- программировании.
Разработку управляемых данными программ также часто путают с написанием конечных автоматов. Выразить логику конечного автомата в виде таблицы или структуры данных действительно возможно, однако запрограммированные вручную конечные автоматы обычно представляют собой жесткие блоки кода, которые гораздо труднее модифицировать, чем таблицу.
При выполнении любого вида генерации кода или создании программ, управляемых данными, необходимо помнить важное правило: решение проблемы всегда следует переносить на более высокие уровни. Не следует вручную улучшать сгенерированный код или любые промежуточные формы представления. Вместо этого следует задуматься о способе усовершенствования или замены средства трансляции. Иначе, вероятно, выяснится, что улучшение вручную кода, который должен был быть корректно сгенерированным машиной, превратится в бесконечную трату времени.
На верхней границе шкалы сложности создание управляемых данными программ сливается с написанием интерпретаторов для p-кода или простых мини-языков, которые рассматривались в главе 8. На других границах оно сливается с генерацией кода и программированием конечных автоматов. Различия фактически не так важны. Важной частью является перемещение логики программы из жестко закодированных управляющих структур в данные.
9.1.1. Учебный пример:
Автор этой книги поддерживает программу, которая называется
esr@snark:~/WWW/writings/taoup$ ascii 10
ASCII 1/0 is decimal 016, hex 10, octal 020, bits 00010000: called ^P, DLE
Official name: Data Link Escape
ASCII 0/10 is decimal 010, hex 0a, octal 012, bits 00001010: called LF, NL
Official name: Line Feed
C escape: '
'
Other names: Newline
ASCII 0/8 is decimal 008, hex 08, octal 010, bits 00001000: called ^H, BS
Official name: Backspace
C escape: ''
Other names:
ASCII 0/2 is decimal 002, hex 02, octal 002, bits 00000010: called ^B, STX
Official name: Start of Text
О том, что в основу данной программы положена хорошая идея, свидетельствует тот факт, что программу можно использовать неожиданным способом — как быстрое вспомогательное CLI-средство для преобразования десятичных, шестнадцатеричных, восьмеричных и двоичных форм представления байтов.
Основная логика данной программы могла бы быть реализована в виде конструкции выбора с 128 условными переходами. Однако в таком случае код был бы громоздким и сложным в сопровождении. Кроме того, в нем смешивались бы части, которые изменяются сравнительно часто (такие как список сленговых названий символов), а также части, изменяемые редко или вообще немодифицируемые (такие как официальные названия). Помещение таких частей в один ряд условных обозначений и ошибки в ходе редактирования, вероятнее всего, затрагивали бы данные, которые должны оставаться неизменными.
Вместо этого утилита была создана как управляемая данными программа. Все строки с названиями символов находятся в табличной структуре, которая значительно крупнее любой из функций в коде (в действительности, если учитывать количество строк, она больше чем любыеnametable.h
, который генерируется способом, рассмотренным далее в данной главе.
Подобная организация упрощает добавление новых названий символов, изменение существующих или удаление старых названий просто путем редактирования таблицы, не затрагивая кода.
Способ организации программы является хорошим Unix-стилем, но формат ее вывода сомнительный. Трудно понять, как практически можно применить вывод в качестве ввода другой программы, поэтому утилита слабо приспособлена к взаимодействию с другими программами.