views:

239

answers:

3

Sorry I couldn't think of a good title for this question...

At application start I load objects from a database via a DB access library, lets call their class CDbObject.

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

protected:
    CState m_state;
}

At runtime, I receive messages which correspond to state changes in these database objects.
I want to minimize DB access and thus not reload the complete object when its state changed.
Instead I want to wrap the DB objects and make them modifyable somehow.
I cannot simply derive a class, implement a setState() method and create objects of that class, because I have to use the objects the DB access library gives me. The objects have no copy-mechanism implemented and I don't want to touch that code at all, if possible.

I could create a wrapper class that stores a pointer to the CDbObject instance and looks like this:

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;
}

But the obvious disadvantage is, that I'd have to duplicate the complete interface of the wrapped class.
In order to avoid duplicating the interface I could provide access to the wrapped object but this makes the wrapper practically useless because the user would be able to get the wrong state if he's not cautious enough.

Is there an elegant way to accomplish what I want?

Edit:

In short: I want to make it transparent to the user where the status of the object is stored. The user shall get the current status by calling one method. I hope this makes it a bit clearer.

A: 

You may find operator-> useful. Depending on what you want the behavior to be when you call a function when in an invalid state, add either this funtion to CWrapper:

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

or this:

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

Then use the wrapper kind of like an auto_ptr:

CWrapper db_wrapper(db_object_ptr);
db_wrapper->f();
Darryl
+1  A: 

I'm with Jason, a class derived from CDbObject fixes the problem pretty easily unless those objects are coming from some opaque interface.

However, if your application crashes, and those status aren't written back to the db, what kind of havoc does that cause? Lots of times the db is there for persistence, if you remove/hide that persistence, can you gracefully recover?

Mark0978
The stuff is actually written to the DB but not by my application. I really only want to avoid having to load the data again.
mxp
A: 

It seems that there are two views on this CDbObject: the 'client code' view, which sees it as not changeable, and the server connection, which receives updates for the object.

So why not keep a CDbObject* (which is writeable) in the database layer, and provide the client with a const CDbObject* pointer to it? This solves the need for different views.

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; 
      }
   }
};

In a further stage, the CDbObject could even implement an observer pattern to notify the client when it's updated...

xtofl