Идиомы и стили С++ - страница 21

Шрифт
Интервал

стр.

больно уж тривиален, поэтому сойдет и так. Класс CSimpleTr имеет смысл переписать в виде шаблона, если не хотите его заново переписывать для каждого нового клиента, да еще добавить функцию isStarted(). Функциональность на Ваш вкус и фантазию. MTS, например, восстановит отмененную транзакцию, если Вы после отмены сделаете закрепление: SetAbort(); SetComplete(); сработает так, как будто SetAbort(); не было вовсе.

Шаг 23 - Классы объектов, поддерживающие транзакции. Продолжение.

Раз уж взялись, что мешает вставить в наш умный указатель с поддержкой отмены не один, а несколько предыдущих состояний объекта? Ничего. Только чтобы потом несколько раз не переделывать, решим несколько чисто технических моментов:

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

2. Определимся с семантикой. Смешивать значения и указатели не стоит, верный путь заработать себе геморрой. У меня не оказалось под рукой подходящего стека, и я написал для этого Шага два варианта - один хранит значения, другой - указатели; первый стек сначала казался проще, но использующий его класс указателя оказался ощутимо сложнее по простой причине - функции стека с указателями могут возвращать NULL, а это совсем немало.

3. Оформим все в виде шаблонов; вообще контейнеры просто просятся быть шаблонами, а smart-указатели несомненно являются контейнерами.

Код ниже, а сейчас пояснения:

Класс CType просто проверочный, чтобы вкладывать в шаблоны; так проще отлаживать шаблон: сначала сделать контейнер-не-шаблон для класса Type, а потом просто приписать сверху объявления строку template‹Type›. Шаблон класса ampstack‹Type› - шаблон стека указателей; push сохраняет указатель, pop достает верхний указатель, isEmpty проверяет на пустоту, emptyAll очищает.

Шаблон класса MLTrans - наконец тот, который нам нужен. Указатель that хранит текущее значение, Push сохраняет текущее значение, PopOne делает однократную отмену, Rollback отменяет все изменения, до первоначального, Commit удаляет историю.

>// Это маленький класс для проверки

>class CType {

> int a;

>public:

> void set (int _a) { a=_a; }

> int get (void) { return a; }

>};


>// Шаблон стека

>template ‹class Type›

>class ampstack {

>private:

> int iTop; // верх стека

> int iSize; // размер стека

> Type** array; // массив указателей

>public:

> // Конструктор-деструктор

> ampstack(int size=10) : iTop(0), iSize(size), array(new Type*[size]) {}

> ~ampstack() {

>  for (int iCounter = 0; iCounter ‹ iTop; iCounter ++)

>   if (*(array+iCounter)!= NULL) delete *(array+iCounter);

>  delete[] array;

> }

> // Управление стеком

> // Направить указатель в стек

> void push (Type* _t) { array[iTop++]=_t; }

> // Вынуть указатель из стека

> Type* pop (void) {

>  if (iTop == 0) return NULL;

>  else return array[--iTop];

> }

> // Стек пуст?

> int isEmpty (void) { return iTop==0; }

> // Очистить стек

> void emptyAll (void) {

>  for (int iCounter = 0; iCounter ‹ iTop; iCounter ++)

>   if (*(array+iCounter)!= NULL) delete *(array+iCounter);

>  iTop = 0;

> }

>};


>// Шаблон класса с многоуровневой отменой

>template ‹class Type›

>class MLTrans {

> typedef ampstack‹Type› stack;

>private:

> Type* that; // Текущее значение

> stack history; // контейнер предыдущих значений

>public:

> // конструктор-деструктор

> MLTrans(): that(new Type) {}

> ~MLTrans () { delete that; }

> // Сохранение текущего значения, aналог SAVE TRANSACTION в SQL серверах

> void Push() {

>  history.push(that);

>  that = new Type(*that);

> }

> // удаление промежуточных состояний

> void Commit () { history.emptyAll(); }

> // Откат на одну позицию; уничтожает текущее значение.

> void PopOne() {

>  if (!history.isEmpty()) {

>   delete that;

>   that = history.pop();

>  }

> }

> // Откат к началу транзакции.

> void Rollback() {

>  Type* old = history.pop();

>  Type* older = NULL;

>  if (old!= NULL) {

>   while ((older = history.pop())!= NULL) {

>    delete old;

>    old = older;

>   }

>   delete that;

>   that = old;

>  }

> }


стр.

Похожие книги