|
||||
|
Шаг 28 - Классы объектов, поддерживающие транзакции. Продолжение 2. Классы объектов, хранящие состояния, получились очень неплохие - при минимальных интеллектуальных затратах, хотя о транзакциях говорить рано: для транзакций они недостаточно кислотные. (ACID - Atomic, Consistent, Isolated, Durable). Не хватает вот чего: 1. Объекты, задействованные в транзакции, блокируются на запись. 2. Объекты, задействованные в транзакции, представляют другим клиентам свое состояние до транзакции. Мы уже понимаем общий принцип: если нужна дополнительная логика - вынесите ее на отдельный уровень. Что означает это в нашем случае? То, что 1: транзакция должна быть представлена отдельным уровнем - отдельным классом; 2: объекты, задействованные в транзакции, должны поддерживать специальный стандартный интерфейс, за который транзакция должна ими рулить. То есть, они либо должны быть порождены от специального абстрактного базового класса, либо они должны быть упакованы в специальный смарт-указатель - делающий то же самое. Все остальное - дело техники. Сразу поясняю код: класс CLockable (базовый) содержит указатель на транзакцию, к которой принадлежит в данный момент, а так же чистые виртуальные функции rollback и commit, назначение которых очевидно. Класс CTransaction представляет собой транзакцию, и содержит в себе список задействованных объектов - затем чтобы роллбачить или коммитить их все вместе, да чтоб на ходу можно проверить - принадлежит ли объект некоей транзакции или нет. Функция addLock() добавляет объект к транзакции, то есть распространяет свое действие на него, тем самым блокируя изменения со стороны других клиентов. После использования объекты автоматически разрегистрируются. Хочу еще раз напомнить о принципе, который в этом Шаге формулируется первый раз: если существует определенная, законченная логика взаимодействия объектов или систем - она выносится на отдельный уровень. Поддержка транзакций очевидно является законченной бизнес-логикой, и, следовательно, должна быть вынесена на специальный уровень. Пусть классы реализуют свою функциональность, не заботясь о свопе, многозадачности-поточности, транзакциях, доступе, приоритетах, авторизациях, синхронизации, сборке мусора (garbage collection) или уплотнении памяти - это не его проблемы. Это - другие уровни, которыми занимаются другие классы, или даже системы. В современном компьютерном мире этот принцип сегодня доминирует, именно на него работают новомодные COM+ с MTS, IBM-websphera, JavaBeans, и множество иных. То, что грамотная корпоративная система должна работать в минимум четырех уровнях, уже является прописной истиной: данные, бизнес-логика сервера, бизнес-логика клиента, клиент. Понимаете теперь, в чем преимущество смарт-указателей? Они позволяют с легкостью создавать новые уровни бизнес-логик без привлечения дополнительных средств и схем, а едиными только средствами языка. Попробуйте сделать что-либо подобное на языке, который не поддерживает указателей и перегрузки операторов (да шаблоны еще)! Вот код. #include "ampstack.h" // Абстрактный базовый класс class CLockable { friend class CTransaction; protected: // текущая транзакция, если есть CTransaction* m_trans; public: CLockable (): m_trans (NULL) {} // регистрируемся в какой-то транзакции int regObj (CTransaction* _pt); // и разрегистрируемся void unregObj(); virtual ~CLockable() {} virtual void rollback () =0; virtual void commit() =0; }; // Класс транзакции class CTransaction { friend class CLockable; private: // коллекция зарегистрированных объектов ampstack‹CLockable› m_locks; // добавить объект к транзакции void addLock (CLockable*); public: virtual ~CTransaction (); // закрепить или отменить все изменения во всех // зарегистрированных объектах. void commit(); void rollback(); // проверить, зарегистрирован ли объект в этой транзакции int allready_locked(CLockable*); }; // зарегистрироваться в транзакции inline int CLockable::regObj (CTransaction* _pt) { if (m_trans!= NULL) return 0; else { _pt-›addLock(this); m_trans = _pt; return 1; } } // разрегистрироваться inline void CLockable::unregObj() { m_trans = NULL; } // добавление объекта к транзакции. inline void CTransaction::addLock(CLockable* _lc) { // а именно, воткнуть указатель на него в стек. m_locks.push (_lc); } // закрепление всех объектов void CTransaction::commit() { // создаем итератор ampIter‹CLockable› it(&(this-›m_locks)); // пробежались по всем, закрепились. it.goStart(); while (!it.isLast()) it.moveNext()-›commit(); // Всех выкинуть из стека, разрегистрировать. while (!m_locks.isEmpty()) m_locks.pop()-›unregObj(); } // отмена всех объектов void CTransaction::rollback() { // создали итератор ampIter‹CLockable› it(&(this-›m_locks)); // пробежались по всем, отменились. it.goStart(); while (!it.isLast()) it.moveNext()-›rollback(); // Всех выкинуть из коллекции и разрегистрировать while (!m_locks.isEmpty()) m_locks.pop()-›unregObj(); } // проверка, зарегистрирован ли объект. int CTransaction::allready_locked(CLockable* _lc) { // создали итератор ampIter‹CLockable› it(&(this-›m_locks)); it.goStart(); while (!it.isLast()) if (it.moveNext() == _lc) return 1; return 0; } |
|
||
Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное |
||||
|