1

我目前有一个正在运行的代码,用于通过给定的 API (libpq) 连接到数据库。

我基本上有两个类:主应用程序类CMain,和数据库相关类(负责连接数据库、发出查询和存储结果)称为CDbConnectorPq。

在我的 CMain 构造函数中,我目前实例化了一个 CDbConnectorPq 类型的类成员,然后我使用该对象直接从我的 CMain 对象触发连接和查询:

main->dbConnectorPq->connect();

问题是除了 libpq 之外,我现在还需要实现另一个 API 来连接到数据库 (ODBC)。我试图弄清楚什么可能是一个体面的建模,以将 libpq 代码与 odbc 代码分开,但仍然使用与我的主要对象完全相同的接口。

我考虑过继承,但最终我得到了 2 个类,CDbConnectorPq 和 CDbConnectorODBC,然后我现在主要有两个成员:一个用于 libpqxx,一个用于 odbc,并且被迫:

if ( gl_use_config_odbc )
  dbConnectorOdbc = new CDbConnectorODBC();
else if ( gl_use_config_libpq )
  dbConnectorPq = new CDbConnectorPq();

进而 :

if ( gl_use_config_odbc ) {
  dbConnectorOdbc->connect();
  dbConnectorOdbc->query();
}
else if ( gl_use_config_pq ) {
  dbConnectorPq->connect();
  dbConnectorPq->query();
}

不知道如何在这里利用继承来掩盖我的主要对象中的 API 特定逻辑。

理想情况下,界面应该是(我认为):

(如果 gl_use_config = PQ) dbConnector->setType(gl_use_config); dbConnector->connect(....); dbConnector>connect(...);

或 ( 如果 gl_use_config = ODBC ) dbConnector->setType(gl_use_config); dbConnector->connect(param1,param2); dbConnector>连接(查询1,查询2);

因为是的,参数编号在 PQ 和 ODBC 之间的数量和类型可能不同......

那么,对于这种情况,什么是智能建模?

4

2 回答 2

2

这不会是您典型的 SO 答案,但这就是我对此的感受。在过去的 15 年里,我为不同的公司使用 OCI、OCCI、OTL、RogueWave、libpq 和 libpqxx 编写了多个数据库层,我的建议是:不要这样做。严重地。

要么只使用 ODBC,甚至连接到 PostgreSQL(我个人不喜欢),要么你应该开发两个完全分离的数据库访问层。如果你试图统一接口和使用,你会面临很多问题,需要多年的经验。即便如此,对于每个新项目,您都会发现另一个问题,并且界面的更改频率会超出您的预期。

如果您想开发单独的层,您可以做一些事情来简化将应用程序从一个层移植到另一个层:

  • 使它们尽可能相似,但不要强求!
  • 如果两个层具有相同的功能和相同的语义,则使用相同的名称。
  • 如果两个层具有不同语义的相同功能,请使用不同的名称。
  • 在使用数据库层的应用程序中,使用一个包含数据库标头并通过typedefs 使用它们的标头,或者更好的是,使用命名空间别名。

有了这个,移植变得更容易。当您将应用程序从一层移植到另一层时,更改typedef标头中的 s 或命名空间别名,让编译器帮助您找到需要手动调整的内容。

于 2013-03-28T00:11:41.573 回答
0

我认为这样做的方法是让您的 CMain 类拥有一个抽象基类的指针,两个子类都派生自该抽象基类。例如

class IDatabase
{
public:
   virtual void connect() = 0;

   [...]
};

那么您需要做的唯一地方是 if/then 逻辑是当您创建适当类的对象时:

if ( gl_use_config_odbc ) {
    dbConnector = new CDbConnectorODBC();
} else {
    dbConnector = new CDbConnectorPq();
}

之后的一切都和之前一样:

dbConnector->connect();

...并且 C++ 虚拟方法查找机制将根据 (dbConnector) 指向的对象类型自动调用适当的方法。

至于不同的参数编号,尽量将实现细节的代码保留在两个子类中,这样主代码就不必担心它们。CMain 看到的 IDatabase 接口应该是实现中立的。

于 2013-03-27T23:51:02.407 回答