The c++ guidelines say not to put any data in the base class and also not to use trivial getter and setter methods, but rather just member variables, but where do I put the member variables in my data base access implementation? If I put it in one of the derived classes, I get the compiler error that my class db_interface does not have such a member.
#include <boost/core/noncopyable.hpp>
#include <postgresql/libpq-fe.h>
#include <sqlite3.h>
#include <iostream>
#include <string>
class db_interface : boost::noncopyable{
public:
void connect() const {connect_to_DB();}
virtual ~db_interface(){};
private:
virtual void connect_to_DB() const = 0;
};
class postgreSQL : public db_interface{
private:
void connect_to_DB() const { std::cout << "THIS IS POSTGRESQL"<< std::endl; }
};
class liteSQL : public db_interface{
public:
std::string dbName;
private:
void connect_to_DB() const { std::cout << "THIS IS LIGHTSQL"<< std::endl; }
};
class DBFactory {
public:
virtual db_interface *createDB(std::string) = 0;
};
class Factory: public DBFactory {
public:
db_interface *createDB(std::string type) {
if(type == "LiteSQL") {
return new liteSQL;
}
else if(type == "PostgreSQL") {
return new postgreSQL;
}
return nullptr;
}
};
It's worth pointing out the guidelines are just that. Some of them even contradict each other. It's up to you to be the engineer and decide what guidelines make sense for you and your project.
However, these guidelines do not really contradict each other.
The first one you linked to is: C.133: Avoid protected data
The other guideline linked in that one is: C.121: If a base class is used as an interface, make it a pure abstract class
The other guideline linked is the one you seem to be skipping and subsequently tripping yourself over: C.9: Minimize exposure of members
C.133 links to last one with the text Prefer private
data.
The ideas being presented do not conflict, but they do take the separation of concerns to a level that is less common in C++ land.
Mainly, your interface should just be an interface. In that regard, putting data in your interface makes no sense.
You can then derive from that interface to create your 'base' class. When here, don't make your data protected
. Make it private
. Your base class is then the only class responsible for that data. Protected functions like getters and setters do make sense here for your derived classes to make changes which your base class alone is responsible for handling/validating.
On top of that, I actually don't like the idea of the interface being all public pure virtual functions and instead I prefer NVI or Non-Virtual Interfaces. Here's a short example:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Interface
class PetInterface {
public:
virtual ~PetInterface() = default;
std::string speak() const { return speak_v(); }
std::string name() const { return name_v(); }
private:
virtual std::string speak_v() const = 0;
virtual std::string name_v() const = 0;
};
// Base class
class Pet : public PetInterface {
public:
Pet() = default;
Pet(std::string name, std::string vocal) : m_name(name), m_vocal(vocal) {}
virtual ~Pet() = default;
private:
std::string m_name;
std::string m_vocal;
protected:
std::string name() const { return m_name; }
std::string vocal() const { return m_vocal; }
};
class Dog final : public Pet {
public:
Dog() = default;
Dog(std::string name) : Pet(name, "Woof") {}
private:
std::string speak_v() const override { return vocal(); }
std::string name_v() const override { return name(); }
};
class Cat final : public Pet {
public:
Cat() = default;
Cat(std::string name) : Pet(name, "Meow") {}
private:
std::string speak_v() const override { return vocal(); }
std::string name_v() const override { return name(); }
};
int main() {
std::vector<std::unique_ptr<PetInterface>> pets;
pets.emplace_back(new Dog("Fido"));
pets.emplace_back(new Cat("Dame Whiskers"));
for (const auto& i : pets) {
std::cout << i->name() << " says \"" << i->speak() << "\"\n";
}
}
Output:
❯ ./a.out
Fido says "Woof"
Dame Whiskers says "Meow"
Pet
is still an abstract class, as only 'leaf' classes should be concrete.
One final note is that you don't need boost to make a thing non-copyable. Just = delete
the copy constructor and copy assignment operator.