Вспомним для начала, что делает транзакция:
Если транзакция началась, то все изменения в ней либо вносятся вместе, либо не вносятся вообще (это вообще определение транзакции).
Если клиент начал и не завершил транзакцию, то другие клиенты не видят его изменений.
Две транзакции не могут одновременно изменять одни и те же данные.
Начнем с первого пункта. Если мы хотим закрепить или отменить проведенные изменения, нужно хранить состояние объекта на заданный момент - начало транзакции, и в момент принятия решения или уничтожать предыдущее состояние (закрепление) или возвращаться к нему (отмена). Пусть обслуживанием занимается smart-указатель. Кладем в него два указателя - один на текущий объект, а второй - на объект, представляющий его предыдущее состояние, и три функции - старт, закрепление, отмена.
Код:
#include <iostream.h>
// Некий скромный класс.
class CSomeClass{
public:
int x;
int y;
};
// Его оплетка: smart-pointer с поддержкой отмены.
class CSimpleTr{
public:
CSomeClass* that; // текущее значение
CSomeClass* previos; // предыдущее значение
public:
// конструктор-деструктор-присваивание-копирование
CSimpleTr():previos(NULL), that(new CSomeClass){};
CSimpleTr(const CSimpleTr& _st):
that(new CSomeClass(*(_st.that))), previos(NULL) {};
~CSimpleTr(){delete that; delete previos;};
CSimpleTr& operator=(const CSimpleTr& _st)
{
if (this != &_st)
{
delete that;
that = new CSomeClass(*(_st.that));
}
return *this;
};
// начало транзакции
void BeginTrans()
{
delete previos;
previos = that;
that = new CSomeClass(*previos);
};
// закрепление
void Commit ()
{
delete previos;
previos = NULL;
};
// отмена транзакции
void Rollback()
{
if (previos != NULL)
{
delete that;
that = previos;
previos = NULL;
}
};
// реализация указателя
CSomeClass* operator->() {return that;};
};
int main (void)
{
// проверим быстренько
CSimpleTr lvPair;
lvPair->x = 5;
lvPair->y = 8;
lvPair.BeginTrans();
lvPair->x = 7;
lvPair->y = 11;
lvPair.Rollback();
lvPair.BeginTrans();
lvPair->x = 7;
lvPair->y = 11;
lvPair.Commit();
return 0;
}
Что может быть проще? Семантика значений, очевидно. Классы должны иметь полный набор обязательных функций, как обычно; в нашем случае класс CSomeClass больно уж тривиален, поэтому сойдет и так. Класс CSimpleTr имеет смысл переписать в виде шаблона, если не хотите его заново переписывать для каждого нового клиента, да еще добавить функцию isStarted(). Функциональность на Ваш вкус и фантазию. MTS, например, восстановит отмененную транзакцию, если Вы после отмены сделаете закрепление: SetAbort(); SetComplete(); сработает так, как будто SetAbort(); не было вовсе.