Как изменить неизменяемый объект? - PullRequest
2 голосов
/ 28 августа 2009

Извините, я не могу придумать хорошего названия для этого вопроса ...

При запуске приложения загружаю объекты из базы данных через библиотеку доступа к БД, давайте вызывать их класс CDbObject.

class CDbObject
{
   //...
   virtual CState getState() const { return m_state; }

protected:
    CState m_state;
}

Во время выполнения я получаю сообщения, которые соответствуют изменениям состояния в этих объектах базы данных.
Я хочу минимизировать доступ к БД и, следовательно, не перезагружать весь объект, когда его состояние изменилось.
Вместо этого я хочу обернуть объекты БД и сделать их модифицируемыми.
Я не могу просто получить класс, реализовать метод setState() и создать объекты этого класса, потому что я должен использовать объекты, которые мне предоставляет библиотека доступа к БД. В объектах не реализован механизм копирования, и я не хочу касаться этого кода, если это возможно.

Я мог бы создать класс-оболочку, который хранит указатель на экземпляр CDbObject и выглядит так:

class CWrapper
{
public:
    CWrapper(CDbObject* p): m_pDbObject(p)
    {
        m_state.setValid(false);
    }

    void setState(CState state)
    {
        m_state.copy(state);
        m_state.setValid(true);
    }

    CState getState() const
    {
        return m_state.isValid() ? m_state : m_pDbObject->getState();
    }

private:
    CDbObject* m_pDbObject;
    CState m_state;
}

Но очевидным недостатком является то, что мне придется дублировать весь интерфейс обернутого класса.
Чтобы избежать дублирования интерфейса, я мог бы предоставить доступ к обернутому объекту, но это делает обертку практически бесполезной, потому что пользователь сможет получить неправильное состояние, если он не будет достаточно осторожен.

Есть ли элегантный способ выполнить то, что я хочу?

Edit:

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

Ответы [ 3 ]

1 голос
/ 28 августа 2009

Я с Джейсоном, класс, производный от CDbObject, решает проблему довольно легко, если только эти объекты не приходят из какого-то непрозрачного интерфейса.

Однако, если ваше приложение дает сбой и эти состояния не записываются обратно в БД, какой хаос это вызывает? Много раз БД существует для настойчивости, если вы удаляете / скрываете эту настойчивость, вы можете изящно восстановиться?

0 голосов
/ 31 августа 2009

Похоже, что у этого CDbObject есть два представления: представление "код клиента", которое видит его неизменным, и соединение с сервером, которое получает обновления для объекта.

Так почему бы не сохранить CDbObject* (доступный для записи) на уровне базы данных и не предоставить клиенту указатель const CDbObject* на него? Это решает необходимость разных представлений.

struct CDataBaseLayer {

   // map of mutable objects
   typedef std::map< CDbObject::key, CDbObject*> objectmap;
   objectmap objects;

   // retrieval of non-writeable objects
   const CDbObject* readObject( CDbObject::key key ) {
      objectmap::iterator it = objectmap.find( key );
      if( it == objectmap.end() ) {
        return fetchObject( key ); 
      } else { 
        it->second->refresh();
        return it->second; 
      }
   }
};

На следующем этапе CDbObject может даже реализовать шаблон наблюдателя, чтобы уведомлять клиента, когда он обновляется ...

0 голосов
/ 28 августа 2009

Вы можете найти оператора-> полезным. В зависимости от того, каким должно быть поведение при вызове функции в недопустимом состоянии, добавьте в CWrapper либо эту функцию:

CDbObject* operator->()
{
  return m_state.isValid() ? m_pDbObject : NULL;
}

или это:

CDbObject* operator->()
{
  if (!m_state.isValid())
    m_pDbObject.update();
  return m_pDbObject;
}

Затем используйте обертку типа auto_ptr:

CWrapper db_wrapper(db_object_ptr);
db_wrapper->f();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...