Reputation: 1932
I currently have a running code I use to connect to a database through a given API (libpq).
I basically have two classes : the main application class CMain, and the database related class (in charge of connecting to the database, issueing the query and storing the results) called CDbConnectorPq.
In my CMain constructor, I currently instantiate a class member of type CDbConnectorPq, and then I use that object to trigger the connection and the querying, directly from my CMain object :
main->dbConnectorPq->connect();
The problem is I need now to implement another API to connect to the database (ODBC), in addition to libpq. I'm trying to figure out what could be a decent modelling to keep separated the libpq code from the odbc code, but still using the very same interface from my main object.
I thought about inheritance but I end up with having 2 classes, CDbConnectorPq and CDbConnectorODBC, and then in my main having now two members : one for libpqxx, one for odbc, and being forced to :
if ( gl_use_config_odbc )
dbConnectorOdbc = new CDbConnectorODBC();
else if ( gl_use_config_libpq )
dbConnectorPq = new CDbConnectorPq();
and then :
if ( gl_use_config_odbc ) {
dbConnectorOdbc->connect();
dbConnectorOdbc->query();
}
else if ( gl_use_config_pq ) {
dbConnectorPq->connect();
dbConnectorPq->query();
}
Don't know how to leverage inheritance here to mask API specific logic from my main object.
Ideally, the interface should be (I think) :
( if gl_use_config = PQ) dbConnector->setType(gl_use_config); dbConnector->connect(....); dbConnector>connect(...);
or ( if gl_use_config = ODBC ) dbConnector->setType(gl_use_config); dbConnector->connect(param1,param2); dbConnector>connect(query1,query2);
Because yes, argument numbers could be different in number and type between PQ and ODBC...
So, what could be a smart modelling for this case ?
Upvotes: 1
Views: 86
Reputation: 56863
This is not going to be your typical SO answer, but it's what I feel about this. Having written several database layers for different companies with OCI, OCCI, OTL, RogueWave, libpq and libpqxx in the last 15 years, my advice is this: Just don't. Seriously.
Either use ODBC exclusively, even to connect to PostgreSQL (which I personally don't like) or you should develop two completly separated database access layers. If you try to unify the interfaces and the usage, you will face many problems and it takes years of experience. And even then, with every new project you will find another problem and the interface will change more often than you'd like.
If you want to develop separate layers, you can do a few things to ease porting an application from one to the other:
typedef
s or, even better, use a namespace alias.With this, porting becomes easier. When you port the application from one layer to the other, change the typedef
s or the namespace alias in the header and let the compiler help you find those things you need to adapt manually.
Upvotes: 2
Reputation: 73041
I think the way to do it is to have your CMain class hold a pointer of an abstract base class that both subclasses derive from. E.g.
class IDatabase
{
public:
virtual void connect() = 0;
[...]
};
Then the only place you need to do if/then logic is when you create the object of the appropriate class:
if ( gl_use_config_odbc ) {
dbConnector = new CDbConnectorODBC();
} else {
dbConnector = new CDbConnectorPq();
}
Everything after that is the same as before:
dbConnector->connect();
... and the C++ virtual-method-lookup mechanism will automatically call the appropriate method based on the type of object that (dbConnector) is pointing to.
As far as differing argument numbers, try to keep the code for implementation details like that inside the two subclasses, so that the main code doesn't have to worry about them. The IDatabase interface that CMain sees should be implementation-neutral.
Upvotes: 0