Best Construct for Db Abstraction with Inheritance
My application processes different messages and stores them in the database. Right now I have used the following design:
Database class:
class DbObject
{
public:
// read/write object members from/to DB
virtual void readFromDb() = 0;
virtual void writeToDb() = 0;
// Other stuff, db connection etc.
void doDbStuff();
}
Base class for messages:
class BaseMsg
{
public:
// read/write object members from/to DB
virtual std::string toXml() = 0;
virtual void fromXml(const & std::string s) = 0;
}
Message types A, B, etc.
class MsgA : public BaseMsg, DbObject
{
public:
std::string toXml();
void fromXml(const & std::string s);
void readFromDb();
void writeToDb();
}
This project works well, each processed message gets its own object, can be written to XML, read from XML, saved and read to / from a database with specific messages (implemented in MsgA).
However, we are currently considering porting the application to a new platform where the current database is not available, so we will use an additional database type.
Typically I would have a base class for database access and child classes for each type of database. But with the current design it is not possible, because I don't want to have an MsgADatabase1 class and an MsgADatabase2 class, etc.
Is there some design pattern where I could keep the current design in principle, but hide the currently used database type behind some level of abstraction?
source to share
1. First thought would be to see it DbOject
as an adapter.
According to the Gang of Four, adapter will offer a target interface for an adapted object (a specific database object), either through multiple inheritance or in composition.
In the context of your multiple inheritance, this design will impose that for each DbObject
that you inherit in a message, its constructor will create a single reference to a specific DB project. But how can a constructor know which specific db class to create?
2.Next thought: combine this adapter with an abstract factory
The most natural pattern for creating a concrete database dependent object that instantiates an abstract database independent would be using the abstract factory tag .
Combined with an adapter, you would have the following scenario:
- on startup, the virtual factory object (class
Database
) gets an instance with a specific dependent database factory (class DatabaseBrandA : public Database
) - a virtual factory provides a function to create
DbIndependentObject
. A concrete factory implements this function by providing its own concrete objects (class DbBrandAdependentObject : public DbIndependentObject
) - Each time a message is generated, the underlying adapter
DbObject
requests a virtual factory to create a new object. So it is referring toDbIndependentObject
, implemented withDbBrandAdependentObject
)
However, I don't know if your content is really DbObject
so rich that it needs a one-to-one relationship with a specific database object. Isn't such a complex model redundant?
3. Final conclusion: adapter combined with proxy template
I suspect your DbObject
only means a convenient way to get virtual read and write functions implemented by a derived message object that can be called whenever interaction with the database is required.
If your current code is designed to DbOject
encapsulate everything related to a database, you just need a proxy to that database.
This will work according to the following scenario:
- on startup, abstract database object (class
Database
) gets an instance with concrete database (class DatabaseBrandA : public Database
) - Each time a message is generated, the database
DbObject
acts as a proxy that refers to the database object. - Each time a database operation needs to be performed, it
DbObject
acts as an adapter for the message, providing the necessary read-write functionality.
source to share