对不起,我想不出这个问题的好标题......
在应用程序启动时,我通过数据库访问库从数据库加载对象,然后调用它们的类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;
}
但明显的缺点是,我必须复制包装类的完整界面 为了避免重复接口,我可以提供对包装对象的访问,但是这使得包装器实际上没用,因为如果用户不够谨慎,用户将能够得到错误的状态。
有没有一种优雅的方式来实现我的目标?
简而言之:我希望对存储对象状态的用户透明。用户应通过调用一种方法获得当前状态。我希望这会让它更清晰一点。
答案 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
甚至可以实现一个观察者模式,以便在客户更新时通知客户......