Сложной частью разработки проекта является поддержка. Под поддержкой в данном случае, подразумевается координация работы группы программистов, с целью повышения скорости разработки и качества продукта. Также в этот пункт входит организация правильного тестирования и отладки.
Первое правило. Любые модификации исходных текстов не должны
удаляться определенный период времени (достаточно длинный период).
Вместо
удаления, они должны сохранятся в специальном хранилище разных версий
программы, такой подход позволяет легко в некоторых случаях произвести "откат"
к старым версиям. Из свободного программного обеспечения могу порекомендовать
пакет PRCS. Этот
пакет удовлетворяет всем основным требованиям пакета контроля версий. Также
одним из средств такого пакета является просмотр различий между разными
версиями файлов, т.е. можно определить места, где были внесены новые ошибки.
Второе правило. Программу необходимо документировать.
Если этого
не делать, возникнут ошибки-недоразумения, которые потом будет
достаточно сложно выявить. Из свободного программного обеспечения рекомендую
пакет doc++,
вот пример его работы: point.h и
документация к point.h. doc++ это работоспособное
подмножество IBM OpenDoc. Разбираться легче с
гипертекстовой документацией, чем с множеством файлов-заголовков. Наличие
хорошего описания того ЧТО делает компонента и наличие гиперссылок
делают документацию намного более удобной для изучения, чем исходные тексты
программы.
Третье правило. Программистам необходимо общаться между собой.
Идеальная ситуация - когда все программисты физически находятся в одном
помещении, однако это не всегда возможно. Если это невозможно, надо
организовать как можно более быстрый способ общения. Пример:
mailing-list, irc, ... Иначе будет невозможно добиться разработки эффективных
интерфейсов компонент.
Качество определяется функциональностью, надежностью, производительностью и пользовательской поддержкой программы, для большинства программ приоритеты расставлены в указанном порядке. Однако для некоторых на первом месте может быть надежность (например различные серверные решения), или пользовательская поддержка (приложения).
Функциональность. Программа должна полностью решать поставленные перед ней задачи, иначе возникнут трудности в ее использовании. Желательно обеспечить хорошую интеграцию с окружением, в котором программа будет использована, что позволит использование этой программы при построении более сложных систем. Хорошо разработанные компоненты могут облегчить процесс дополнения функциональных возможностей. Таким образом программу можно защитить от устаревания. Усовершенствовать программу, написанную без использования компонентного строения значительно труднее.
Надежность. Ненадежность сильно затрудняет использование программы, поскольку пользователь не может быть уверен в корректности полученных данных. Хотя иногда это может быть и приемлемо, но пользователь в любом случае останется недоволен если программа ненадежна. Единственный способ добиться надежности - хорошее тестирование. Свободное программное обеспечение привлекает значительно больше бета-тестеров, чем закрытые проекты. Количество бета-тестеров определяет скорость выявления ошибок. Как результат - много свободного ПО работает надежней коммерческого. Также надежность можно повысить путем документирования программы, чтобы ошибки-недоразумения возникали как можно реже. Компонентное строение позволяет построить эффективную систему тестов, которая в свою очередь позволит значительно повысить надежность.
Производительность. Программа должна требовать разумные ресурсы. Текстовый редактор не будут использовать, если символ появляется на экране на секунду позже, чем была нажата клавиша, или если для запуска необходимо xxxMB памяти, не имеющихся в данный момент у пользователя. Думаю, что это хорошее пояснение того, что значит фраза "разумные ресурсы". Производительность программы определяет класс машин, на котором она может быть использована. Компонентное строение позволяет легко повышать эффективность программы в двух аспектах:
Поддержка пользователей. Необходимо создать понятное описание продукта для пользователей. Уровень знаний пользователя подразумевается достаточный для начала использования продукта. Создание поддержки в виде консультаций, багфиксов и т.д. тоже является несомненным плюсом.
Компонентное строение программы позволяет повысить скорость написания/отладки программы, т.к. ведет себя намного более предсказуемо, чем написание программы не разделенной на компоненты. В pure C размер исходных текстов растет линейно, когда в C++ приращение размера исходных текстов уменьшается благодаря легкому повторному использованию уже ранее написанных компонент. Таким образом на C++ можно за более короткий промежуток времени написать более качественную программу.
Компонентное строение программы разрешает проблему с повторным использованием. Единственная проблема, возникающая при использовании компоненты - это качество объектных интерфейсов. Объектные интерфейсы должны быть эффективными и хорошо документированы.
Хорошо перед разработкой проекта оценить потенциальный круг платформ, на которых он будет работать. Часто эта оценка делается слишком узкая. Переносимость в любом случае качество очень полезное.
Типы. Объявите свои типы которые будут отображать 8,16,32,64 битные значения. Например:
typedef char int8; // bit8 typedef short int16; // bit16 typedef long int32; // bit32 typedef unsigned char uint8; // bit8u, uchar typedef unsigned short uint16; // bit16u, ushort typedef unsigned long uint32; // bit32u, ulong
Endian-ness (порядок байт). Существует два стандартных порядка байт при хранении значений размерами больше одного байта - little-endian и big-endian. Little-endian - старший байт идет последним. Big-endian - старший байт идет первым. Intel-платформа использует little-endian порядок. Стандартные сетевые протоколы используют big-endian порядок байт. Сдвиги сохраняют свое значение, т.е. сдвиг влево на big-endian машине не равносильно умножить на 2. Вывод прост: не стоит пользоваться сдвигами для деления или умножения значений размером больше одного байта. Современные компиляторы автоматически преобразуют, где это возможно, деления или умножение на константу в более быстрый аналог со сдвигами. Для флагов сдвиги можно использовать, но физически результат на little-endian и big-endian машинах будет разный. Лучше сдвиги использовать по-минимуму.
Использование внешних библиотек. Круг платформ, на котором будет работать программа не больше круга платформ, на котором работают все используемые внешние библиотеки одновременно. Использование внешних библиотек может привести к потребности переноса и их тоже. Не используйте плохо поддерживаемые библиотеки, которые будет сложно собственноручно перенести на другую платформу.
Ассемблер. В некоторых случаях производительность можно сильно повысить, используя ассемблер. Переносимое использование ассемблера выполняется в три шага:
Кодирование имен функций (name-mangling). Полиморфизм. Как результат получаем кодирование имени функции в зависимости от аргументов. Name-mangling не стандартизирован. Это представляет проблему для коммерческих библиотек без исходных текстов - либо компилировать под каждую платформу всеми возможными компиляторами, либо писать процедурный wrapper для ANSI-C.
Имена файлов. Необходимо решить, под какими платформами будут компилироваться исходные тексты. Minix не поддерживает имена файлов больше 30 букв. DOS поддерживает только формат имени файлов 8+3. MS WinXX не различают больших/маленьких букв в имени файлов. Можно создать таблицы переименования и использовать на полноценных UNIX-like платформах понятные длинные имена файлов, а при переносе на другие системы с не столь совершенными файловыми системами изменять имена файлов. Удобно использовать только маленький регистр при названии файлов, тогда сортировка по имени будет корректнее показывать список файлов программисту. Почти все компиляторы распознают расширение .cpp, и
0,NULL и void*. В C++ правильнее использовать 0, а не NULL, как в C. Дело в том, что конверсия 0->(T*) требует один шаг, а конверсия NULL->(T*) требует два шага 0->(void*)->(T*), при использовании автоматических конверсий может привести к неудобствам. Также большинство компиляторов не будет автоматически преобразовывать (void*)->(T*) и надо будет писать "if (xxx != (char*)NULL)", вместо изящного "if (xxx != 0)".
Расширения компилятора. Многие компиляторы имеют расширенный синтаксис. Например под 16-битными платформами существовали типы указателей far и near, GNU C/C++ позволяет делать макросы с переменным количеством аргументов, и так далее. Безусловно использование таких расширений сразу убивает всю переносимость компоненты. Стандартного синтаксиса C++ достаточно для создания эффективной программы. Не стоит пользоваться нестандартными средствами, если они не позволяют кардинально улучшить работу компоненты/программы.
Валерий Щедрин
<valery@forthpick.kiev.ua>