如何修改不可变对象?

时间:2009-08-28 15:07:27

标签: c++ wrapper immutability

对不起,我想不出这个问题的好标题......

在应用程序启动时,我通过数据库访问库从数据库加载对象,然后调用它们的类CDbObject

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

protected:
    CState m_state;
}

在运行时,我会收到与这些数据库对象中的状态更改相对应的消息 我希望最小化数据库访问,因此在状态改变时不会重新加载整个对象 相反,我想包装数据库对象,并以某种方式使它们可修改 我不能简单地派生一个类,实现一个setState()方法并创建该类的对象,因为我必须使用DB访问库给我的对象。对象没有实现复制机制,如果可能,我根本不想触摸该代码。

我可以创建一个包装器类来存储指向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;
}

但明显的缺点是,我必须复制包装类的完整界面 为了避免重复接口,我可以提供对包装对象的访问,但是这使得包装器实际上没用,因为如果用户不够谨慎,用户将能够得到错误的状态。

有没有一种优雅的方式来实现我的目标?

编辑:

简而言之:我希望对存储对象状态的用户透明。用户应通过调用一种方法获得当前状态。我希望这会让它更清晰一点。

3 个答案:

答案 0 :(得分:1)

我和Jason在一起,一个派生自CDbObject的类很容易解决这个问题,除非那些对象来自一些不透明的接口。

但是,如果您的应用程序崩溃,并且这些状态没有写回数据库,那会造成什么样的破坏?很多时候db都存在持久性,如果你删除/隐藏持久性,你可以优雅地恢复吗?

答案 1 :(得分:0)

您可以找到operator->有用。根据您在处于无效状态时调用函数时的行为,将此函数添加到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();

答案 2 :(得分:0)

这个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甚至可以实现一个观察者模式,以便在客户更新时通知客户......