我知道标题有点过于宽泛,但我想知道如何避免(如果可能的话)我刚刚在我们的解决方案上编码的这段代码。
当此代码导致日志信息不足时,问题就出现了:
...
var users = [someRemotingProxy].GetUsers([someCriteria]);
try
{
var user = users.Single();
}
catch (InvalidOperationException)
{
logger.WarnFormat("Either there are no users corresponding to the search or there are multiple users matching the same criteria.");
return;
}
...
我们在我们的模块中有一个业务逻辑,需要一个符合某些标准的“用户”。事实证明,当问题出现时,这个小的“不确定”的信息不足以让我们正确地知道发生了什么,所以我编码了这个方法:
private User GetMappedUser([searchCriteria])
{
var users = [remotingProxy]
.GetUsers([searchCriteria])
.ToList();
switch (users.Count())
{
case 0:
log.Warn("No user exists with [searchCriteria]");
return null;
case 1:
return users.Single();
default:
log.WarnFormat("{0} users [{1}] have been found"
users.Count(),
String.Join(", ", users);
return null;
}
然后从主代码中调用它:
...
var user = GetMappedUser([searchCriteria]);
if (user == null) return;
...
我看到的第一个奇怪的事情就是列表上switch
的{{1}}语句。起初这看起来很奇怪,但不知何故最终成为更清洁的解决方案。我试图在这里避免异常,因为这些条件非常正常,我听说尝试使用异常来控制程序流而不是报告实际错误是不好的。代码之前从Single抛出.Count()
,所以这更像是一个重构。
这个看似简单的问题还有另一种方法吗?它似乎是一种Single Responsibility Principle违规行为,日志介于代码和所有内容之间,但我没有看到一个体面或优雅的方式。在我们的情况下情况更糟,因为相同的步骤重复两次,一次是“用户”,然后是“设备”,如下所示:
对于这两项操作,我们必须确切知道发生了什么,返回了哪些用户/设备不是唯一的,等等。
答案 0 :(得分:1)
但是你仍然有原始问题,这就是switch语句在这里看起来很尴尬。在查看switch语句时会想到策略模式,虽然实际上我认为在这种情况下它有点过分。
如果你想探索它,可以考虑创建一个基础“UserSearchResponseHandler”类,以及三个子类:NoUsersReturned; MultipleUsersReturned;和OneUserReturned。它将有一个工厂方法接受一个用户列表并根据用户数量返回一个UserSearchResponseHandler(在工厂内部封装开关的逻辑。)每个处理程序方法都会做正确的事:记录适当的东西然后返回null ,或返回一个用户。
战略模式的主要优势在于您对其识别的数据有多种需求。如果您的代码中隐藏了切换语句,这些语句都依赖于搜索找到的用户数,那么这将是非常合适的。工厂还可以封装更复杂的规则,例如“user.count must = 1 AND user [0] .level must = 42 AND它必须是9月的星期二”。您还可以真正了解工厂并使用注册表,从而允许对逻辑进行动态更改。最后,工厂很好地将业务规则的“解释”与规则的“处理”区分开来。
但在你的情况下,可能不是那么多。我猜你可能只有这一规则的一次出现,它看起来很静态,而且它已经恰当地位于你获得它正在验证的信息的位置附近。虽然我仍然建议从响应解析器中拆分搜索,但我可能只是使用切换。
考虑它的另一种方式是使用一些Goldilocks测试。如果这确实是一个错误的情况,你甚至可以抛出:
if (users.count() < 1)
{
throw TooFewUsersReturnedError;
}
if (users.count() > 1)
{
throw TooManyUsersReturnedError;
}
return users[0]; // just right
答案 1 :(得分:0)
这样的事情怎么样?
public class UserResult
{
public string Warning { get; set; }
public IEnumerable<User> Result { get; set; }
}
public UserResult GetMappedUsers(/* params */) { }
public void Whatever()
{
var users = GetMappedUsers(/* params */);
if (!String.IsNullOrEmpty(users.Warning))
log.Warn(users.Warning);
}
如果需要,请切换List<string> Warnings
。这会将您的GetMappedUsers
方法更像是一个返回一些数据和一些结果元数据的服务,它允许您将日志记录委派给调用者 - 它所属的位置 - 因此您的数据访问代码可以继续执行它的工作。
虽然,老实说,在这种情况下,我更希望只返回GetMappedUsers
的用户ID列表,然后使用users.Count
来评估调用方中的“案例”并根据需要进行记录