我公司的一些代码采用以下格式:
class ObjectA {
public:
ObjectA(ObjectB &objectB): objectB(objectB) { }
void Initialize() { ... }
private:
ObjectB &objectB;
};
class ObjectB {
public:
ObjectB(ObjectA &objectA): objectA(objectA) { }
void Initialize() { ... }
private:
ObjectA &objectA;
};
基本上,两个类相互依赖。
这本身并不是困扰我的事情,(尽管这不是一个很好的设计IMO),这是在两种情况下相互依赖都通过引用传递给构造函数 >。
因此,实际构造这对对象的唯一方法(在应用程序中它们都是必需的)是将不完整的引用作为依赖项传递。为了解决这个问题,将单独的Initialize
方法添加为第二阶段构造函数,然后在创建两个对象之后调用它们。因此,构造函数通常除了将构造函数参数分配给对象内部并用Intizialize
方法初始化所有内容外,什么都不做。
尽管代码可能是有效的,但我倾向于出于以下原因,这是一个有缺陷的设计:
Initialize
Initialize
被多次调用的,这可能会导致奇怪的,难以跟踪的错误我的同事告诉我,使用单独的initialize方法可以简化对象的构造,因为开发人员不必担心在应用程序根目录中声明的对象顺序。可以以完全任意的顺序声明和构造它们,因为一旦调用Initialize保证一切都有效。
我一直在倡导对象构造应该遵循依赖关系图的相同顺序,并且应该重新设计这些对象以消除相互依赖关系,但是我想看看SO专家必须说些什么。我以为这是不好的做法我错了吗?
编辑:
这些类的实际构造方式是通过如下所示的工厂类:
Factory {
public:
Factory():
objectA(objectB),
objectB(objectA) {
}
private:
ObjectA objectA;
ObjectB objectB;
};
答案 0 :(得分:1)
这是不好的做法,是的。通过objectB
引用objectA
可以使ObjectA
在尚未正确初始化的情况下工作,这是不可以的。
现在它可能是符合标准和安全的代码,因为ObjectB
不会尝试在其构造函数中访问传递给objectB
的引用,但是安全性被线程挂起了。如果以后有人决定只是在构造函数中进行初始化,或者访问objectB
中的任何内容,或者以其他方式更改class DB_Connect
{
private MySqlConnection connection;
private string server;
private string database;
private string uid;
private string password;
//Constructor
public DB_Connect()
{
Initialize();
}
//Initialize values
private void Initialize()
{
server = "localhost";
database = "xyz";
uid = "root";
password = "";
string connectionString = $"datasource=127.0.0.1;port = 3306; SERVER={server}; DATABASE={database}; USERNAME={uid}; PASSWORD={password};sslmode=none";
connection = new MySqlConnection(connectionString);
}
//open connection to database
public bool OpenConnection()
{
try
{
connection.Open();
return true;
}
catch (MySqlException ex)
{
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
return false;
}
}
//Close connection
public bool CloseConnection()
{
try
{
connection.Close();
return true;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
}
的使用方式,那么您最终会得到UB。
这听起来像是两个构造函数都运行后重新定位的指针的用例。
答案 1 :(得分:1)
我也不喜欢所发布的代码-它不必要地复杂且脆弱。当两个类像这样合作时,通常会有所有权的概念,因此,如果类型为A
的对象拥有类型为B
的对象(他们可能应该这样做),那么您将执行以下操作:>
#include <memory>
class A;
class B
{
public:
B (A& a) : a (a) { }
private:
A& a;
};
class A
{
public:
A () { b = std::make_unique <B> (*this); }
private:
std::unique_ptr <B> b;
};