2015-04-24 3 views
2

Я строю класс с чистыми виртуальными функциями, называемый Database. Идея состоит в том, чтобы иметь класс, который обрабатывает все интерфейсы базы данных (то есть: open и close) и может использоваться на моих бизнес-слоях.Как реализовать чистые виртуальные функции с различными структурами параметров

Класс базы данных будет реализован в нескольких «вариантах» для разных баз данных, таких как mySqlDatabase и OracleDatabase.

Я представил Database имея чистые виртуальные методы с без кода - только заголовочный файл следующим образом:

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> 
. 

} 

Три open вариации существуют, чтобы поддерживать различные требования подключения к базе данных , от простейшего (например, Sqlite3, которому требуется только имя файла), для Oracle (которому нужны все эти переменные для подключения).

У меня есть несколько вопросов по поводу реализации (давайте оракул, например):

а) Должен ли я должен снова переобъявить виртуальные методы в производном заголовочном файле класса, как:

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

б) Как я могу структурировать реализацию методов open в производном классе (возьмем 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... 
    } 

Я использую правильную стратегию? Я просматривал виртуальные и чистые виртуальные стратегии и считаю, что это лучший подход для моей проблемы.

Любые предложения/подсказки?

OBS: Я иду из мира C#, поэтому я извиняюсь, если здесь есть какое-то заблуждение.

+1

Вы смотрели в интерфейс на основе шаблона дизайна? http://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns#Interface-based_Programming_.28IBP.29 –

+0

Я знаком с шаблоном проектирования интерфейса. Я использовал его уже в C# и это то, что я хочу выполнить ... Мои сомнения связаны с синтаксисом C++ и поведением о том, как его реализовать, особенно имея разные «открытые» методы ... – Mendes

+3

Я бы рекомендовал маркировать ваши функции-члены как 'override' в подклассе, как в: 'void open (const std :: string databasename) override;' Это гарантирует, что вы получите ошибку компилятора, если вы на самом деле ничего не переопределяете. –

ответ

2

Для написания функций запроса (то есть одного и того же интерфейса для всех баз данных), чистые виртуальные функции - это путь.

Здесь вы пытаетесь написать open функцию, для которой вы могли бы хотеть рассмотреть Factory Design Pattern: вы пишете базы данных Withour любой open функции; и вы пишете такую ​​функцию, как static std::unique_ptr<Database> Sqlite3Database::open(/*...*/).

Использование виртуальной функции, аналогичной той, которую вы защищаете, не является хорошей идеей: в любом случае у вас есть 3 разных функции, которые полностью зависят от используемой базы данных; и, что еще хуже, ваш материнский класс зависит от его детей: чтобы добавить новую базу данных с другой схемой регистрации, вам нужно добавить прототип функции в Database.

Другой путь будет использовать чистую виртуальную функцию (предпочтительно protected и вызывается из конструктора, чтобы сохранить RAII, и после NVI idiom), который принимает в качестве аргумента строку инициализации, такие как той, которая используется PDO.Не совсем то же самое, что и в любом случае тип базы данных можно вывести из созданного типа, но идея состоит в том, чтобы сохранить один аргумент, чтобы не иметь несколько версий open

(Старый ответ был сохранен для принципов, которые он пытался объясните)

На самом деле вы можете сделать гораздо проще: забудьте о open и просто выполните всю свою инициализацию внутри Sqlite3Database::Sqlite3Database(/* ... */).

В конце концов, вы не можете открыть базу данных, не зная, какой из них она есть (так как вам нужно знать имя пользователя/пароль и даже больше: вы должны знать, какие аргументы необходимы), поэтому нет смысла пытаться сделать из этого виртуальную чистую функцию.

Итак, пример того, что вы могли бы сделать:

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

Ekleog, я получил вашу мысль ... Идея конструктора/деструктора имеет смысл. Я просто не хочу использовать PDO, поскольку это будет путать для вышеупомянутых слоев sw ... Я думаю о создании разных конструкторов, требующих точные параметры для реализованного типа db ... – Mendes

+0

Сохранение открытости ... В RAII это не имеет никакого смысла ... – Mendes

+0

Я просто добавлю пример того, что именно я имею в виду. Это не использует PDO. – Ekleog

Смежные вопросы