问题描述
我正在用单例模式编写一个类。
class GroupListDAO {
public:
static GroupListDAO* getInstance() {
static GroupListDAO groupListDAO;
return &groupListDAO;
}
init(server::MysqLdb::MysqLHelperTempalte* pHelper) {
MysqLHT = pHelper;
}
bool getUserHeartNum(uint32_t owner,uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner,uint32_t totalNum,uint32_t update_time);
private:
MysqLHelperTempalte *MysqLHT;
GroupListDAO() = default;
~GroupListDAO() = default;
};
如你所见,这个类是用来连接MysqL的。因此,在调用任何其他成员函数之前,必须先初始化成员数据 MysqLHT
。
总之,类用户必须使用这个类如下:
GroupListDAO *p = GroupListDAO::getInstance();
p->init(XXX); // init must be called before calling any other member functions
p->getUserHeartNum(...);
p->setUserHeartNum(...);
所以我在想是否有办法强制类用户调用函数init
。这意味着如果类用户代码如下:
GroupListDAO *p = GroupListDAO::getInstance();
p->getUserHeartNum(...);
p->setUserHeartNum(...);
可能会产生一些编译时错误。
Ofc,你可能会说我们可以在其他成员函数中if (MysqLHT == nullptr) { throw exception; }
,但这会是运行时错误,而不是编译时错误。
真实案例
一个大型项目由 5 个开发人员开发。他们都在使用一些 Singleton 对象。但他们必须说:嘿,我已经初始化了,你们可以在代码中使用它。或者,对不起,我们都忘记初始化了...
解决方法
正如评论中所建议的,您可以使用带有默认值的参数:
C:\Program Files\Python36
一方面,让调用者传递参数但只在第一次调用时使用它并不好。另一方面,您的设计表明有一个地方您知道它是对 getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr)
的第一次调用(您不想 getInstance
两次,对吗?)。因此我建议:
init
class GroupListDAO {
public:
static GroupListDAO* createInstance(server::mysqldb::MysqlHelperTempalte* pHelper) {
return getInstance(pHelper);
}
static GroupListDAO* getInstance() {
return getInstance(nullptr);
}
private:
static GroupListDAO* getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr) {
static GroupListDAO groupListDAO(pHelper);
return &groupListDAO;
}
};
可能需要成为实际对象的包装器,并且可以在使用 GroupListDAO
调用时抛出其构造函数。我不知道创建编译器错误的简单方法。
PS:实际上我认为您有相互冲突的要求。一方面,您需要一个对调用者隐藏初始化的单例。可以通过 nullptr
获取实例,无需关心初始化。另一方面,您确实想关心初始化,因为对 getInstance
的第一次调用是“特殊的”。单例模式和使用 getInstance
方法都不是孤立的完美习惯用法。当一起使用时,它们会更糟。我并不是说这是绝对不行的,但在两种方法都应该保留的情况下必须做出妥协也就不足为奇了。
如果你真的想要一个编译错误,你就必须摆脱你的单例模式:
class GroupListDao
{
public:
GroupListDao(...) { /* do your initialization here */ }
bool getUserHeartNum(uint32_t owner,uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner,uint32_t totalNum,uint32_t update_time);
// ...
}
这样可以确保 GroupListDao
的任何实例都已初始化。然后你必须在你的代码中传递这个实例。因此,您不再能确定在编译时您的应用程序中只有一个 GroupListDao
实例(如果您确实需要,您可以在构造函数中跟踪已创建实例的数量并在运行时引发错误)。
选择你的毒药 ;)