Вариант 4. Файл/память, буферизация, тесты

Стоимость

35 баллов

Описание

Спроектировать библиотеку ввода/вывода, основанную на применении паттерна Decorator. Центральным элементом библиотеки должен быть интерфейс потока. Этот интерфейс должен реализовываться в нескольких конкретных классах, предназначенных для записи на различные устройства (в файл, в память, в строку). Должна быть возможность применения модификаторов (буферирование, кодирование, сжатие) к любому потоку, причем, следует учесть, что к потоку может применяться произвольное количество модификаторов. Предусмотреть также возможность определения пользователем библиотеки собственных модификаторов потока, и обеспечить удобное их использование, наряду с имеющимися изначально в проекте.

[Замечание]Замечание

Все перечисленные задачи решаются правильным применением паттерна Decorator. Фактически, Вам необходимо выделить нужные классы, и определить роль каждого класса в паттерне.

Необходимо реализовать только те классы, которые непосредственно отвечают за запись в файл и память, чтение из файла и памяти, а также буферизацию потока. Кроме того, нужно реализовать классы для чтения и записи в поток значений некоторых встроенных типов (целых чисел, вещественных чисел, строк - выбрать два из трех).

На самом нижнем уровне вывод осуществлять с помощью функций open(), read(),write(),close(), описанных в заголовочном файле io.h.

При возникновении ошибки (например, невозможности записи в поток) следует генерировать исключение (exception), причем, лучше всего, если класс исключения будет производным от std::exception.

Возможно, по меньшей мере, два подхода по разделению обязанностей чтения и записи между классами. Либо вся эта функциональность может реализовываться одним классом потока, либо могут быть предусмотрены разные классы для ввода и для вывода. Решение о том, какого подхода будете придерживаться Вы принимать, безусловно, Вам. Однако, во втором случае, вероятно, придется реализовывать раздельно декораторы для входных и выходных потоков, что приведет к удвоению числа классов в библиотеке.

Интерфейс(ы) потока ввода/вывода должен включать, как минимум, две операции: побайтовый ввод/вывод, и ввод/вывод последовательности байт заданного размера.

Защита

Для защиты лабораторной работы необходимо представить:

  • Работающую программу (причем, описания структур должны быть вынесены в заголовочные файлы, а их определение - в соответствующие .cpp-модули).
  • Диаграмму классов. Для каждого реализованного класса должны быть показаны все операции и атрибуты, причем для каждой операции должна быть показана сигнатура и спецификатор доступа. Для классов, входящих в проект библиотеки, но реализация которых не предусмотрена заданием, можно показать только обязанности
  • За набор юнит-тестов можно получить дополнительные 5 баллов. Юнит тесты должны быть выполнены с применением какой-либо (на ваше усмотрение) платформы юнит-тестирования для C++: Boost::Test, CppUnit, CppUnitLite (самый простой, пожалуй, вариант) и т.п.

Пример

Разработанная библиотека должна допускать, к примеру, такое использование:

/*...*/

int main() {

	char *memory_storage;
	/* ... */
	/* выделение памяти, заполнение хранилища memory_storage*/
	/* ... */        

        /* Создается поток записи в файл (FileStream) и настраивается
         * на работу с файлом "output.txt".
         * Создается экземпляр BufferedStream, добавляющий буферизацию
         * для связанного с ним потока. Непосредственно при создании
         * BufferedStream связывается с экземпляром FileStream
         * (в данном случае, указатель на созданный FileStream просто 
         * передается конструктору BufferedStream в качестве аргумента).
         */
       
        Stream *os = new BufferedStream(
                               new FileStream("output.txt", WRITE)
                     );

        /* Создается еще один поток, на этот раз для буферированного
         * чтения из некоторой области памяти (memory_storage).
         */
	Stream *is = new BufferedStream(
				new MemoryStream(memory_storage, READ)
	             );

        char buf[1024];

	/* Здесь предполагается, что операция read потока возвращает
	 * количество считанных символов. В спроектированной Вами библиотеке
	 * это может быть не так, но, в любом случае, должна быть какая-то
	 * возможность узнать это количество.
	 */
	int qty = is->read(&buf[0], 1024);

        os->write('l');            // вывод в поток одного символа
        os->write(&buf[0], qty);   // вывод в поток указанного количества 
                                   // символов

        /* Создается экземпляр класса для чтения значений встроенных
         * типов и связывается с потоком is.
         */
	DataReader *dr = new DataReader(is);
	int someInt;
	dr->read(someInt);   // читает из потока число в десятичной записи;
	                     // исходит из того, что справа и слева число
	                     // ограничивается пробелами или табуляциями.

	delete dr;
        delete os;
        delete is;

	return 0;
}
Сайт управляется системой uCoz