Прототип библиотеки
Мы спроектировали наш прототип, изучив только один источник данных, тестирование провели на данных из того же источника. Стало быть, нечего удивляться тому, что первое же столкновение с данными из другого источника привело к гибельным последствиям.
Длинные строки на вводе, большое количество полей, непредусмотренные или пропущенные разделители – все это вызывает проблемы. Наш ненадежный прототип подходит только для индивидуального использования или в целях демонстрации принципиальной пригодности выбранного подхода, но не более того. Что ж, пришло время переработать проект.
При создании прототипа мы сделали ряд предположений – явных и неявных. Ниже перечислены некоторые из наших решений, зачастую не самых подходящих для универсальной библиотеки. Каждое поднимает вопрос, требующий более тщательной проработки.
- Прототип не способен обработать длинные строки или большое количество полей. Он может выдавать неправильные ответы или вообще зависать, потому что в нем нет даже проверки на переполнение, не говоря уже о возвращении каких-то разумных значений при возникновении ошибок.
- Предполагается, что ввод состоит из строк, оканчивающихся символом перевода строки.
- Поля разделены запятыми; если поле заключено в кавычки, последние удаляются. Не предусмотрен случай вложенных кавычек или запятых.
- Вводимая строка не сохраняется; в процессе генерации полей она переписывается.
- При переходе от одной строки к следующей никакие данные не сохраняются; если что-то надо запоминать, то следует создавать копию.
- Доступ к полям осуществляется через глобальную переменную – массив field, который используется совместно функцией csvgetline и функцией, которая ее вызвала; нет никакого контроля доступа к содержимому полей или указателям. Не предусмотрено никаких средств для предотвращения доступа за последнее поле.
- Использование глобальных переменных делает проект непригодным для многонитевой среды или даже для одновременного исполнения двух параллельных вызовов.
- Функция csvgetline читает только из уже открытых файлов; открытие лежит целиком на совести вызывающего кода.
- Ввод и разделение полей прочно связаны друг с другом: каждый вызов читает строку и сразу же разбивает ее на поля вне зависимости от того, есть ли в этом необходимость.
- Возвращаемое значение есть количество полей в строке; для подсчета этого значения строка должна быть разделена на поля. Не предусмотрено никакого механизма для определения ошибок конца файла.
- Нет никакой возможности изменить ни одно из перечисленных свойств, не внося изменений в код.
В этом длинном, но далеко не полном списке приведены те решения, которые мы приняли на этапе проектирования, – и каждое решение навсегда вплетено в код. Это приемлемо для временной версии, разбирающей файлы известного формата, поступающие из одного конкретного источника. Но что будет, если формат изменится, или запятая появится внутри кавычек, или сервер выдаст длинную строку или много полей?
Может показаться, что со всем этим нетрудно справиться, ведь "библиотека" мала и, в конце концов, является всего лишь прототипом. Представьте, однако, что этот код, пролежав в забвении месяцы или годы, в какой-то момент станет частью большой программы, спецификации которой будут/ меняться. Как адаптируется csvgetline? Если программу будут использовать другие люди, то скороспелые решения в ее проектировании могут вызвать проблемы, которые проявятся через долгое время. К сожалению, история многих интерфейсов подтверждает это заявление: большое количество временного, чернового кода просачивается в большие программные системы, в которых этот код так и остается "грязным" и зачастую слишком медленным.