Reputation: 18451
I'm building a class with pure virtual functions called Database
. The idea is to have a class that handles all the database interfaces (ie: open
and close
) and can be used on my business layers.
The Database class will be implemented in several 'flavours' for different databases, like mySqlDatabase and OracleDatabase.
I imagined Database
having pure virtual methods with no code - just a header file as follows:
Database.hpp
class Database {
public:
Database();
virtual ~Database();
virtual void open(const std::string databasename) = 0;
virtual void open(const std::string databasename, const std::string username, const std::string password) = 0;
virtual void open(const std::string databasename, const std::string schema, const std::string username, const std::string password) = 0;
.
<Other stuff>
.
}
The three open
variations are there to support different database connection requirements, from the simplest one (like Sqlite3 that needs only a filename), to Oracle (that needs all of that variables to connect).
I have some questions about the implementations (let's take oracle for example):
a) Shall I need to redeclare the virtual methods again on the derived class header file, like:
class OracleDatabase : public Database {
public:
OracleDatabase ();
virtual ~OracleDatabase ();
void open(const std::string databasename);
void open(const std::string databasename, const std::string username, const std::string password);
void open(const std::string databasename, const std::string schema, const std::string username, const std::string password);
}
b) How do I structure the implementation of the open
methods in the derived class (let´s take Sqlite3)?
void Sqlite3Database::open(const std::string databasename){
...do some stuff...
}
void Sqlite3Database::open(const std::string databasename, const std::string username, const std::string password) {
...do some stuff...
}
void Sqlite3Database::open(const std::string databasename, const std::string schema, const std::string username, const std::string password) {
...do some stuff...
}
Am I using the right strategy? I've been browsing around virtual and pure virtual strategies and think this is the best approach for my problem.
Any suggestions/hints?
OBS: I'm coming from C# world so I do apologize if there is some misconception here.
Upvotes: 2
Views: 702
Reputation: 1054
For writing query functions (ie. same interface for all databases), pure virtual functions are the way to go.
Here, you are trying to write an open
function, for which you might want to consider the Factory Design Pattern: you write your Database withour any open
function; and you write a function such as static std::unique_ptr<Database> Sqlite3Database::open(/*...*/)
.
Using a virtual function like the one you are advocating is not a good idea: anyway you have 3 different functions that completely depend on the database that is used; and worse, your mother class depends on its children: to add a new database with another logging scheme, you have to add a function prototype to Database
.
Another way to go would be to use a pure virtual function (preferably protected
and called from constructor to preserve RAII; and following the NVI idiom) that takes as argument an initialization string such as the one used by PDO. Not exactly the same as anyway the database type can be inferred from the type instantiated, but the idea is to keep a single argument so as not to have multiple versions of open
(Old answer kept for the principles it tried to explain)
Actually you can do much easier: forget about open
, and just do all of your initialization inside Sqlite3Database::Sqlite3Database(/* ... */)
.
After all, there is no way you can open a database without knowing which kind of DB it is (as you have to know a username/password, and even more: you have to know what arguments are required), so there is no sense in trying to make a virtual pure function out of this.
So, an example of what you could do:
class Database {
public virtual void create(/* ... */) = 0;
// ...
};
class Sqlite3Database : public Database {
Sqlite3Database(string filename);
public virtual void create(/* ... */) override;
// ...
};
class MySqlDatabase : public Database {
MySqlDatabase(int host, short port, string username, string password);
public virtual void create(/* ... */) override;
};
Upvotes: 3