
一位同事发来了一封邮件,其中包含一些他正在苦苦思索的代码。他试图避免使用 try/catches
来驱动业务逻辑。
问题不在于 try/catches
,它只是问题的症状。你能发现问题所在吗?你需要做一些假设,但我相信你会得出与我相同的结论。
代码如下;为了保护无辜者,我对其进行了修改:
private Customer GetOrCreateCustomer(long customerTelephoneNumberOrCustomerId)
{
Customer customer;
try
{
customer = this.DoMagic(customerMasterTelephoneNumberOrCustomerId);
}
catch (DataException)
{
try
{
//TODO: I know this isn't ideal. Still thinking of a better way to do this.
customer = this. GetCustomer(customerMasterTelephoneNumberOrCustomerId);
}
catch (DataException)
{
customer = this.GetCustomerFromExternal(customerMasterTelephoneNumberOrCustomerId);
customer.CustomerId = this.CreateCustomer(customer);
}
}
return customer;
}
这个系统中有一个潜在的哲学理念,即空值是不好的。在大多数可能产生空值的情况下,都会抛出异常。起初我没有看出这有什么问题。我将其视为一个架构决策,一种美学选择,但当我与代码交互时,我发现这显然是一个架构错误。
你可能会问,为什么在空值情况下抛出异常是不好的?
以下是考虑抛出异常时的一些指导原则:
- 你必须检查空值才能抛出异常这一事实应该提示你这是不必要的。这是一个预期的结果,因此不是异常。
- 抛出异常是一个资源密集型操作,是 .Net 中可以执行的最耗费资源的操作之一。
异常
就是异常。它是对代码中所做假设的异常——当这些假设被打破时,系统必须终止,它无法继续运行,因为系统处于未知状态(即数据库不再可用),这也可能是一个攻击向量。- 抛出异常意味着你必须将上游调用包装在
try/catch
块中以强制执行业务规则。空值是控制应用程序流程的业务机会。对空值的操作应该在必须做出业务决策的地方进行。例如,客户变量为空,在 UI 层向用户显示一条消息,说明无法找到 id 为 ‘1234’ 的客户。
作者:Chuck Conway 专注于软件工程和生成式人工智能。在社交媒体上与他联系:X (@chuckconway) 或访问他的 YouTube。