преподают громоздкую иерархию как способ удовлетворения правила представления. С этой точки зрения множество классов приравниваются к внедрению знаний в данные. Проблема данного подхода заключается в том, что слишком часто 'развитые данные' в связующих уровнях фактически не относятся к какому-либо естественному объекту в области действия программы — они предназначены только для связующего уровня. (Одним из верных признаков этого является распространение абстрактных подклассов или 'смесей'.)
Другим побочным эффектом ОО-абстракции представляется то, что постепенно исчезают возможности для оптимизации. Например,
Для Unix-программистов характерно инстинктивное осознание данных проблем. Данная тенденция представляется одной из причин, по которой ОО-языкам в Unix не удалось вытеснить не-ОО-конструкции, такие как С, Perl (который в действительности обладает ОО-средствами, но они используются не широко) и shell. В мире Unix больше открытой критики ОО-языков, чем это позволяют ортодоксы в других операционных системах. Unix-программисты знают, когда
Одной из причин того, что ОО-языки преуспели в большинстве характерных для них предметных областей (GUI-интерфейсы, моделирование, графические средства), возможно, является то, что в этих областях относительно трудно неправильно определить онтологию типов. Например, в GUI-интерфейсах и графических средствах присутствует довольно естественное соответствие между манипулируемыми визуальными объектами и классами. Если выясняется, что создается большое количество классов, которые не имеют очевидного соответствия с тем, что происходит на экране, то, соответственно, легко заметить, что связующий уровень стал слишком большим.
Одна из основных трудностей проектирования в стиле Unix состоит в комбинации достоинств отделения (упрощение и обобщение проблем из их исходного контекста) с достоинствами тонкого связующего уровня и плоских, простых и прозрачных иерархий кода и конструкции.
Некоторые из этих моментов будут повторно рассматриваться при обсуждении объектно- ориентированных языков программирования в главе 14.
4.6. Создание модульного кода
Модульность выражается в хорошем коде, но главным образом она является следствием хорошего проектирования. Ниже приведен ряд вопросов о разрабатываемом коде, ответы на которые могут помочь программисту в улучшении модульности кода.
• Сколько глобальных переменных присутствует в коде? Глобальные переменные — разрушители модульности, простой способ передачи информации из одних компонентов в другие неаккуратным и беспорядочным путем[47].
• Остаются ли размеры отдельных модулей в пределах зоны наилучшего восприятия Хаттона? Если это не так, то возможно появление долговременных проблем при сопровождении. Известны ли пределы зоны наилучшего восприятия для данного программиста? Известны ли пределы зоны для других сотрудничающих программистов? Если нет, то наилучшим решением будет придерживаться консервативной точки зрения и сохранять размеры, ближайшие к нижней границе диапазона Хаттона.
• Не слишком ли крупные отдельные функции в модулях? Это не столько вопрос количества строк кода, сколько его внутренней сложности. Если неформально в одной строке невозможно описать взаимодействие функции и вызывающей ее программы, то, вероятно, размер функции слишком велик[48].
Лично я склонен разбивать подпрограмму, когда в ней слишком много локальных переменных. Другой признак — уровни отступов (их слишком много). Я редко смотрю на длину.
• Имеет ли код внутренние API-интерфейсы — т.е. наборы вызовов функций и структур данных, которые можно описать как цельные блоки, каждый из которых изолирует некоторый уровень функций от остальной части кода? Хороший API имеет смысл и понятен без рассмотрения скрытой в нем реализации. Классическая проверка заключается в том, чтобы попытаться описать API другому программисту по телефону. Если это не удалось, то весьма вероятно, что интерфейс слишком сложен и спроектирован неудачно.
• Имеет ли любой из разрабатываемых API-интерфейсов более семи входных точек? Имеет ли какой-либо из классов более семи методов? Имеют ли структуры данных более семи членов?
• Каково распределение входных точек в каждом модуле проекта?[49] Не кажется ли распределение неравномерным? Действительно ли в некоторых модулях необходимо такое большое количество входных точек? Сложность модуля также растет, как квадрат числа входных точек — еще одна причина того, что простые API лучше, чем сложные.
Может оказаться полезным сравнение данных вопросов с перечнем вопросов о прозрачности и воспринимаемости в главе 6.
5
Текстовое представление данных: ясные протоколы лежат в основе хорошей практики
В данной главе рассматриваются традиции Unix в аспекте двух различных, но тесно связанных друг с другом видов проектирования: проектирования форматов файлов для сохранения данных приложений в постоянном хранилище памяти и проектирования протоколов прикладного уровня для передачи (возможно, через сеть) данных и команд между взаимодействующими программами.
Объединяет оба вида проектирования то, что они задействуют сериализацию структур данных. Для внутренней работы компьютерных программ наиболее удобным представлением сложной структуры данных является то, в котором все поля имеют характерный для конкретной машины формат данных (например, представление целых чисел со знаком в двоичном дополнительном коде) и все указатели являются абсолютными адресами памяти (в противоположность, например, именованным ссылкам). Однако такие формы представления не подходят для хранения и передачи. Адреса памяти в структуре данных теряют свое значение за пределами оперативной памяти, и выпуск необработанных собственных форматов данных приводит к проблемам взаимодействия при передаче данных между машинами с различными соглашениями (например, с обратным и прямым порядком следования байтов или между 32- и 64-битовой архитектурами).
Для передачи и хранения передаваемое квази-пространственное расположение структур данных,