工厂设计模式在C++中的应用

工厂设计模式是一种在软件开发中常用的设计模式,它提供了一种创建对象的最佳方式。在C++中,这种模式尤其有用,因为它可以帮助封装对象的创建过程,从而提高代码的可读性和可维护性。本文将探讨工厂设计模式的动机、两种实现方式,以及如何通过RAII(资源获取即初始化)和智能指针来简化代码和提高代码的可维护性。

使用工厂设计模式的动机

工厂设计模式的主要动机是封装对象的创建过程。这样做的好处是多方面的:

首先,它有助于将对象的初始化代码与使用对象的代码分离,从而提高代码的可读性和可维护性。例如,如果第三方库的构造函数没有提供足够的对象初始化功能,可能需要额外的代码来进行构建和配置。通过在工厂类和方法中集中这些代码,可以轻松地管理和重用配置。如果第三方库随时间发展,只需要在工厂中维护代码,而不需要在代码库的多个实例中进行更新。

其次,工厂设计模式在解耦客户端代码和具体类方面发挥着关键作用。客户端代码不直接实例化类,而是依赖工厂来创建实例,这促进了松耦合。这种解耦允许在运行时进行多态行为,并且能够实现缓存机制或单例实例。它将使用创建的实例的代码与这些关注点隔离开来。

如果稍微扩展设计模式,工厂的抽象将有助于在测试中替换具体实现。这也是解耦的一个好处。

实现方式

将检查工厂设计模式的两种实现方式。它们创建了一个SQLite数据库,其中包含一个表,并从中写入和读取数据。虽然它们可能作为设计模式使用的例子过于复杂,但这里的代码专注于在少量代码中提供实用的东西。

以下代码片段显示了第一种实现方式,这是一种传统的实现方式。工厂类持有数据库的标识,其工厂方法创建一个与之连接的数据库连接。

#include #include namespace sample { class DbConnFactory { private: std::string mDbName; public: DbConnFactory(const std::string& dbName); sqlite3 *open(); }; } // namespace sample

在这段代码中,定义了一个名为DbConnFactory的类,它有一个私有成员变量mDbName,用于存储数据库名称。公开的构造函数接受一个数据库名称,并将其存储在mDbName中。open()方法用于打开数据库并返回一个指向sqlite3结构的指针。

main函数使用工厂类创建数据库连接两次:一次用于数据库初始化,一次用于从中读取记录。

#include #include #include #include #include "DbConnFactory.hpp" static const int LINE_MAX_LENGTH = 30; void execIn(sqlite3* db, const std::string &sql, int (*callback)(void *notUsed, int argc, char **argv, char **colName) = nullptr) { std::cout << "[SQL] " << sql << std::endl; char *err = nullptr; int rc = sqlite3_exec(db, sql.c_str(), callback, 0, &err); if (rc != SQLITE_OK) { std::string errMsg = "SQL Error (" + std::to_string(rc) + "): " + std::string(err); sqlite3_free(err); throw std::runtime_error(errMsg); } } void createDb(sample::DbConnFactory& factory) { sqlite3 *db = factory.open(); try { execIn(db, "CREATE TABLE IF NOT EXISTS ORDER_ENTRY (ID INTEGER PRIMARY KEY AUTOINCREMENT, CUSTOMER TEXT NOT NULL);"); execIn(db, "INSERT INTO ORDER_ENTRY (CUSTOMER) VALUES ('Paul'); INSERT INTO ORDER_ENTRY (CUSTOMER) VALUES ('Mark');"); } catch (std::exception const &ex) { sqlite3_close(db); throw; } sqlite3_close(db); } void readDb(sample::DbConnFactory factory) { sqlite3 *db = factory.open(); std::cout << std::string(20, '-') << std::endl; execIn(db, "SELECT * FROM ORDER_ENTRY;", []( void *notUsed, int argc, char **argv, char **colName) { std::cout << std::string(20, '-') << std::endl; for (int i = 0; i < argc; i++) std::cout << colName[i] << ": " << (argv[i] ? argv[i] : "NULL") << std::endl; return 0; }); std::cout << std::string(20, '-') << std::endl; } int main(int argc, char *arg[]) { sample::DbConnFactory factory("sample.db"); try { createDb(factory); readDb(factory); } catch (std::exception const &ex) { std::cerr << "[ERROR] " << ex.what() << std::endl; } }

这种实现方式的一个缺点是工厂方法返回一个指针。因此,调用者负责正确销毁返回的实例。在SQLite数据库连接的情况下,需要调用sqlite3_close函数,这在代码中多次出现。依赖调用者进行对象处理有点风险。在实践中,确保在所有调用者代码中实现这一点也是成本高昂的。

以下实现通过将对象销毁责任转移到工厂方法中,使用RAII智能指针来改进代码。工厂方法创建一个数据库连接,并将其包装在一个类中,然后将其作为唯一指针返回。智能指针确保返回的实例将被销毁,包装类负责正确销毁数据库连接实例。

#ifndef H_SMART_DB_CONN_FACTORY #define H_SMART_DB_CONN_FACTORY #include #include #include namespace sample { class SmartDbConn { private: sqlite3* mDb; public: SmartDbConn(sqlite3* db); ~SmartDbConn(); sqlite3* db() { return mDb; } }; class SmartDbConnFactory { private: std::string mDbName; public: SmartDbConnFactory(const std::string& dbName); std::unique_ptr open(); }; } // namespace sample #endif

在这段代码中,定义了两个类:SmartDbConn和SmartDbConnFactory。SmartDbConn类封装了一个sqlite3数据库连接,并在其析构函数中负责关闭数据库连接。SmartDbConnFactory类负责创建SmartDbConn实例,并将其作为唯一指针返回。

#include #include #include #include #include "SmartDbConnFactory.hpp" static const int LINE_MAX_LENGTH = 30; void execIn(sample::SmartDbConn &db, const std::string &sql, int (*callback)(void *notUsed, int argc, char **argv, char **colName) = nullptr) { std::cout << "[SQL] " << sql << std::endl; char *err = nullptr; int rc = sqlite3_exec(db.db(), sql.c_str(), callback, 0, &err); if (rc != SQLITE_OK) { std::string errMsg = "SQL Error (" + std::to_string(rc) + "): " + std::string(err); sqlite3_free(err); throw std::runtime_error(errMsg); } } void createDb(sample::SmartDbConnFactory& factory) { auto db = factory.open(); execIn(*db, "CREATE TABLE IF NOT EXISTS ORDER_ENTRY (ID INTEGER PRIMARY KEY AUTOINCREMENT, CUSTOMER TEXT NOT NULL);"); execIn(*db, "INSERT INTO ORDER_ENTRY (CUSTOMER) VALUES ('Paul'); INSERT INTO ORDER_ENTRY (CUSTOMER) VALUES ('Mark');"); } void readDb(sample::SmartDbConnFactory factory) { auto db = factory.open(); std::cout << std::string(20, '=') << std::endl; execIn(*db, "SELECT * FROM ORDER_ENTRY;", []( void *notUsed, int argc, char **argv, char **colName) { std::cout << std::string(20, '-') << std::endl; for (int i = 0; i < argc; i++) std::cout << colName[i] << ": " << (argv[i] ? argv[i] : "NULL") << std::endl; return 0; }); std::cout << std::string(20, '=') << std::endl; } int main(int argc, char *arg[]) { sample::SmartDbConnFactory factory("sample.db"); try { createDb(factory); readDb(factory); } catch (std::exception const &ex) { std::cerr << "[ERROR] " << ex.what() << std::endl; } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485