一位同事发来了一封电子邮件,其中包含他正在努力解决的一些代码。他试图避免使用 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 是一位 AI 工程师,拥有近 30 年的软件工程经验。他构建实用的 AI 系统——内容管道、基础设施代理和解决实际问题的工具——并分享他沿途的学习成果。在社交媒体上与他联系:X (@chuckconway) 或访问他的 YouTube 和 SubStack。