异常vs特设类型.什么东西可能会掉下来

本文关键字:vs 类型 什么 异常 | 更新日期: 2023-09-27 18:10:39

我在读一本企业应用开发的书,同时在MVC 3项目上工作。我目前正在决定如何处理异常。以前,我会让异常在堆栈中冒泡,然后在最高层处理它。

该书建议在域模型中创建一个特别的类并返回它。例如

public sealed class MissingCustomer: Customer
{
}
// On method failure return new MissingCustomer();

我可以看到这个想法,但我正在努力证明需要这样做。在代码方面,我同意返回一个新的丢失的客户,而不是抛出一个异常,这要简洁得多。

您如何看待这种方法?您是否遇到过这种方法产生显著差异的情况?

如果我们假设客户必须始终存在,那么抛出一个异常说"嘿,客户应该始终存在,但由于某些原因它没有,所以我要通知用户发生了异常"是有意义的。另一方面,我可以假设客户可能会被另一个人删除,因此我需要优雅地处理这个问题。

无论哪种方式,我认为我们都需要一个MissingCustomer类或MissingCustomerException,因为customer是一个非常常见的实体,在整个系统中使用。如果视图模型期望一个客户,而我们返回一个MissingCustomer——这很好,因为继承将使它工作。

例如,我有一个返回OrderViewModel的操作方法。此操作方法需要对客户的引用。
Customer customer = CustomerRepository.Find(10);
if(customer == null)
{
    return new MissingCustomer();
}

这将会失败,因为操作方法将返回OrderViewModel类型的视图模型,所以现在我更倾向于使用异常,而不是MissingCustomer对象。

编辑

另外,MissingCustomer类型对象将继承Customer类型的属性。这些属性是不需要的,因为我们唯一想做的是通知用户无法找到客户。

谢谢

异常vs特设类型.什么东西可能会掉下来

我总是从用于获取客户的方法返回null。只看一个名为GetCustomer的函数,你永远不会期望它能返回一个不是真正的客户的客户对象。

如果可以的话,最好使用var customer = repos.GetCustomer(1) ?? Customer.Empty之类的东西。目的是明确的,代码变得更不容易出错。

如果您的方法总是期望得到一个客户,那么您就没有在早期验证输入。与其创建一个解决方案来让代码工作,不如尝试在早期修复它。也许检查一下客户是否存在?

我现在注意到这个问题真的是针对你的视图模型的。用这种逻辑是完全没问题的。视图模型毕竟是用来调整MVC中的"M"来适应视图的(因此从视图中删除逻辑)。

我会用

public class YourViewModel
{
    public Customer Customer { get { return _customer ?? Customer.Empty; }}
}

这听起来类似于Null对象模式,如果您有一些接受Customer对象的代码,并且您需要该代码能够运行并且即使没有Customer对象也不会抛出错误,则Null对象模式很有用。

例如,假设您有一个显示订单详细信息的视图,并且在某些情况下,订单可能将其Customer设置为null。在这种情况下,您不希望显示错误消息,您仍然希望显示订单的详细信息,并且可能为客户显示"None"。

您有两种选择:要么对视图进行编码以专门检查空客户并输出"None",要么使用名称为"None"的特殊MissingCustomer对象填充Order并忘记空检查。

根据具体情况,您可能会发现这种模式节省了大量"if null"代码。

另一方面,如果您打算使用MissingCustomer类进行错误检查,请按照以下行…

Customer c = ...
if (c is MissingCustomer)
{
    // Display error, no customer found
}

…这只是抛出了一个完美的错误处理机制(异常),并使用了一些更容易出错的东西。您必须始终记得检查MissingCustomer,并且在您忘记的地方,您可能会得到一些难以检测和难以调试的错误程序行为。

在我看来:

  • 如果它是一个你通常期望返回一个对象的方法,不能产生一个对象是一个错误,应该这样指出(带一个异常)。如果在某些情况下应该处理错误,请使用自定义异常类并在这些情况下捕获异常。
  • 如果这是一个方法,你可能会或可能不会返回一个对象,使用null作为"无记录"的结果,和异常的其他错误。方法的命名方式可能会提醒您可以返回null,例如"TryGetCustomer",或者甚至只是约定存储库"Find"方法可以返回null。
  • 如果你有一些代码要做很多"If null"检查,你可以考虑null对象/特殊情况模式来潜在地简化代码。

我现在有完全相同的问题,我将使用ad-hoc类型的方法。

我选择这种方法的原因是因为我想将我的"customer"实例显示为"The customer 'Name' could not be found"。在我的解决方案中,确保其他任务正常执行非常重要。没有找到这个特定的Customer实例的事实并不是一个停止问题。

因此,如果您有一些针对丢失Customer的行为,请使用ad-hoc。如果没有客户就无法继续,则抛出异常。

这就是我要做的。

看情况…异常应该只在特殊情况下抛出,例如,当你真的不知道发生了什么或应该如何处理时。

如果你有一个具体的想法,你将如何处理这个特殊的异常在你的顶层,那么我会说你不应该使用异常。

假设你的程序允许用户按名字搜索一个客户,而这个客户找不到:这不是一个异常,这是你的应用程序应该处理的问题的一部分。

在MVC中,我发现从可寻址资源的角度来考虑事物是很有用的。

如果您的用户导航到资源/YourApp/Customer/22,而客户22不存在,那么您可以执行以下操作之一:

  • 将它们路由到通用404页面(资源未找到)。
  • 给他们量身定制的回应,例如:"对不起,没有找到客户"。

考虑如何将这种情况传达给用户。

书中建议的方法并不坏,但是我可能会沿着字符串的行实现相同的想法。空属性,通过向Customer对象本身添加一个静态只读字段…

static readonly Customer Missing = new Customer();

这是一个责任问题,但是返回一个null对象确实意味着调用堆栈上的层将知道如何处理它。

  • 返回MissingCustomer类型对象还意味着您正在MissingCustomer创建内部/下面进行适当的错误记录。如果你不是,那么你需要让异常数据冒泡(返回一个异常是最好的-假设它是异常的)。
例如,我有一个返回OrderViewModel的操作方法。这Action方法需要对客户的引用。

您总是可以采用分离类型的方法:与其将视图耦合到客户,不如将其耦合到所需数据的表示(例如,"CustomerOrderSummary"对象/接口)。这是沿着接口隔离原则的路线,并且也与MVVM的某些部分保持一致:在MVVM中,您可以根据使用场景定制交换的数据。