这是一个理论问题,不是实际问题。
我正在尝试设计一个遵循软件最佳实践的程序。但我无法找出最佳方法来执行某些允许测试驱动开发和良好封装和抽象的事情,同时还要最大限度地减少耦合。
我计划有两个抽象层,一个数据库管理层和一个表管理层。
class TableManager()
{
init() {
manager = DatabaseManger()
}
do_operation(String operation) {
connection = manager.get_db_connection()
connection.execute(operation)
}
}
这似乎是个好主意,因为实际的连接细节对表管理器是隐藏的。但这似乎也很糟糕,因为 TableManager 与 DatabaseManager 高度耦合,为了测试,我需要有一个单独的数据库连接来设置数据库状态,比如删除数据库和表,所以我们在数据库不存在时测试操作存在之类的东西
有人可以在这里权衡一下并挑战我的假设吗?我可能高估了某些因素的重要性或低估了其他因素。这是设计模式的常见问题吗?
最佳答案
考虑将 manager
作为构造函数参数传递给 TableManager
,这样您就可以有两个不同的数据库管理器实现:一个具体的和一个模拟的。
// These two classes have the same interface.
class ConcreteDBManager
{
auto numberOfTables()
{
auto connection = connectToRealDB();
return connection.queryAs<int>("COUNT TABLES");
}
};
class MockDBManager
{
auto numberOfTables()
{
return 5;
}
};
template <typename TDatabaseManager>
class TableManager
{
TDatabaseManager _manager;
TableManager(const TDatabaseManager& manager)
: _manager{manager}
{
}
auto numberOfTables()
{
return _manager.numberOfTables();
}
};
使用这种方法,您可以通过提供一个 MockDBManager
实例来测试 TableManager
的功能,这样您就不必连接到您的实际数据库单元测试...
TEST(TableManager, RetrieveNumberOfTables)
{
MockDBManager mockDBManager;
TableManager<MockDBManager> tableManager(mockDBManager);
EXPECT_EQ(5, tableManager.numberOfTables());
}
...但是您可以使用相同的 TableManager
接口(interface)连接到生产代码中的真实数据库:
void realProductionCode()
{
ConcreteDBManager concreteDBManager;
TableManager<ConcreteDBManager> tableManager(concreteDBManager);
std::cout << tableManager.numberOfTables();
}
关于c++ - 设计模式会破坏抽象吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41506311/