哪种设计最为可取:test-create,try-create,create-catch?

and*_*iys 64 c#

我们假设有一个创建用户的操作.如果存在指定的电子邮件或用户名,则此操 如果失败,则需要确切知道原因.在我看来,有三种方法可以做到这一点,我想知道是否有明显的赢家.

所以,这是一个类用户:

class User
{
    public string Email { get; set; }
    public string UserName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

有三种方法可以完成创建操作:

测试创建

if (UserExists(user)) act on user exists error;
if (UsernameExists(user)) act on username exists error;
CreateUser(user);
Run Code Online (Sandbox Code Playgroud)

UserExists和UsernameExists向db服务器发出请求以进行验证.在CreateUser中再次重复这些调用以确保正确使用API​​.如果验证失败,我会在两种情况下抛出ArgumentOutOfRangeException.因此性能受到了打击.

尝试 - 创建

enum CreateUserResultCode
{
    Success,
    UserAlreadyExists,
    UsernameAlreadyExists
}

if (!TryCreate(user, out resultCode))
{
    switch(resultCode)
    {
        case UserAlreadyExists: act on user exists error;
        case UsernameAlreadyExists: act on username exists error;
    }
}
Run Code Online (Sandbox Code Playgroud)

这种模式只进行一次验证,但我们采用所谓的错误代码,这不是一种好的做法.

创建,捕捉

try
{
    CreateUser(user);
}
catch(UserExistsException)
{
    act on user exists error;
}
catch(UsernameExistsException)
{
    act on username exists error;
}
Run Code Online (Sandbox Code Playgroud)

我这里不使用错误代码,但我现在必须为每种情况创建一个单独的异常类.它或多或少应该如何使用异常,但我想知道创建一个单独的异常而不是枚举条目是值得的.

那么,我们是否有一个明确的赢家,或者更多的是品味问题?

Ree*_*sey 80

那么,我们是否有一个明确的赢家,或者更多的是品味问题?

第一个选项有一个根本的缺陷 - 如果CreateUser依赖外部资源,它永远不会是线程安全的或安全的,并且其他实现可能会在您的测试之间创建.一般来说,我倾向于避免这种"模式".

至于其他两个选项 - 它实际上取决于预计是否发生失败.如果CreateUser预计会在某种程度上失败,那么Try*模式是我的首选,因为使用异常本质上变得使用控制流的异常.

如果失败真的是一个例外情况,那么例外将更容易理解.

  • 当然,没有一个选项可以保证线程安全.最后两个只有在存在用于create-if-possible的原子操作时才能是线程安全的,该操作返回有关成功或失败原因的信息.我知道你知道这一点,但我认为值得指出,以免有人得到错误的印象. (6认同)
  • @LarsH是的 - 当然.第一个选项只是保证API本身永远不会有线程安全.其他要求API(内部)处理竞争条件的潜力...... (3认同)
  • @andriys这取决于他们是否需要调用调用堆栈.如果您需要,异常可能会更好 - 但如果这只是控制流(如您的示例代码),那么单个结果通常更简单,更清晰.我肯定会避免单独的检查,但.... (2认同)

Meh*_*dad 37

Test-Create会导致竞争条件,所以这不是一个好主意.它也可能做额外的工作.

如果您希望错误成为正常代码流的一部分(例如在用户输入的情况下),则Try-Create是好的.

如果错误确实非常特殊,那么Create-Catch很好(所以你不担心性能).

  • 我想指出,在许多情况下,例如点击数据库,异常的性能成本并不是一个大问题.但是,处理这些例外的概念成本是.如果有人去复制你的`Create-Catch`逻辑并忘记了'Catch`部分,那么就会发生奇怪的事情,但是在`Try-Create`情况下,返回值使得它变得更加明显而失败是沉默的.当然,对一切的利弊. (2认同)

rec*_*ive 9

这有点主观,但有一些具体的优点和缺点值得指出.

测试创建方法的一个缺点是竞争条件.如果两个客户端几乎同时尝试创建同一个用户,则他们可能都会通过测试,然后尝试创建相同的用户.

在Try-Create和Create-Catch之间,我更喜欢Create-Catch,但那是个人品味.有人可能会说,Create-Catch使用流控制的异常,这通常是不受欢迎的.另一方面,Try-Create需要一个有点尴尬的output参数,这可能更容易被忽视.

所以,我更喜欢Create-Catch,但这里肯定有争论的余地.