使用 属性 getter 进行初始化(以避免必须按特定顺序调用方法)是不好的做法吗?
Is using property getters for initialization (to avoid having to call methods in specific order) bad practice?
假设我有一个 class 为我的应用程序提供一些数据。数据最初来自数据库,我通过一些处理整个数据库事物的方法提供它,并将结果呈现为可用的 class 而不是原始查询结果。 class 必须进行一些设置(并不复杂)以确保调用的任何方法都可以使用数据库(例如连接到数据库,确保它包含一些关键信息等)。所以,如果我把它放在一个方法中(比如,方法 Init(),它将处理数据库检查、连接到它、验证它确实包含信息),我必须确保在之前调用这个方法任何其他方法。
所以,我通常会发现,而不是这样做:
public class DataProvider
{
private SqlController controller;
public void Init()
{
controller = new SqlController();
controller.Init();
controller.ConnectToDataBase();
CheckForCriticalInfoInDatabase();
}
public Data GetData()
{
// get data from database (not actually going to use raw queries like that, just an example)
var queryResult = sqlController.RunQuery("SELECT something FROM SOME_TABLE");
// and present it as usable class
Data usefulData = QueryResultToUsefulData(queryResult);
return usefulData;
}
...
}
然后总是确保我在 GetData() 之前调用 Init(),我会做类似
的事情
private SqlController _controller;
private SqlController controller
{
get
{
if (_controller == null)
{
_controller = new SqlController();
_controller.Init();
_controller.ConnectToDataBase();
CheckForCriticalInfoInDatabase();
}
return controller;
}
}
所以,现在我可以确定我不会使用未初始化的 SqlController,并且我不必在使用它的每个方法中都执行相同的空值检查。但是,我从来没有注意到在其他人的代码中以这种方式使用吸气剂。
有什么我没发现的陷阱吗?对我来说,它看起来与惰性初始化相同,不同的是我使用它不是因为初始化很重或很长,而是因为我不想检查我调用方法的顺序。 This question 指出它不是线程安全的(在我的情况下不是问题,而且我想它可以通过一些锁使其成为线程安全的)并且将 属性 设置为 null 会导致不直观行为(不用担心,因为我根本没有 setter 并且不应该以任何方式触及支持字段)。
此外,如果这种代码符合惯例,那么确保我的方法不依赖于它们被调用的顺序的正确方法是什么?
正如@madreflection 在 OP 评论中所说,对任何可能变慢的事情都使用一种方法。 getter 和 setter 应该只是获取和设置值的快速方法。
与 dbs 的连接可能很慢或无法连接,因此您可能设置了 catch 以尝试不同的连接方法等。
您还可以在对象的构造函数中进行检查,这样如果 init() 不在 运行 不同的函数中,就无法使用对象,从而节省了跟踪错误实际位置的时间正在发生。
例如,如果您有一个函数创建对象,执行一堆 'stuff' 然后尝试在没有 运行ning init() 的情况下使用该对象,那么在所有'stuff' 不是你创建对象的地方。这可能会让您认为您使用该对象的任何方式都有问题,而不是它尚未初始化。
假设我有一个 class 为我的应用程序提供一些数据。数据最初来自数据库,我通过一些处理整个数据库事物的方法提供它,并将结果呈现为可用的 class 而不是原始查询结果。 class 必须进行一些设置(并不复杂)以确保调用的任何方法都可以使用数据库(例如连接到数据库,确保它包含一些关键信息等)。所以,如果我把它放在一个方法中(比如,方法 Init(),它将处理数据库检查、连接到它、验证它确实包含信息),我必须确保在之前调用这个方法任何其他方法。 所以,我通常会发现,而不是这样做:
public class DataProvider
{
private SqlController controller;
public void Init()
{
controller = new SqlController();
controller.Init();
controller.ConnectToDataBase();
CheckForCriticalInfoInDatabase();
}
public Data GetData()
{
// get data from database (not actually going to use raw queries like that, just an example)
var queryResult = sqlController.RunQuery("SELECT something FROM SOME_TABLE");
// and present it as usable class
Data usefulData = QueryResultToUsefulData(queryResult);
return usefulData;
}
...
}
然后总是确保我在 GetData() 之前调用 Init(),我会做类似
的事情 private SqlController _controller;
private SqlController controller
{
get
{
if (_controller == null)
{
_controller = new SqlController();
_controller.Init();
_controller.ConnectToDataBase();
CheckForCriticalInfoInDatabase();
}
return controller;
}
}
所以,现在我可以确定我不会使用未初始化的 SqlController,并且我不必在使用它的每个方法中都执行相同的空值检查。但是,我从来没有注意到在其他人的代码中以这种方式使用吸气剂。
有什么我没发现的陷阱吗?对我来说,它看起来与惰性初始化相同,不同的是我使用它不是因为初始化很重或很长,而是因为我不想检查我调用方法的顺序。 This question 指出它不是线程安全的(在我的情况下不是问题,而且我想它可以通过一些锁使其成为线程安全的)并且将 属性 设置为 null 会导致不直观行为(不用担心,因为我根本没有 setter 并且不应该以任何方式触及支持字段)。
此外,如果这种代码符合惯例,那么确保我的方法不依赖于它们被调用的顺序的正确方法是什么?
正如@madreflection 在 OP 评论中所说,对任何可能变慢的事情都使用一种方法。 getter 和 setter 应该只是获取和设置值的快速方法。
与 dbs 的连接可能很慢或无法连接,因此您可能设置了 catch 以尝试不同的连接方法等。
您还可以在对象的构造函数中进行检查,这样如果 init() 不在 运行 不同的函数中,就无法使用对象,从而节省了跟踪错误实际位置的时间正在发生。
例如,如果您有一个函数创建对象,执行一堆 'stuff' 然后尝试在没有 运行ning init() 的情况下使用该对象,那么在所有'stuff' 不是你创建对象的地方。这可能会让您认为您使用该对象的任何方式都有问题,而不是它尚未初始化。