例外处理:合同与例外方法
我知道两种处理异常的方法,让我们来看看它们。
- 合同方式。当某个方法不执行其在方法标头中将要执行的操作时,它将引发异常。因此,该方法"承诺"它将执行该操作,并且如果由于某种原因而失败,则将引发异常。
- 出色的方法。仅当发生真正奇怪的事情时才抛出异常。当我们可以使用常规控制流(If语句)解决问题时,不应使用异常。我们不会像在合同方法中那样将异常用于控制流。
让我们在不同情况下使用两种方法:
我们有一个Customer类,该类具有一种称为OrderProduct的方法。
合同方式:
class Customer { public void OrderProduct(Product product) { if((m_credit - product.Price) < 0) throw new NoCreditException("Not enough credit!"); // do stuff } }
特殊方法:
class Customer { public bool OrderProduct(Product product) { if((m_credit - product.Price) < 0) return false; // do stuff return true; } } if !(customer.OrderProduct(product)) Console.WriteLine("Not enough credit!"); else // go on with your life
在这里,我更喜欢采用特殊的方法,因为假设客户没有赢得彩票就没有钱,这并不是真正的例外。
但是,我在合同风格上存在这种情况。
极好:
class CarController { // returns null if car creation failed. public Car CreateCar(string model) { // something went wrong, wrong model return null; } }
当我调用一个名为CreateCar的方法时,我该期望一个Car实例而不是一些糟糕的null指针,后者可能会在十行之后破坏我正在运行的代码。因此,我更喜欢合同而不是合同:
class CarController { public Car CreateCar(string model) { // something went wrong, wrong model throw new CarModelNotKnownException("Model unkown"); return new Car(); } }
我们使用哪种风格?我们认为对异常的最佳通用方法是什么?
解决方案
回答
我相信,如果我们要构建一个将由外部程序使用(或者将由其他程序重用)的类,则应使用合同方法。一个很好的例子就是任何一种API。
回答
我通常的方法是使用合同来处理由于"客户端"调用(即由于外部错误)引起的任何类型的错误(即ArgumentNullException)。
参数上的每个错误都不会被处理。引发异常,由"客户端"负责处理。另一方面,对于内部错误,请始终尝试更正它们(就像由于某种原因而无法获得数据库连接一样),并且只有在我们无法处理它的情况下,才引发异常。
重要的是要记住,客户端始终无法处理此类级别的大多数未处理的异常,因此它们很可能会由最通用的异常处理程序处理,因此,如果发生此类异常,我们无论如何都可能是FUBAR。
回答
我赞成我们所说的"合同"方法。在支持异常的语言中,无需返回null或者其他特殊值来指示错误。我发现在没有很多" if(result == NULL)"或者" if(result == -1)"子句以及很简单,直接的逻辑的情况下,理解代码要容易得多。
回答
如果我们实际上对异常感兴趣,并想考虑如何使用它们来构建健壮的系统,请考虑阅读存在软件错误时制作可靠的分布式系统。