Guard Clauses 设计模式

1. Guard Clauses

当业务增长导致代码变得臃肿且复杂时,函数作为最小的可执行单元,其易读性变得尤为重要。函数的清晰性可以通过良好的命名和结构来实现,而Guard Clauses设计模式则是简化复杂性的一种有效手段。通过使用Guard Clauses,我们可以在函数开始处进行预判并立即返回或抛出异常,以确保函数仅在有效条件下执行,避免深度嵌套的if和switch语句。进一步的优化是通过反转流程,即编写主逻辑而将错误情况作为else语句处理,使得代码更加简洁、清晰,减少了深层嵌套和沉长的可能性。

如下是一个简单的函数没有使用Guard Clauses

public void Subscribe(User user, Subscription subscription, Term term)
{
    if (user != null)
    {
        if (subscription != null)
        {
            if (term == Term.Annually)
            {
                // subscribe annually
            }
            else if (term == Term.Monthly)
            {
                // subscribe monthly
            }
            else
            {
                throw new InvalidEnumArgumentException(nameof(term));
            }
        }
        else
        {
            throw new ArgumentNullException(nameof(subscription));
        }
    }
    else
    {
        throw new ArgumentNullException(nameof(user));
    }
}

我们可以反转一下if语句中的逻辑并将抛出异常的语句放到if中,从而来消除else语句

public void Subscribe2(User user, Subscription subscription, Term term)
{
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user));
    }
    if (subscription == null)
    {
        throw new ArgumentNullException(nameof(subscription));
    }
    if (term == Term.Annually)
    {
        // subscribe annually
    }
    else if (term == Term.Monthly)
    {
        // subscribe monthly
    }
    else
    {
        throw new InvalidEnumArgumentException(nameof(term));
    }
}

对null和抛出特定类型异常的常见行为的检查显然违反了DRY原则。可以将此代码提取到一个Helper方法中:

public static class Guard
{
    public static void AgainstNull(object argument, string argumentName)
    {
        if (argument == null)
        {
            throw new ArgumentNullException(argumentName);
        }
    }
    public static void AgainstInvalidTerms(Term term, string argumentName)
    {
        // note: currently there are only two enum options
        if (term != Term.Annually &&
            term != Term.Monthly)
        {
            throw new InvalidEnumArgumentException(argumentName);
        }
    }
}

现在可以调用这些帮助方法,设置不需要在函数中包含任何if语句,因为如果发生异常,它将从原始函数退出。现在你的代码将变成如下:

public void Subscribe3(User user, Subscription subscription, Term term)
{
    Guard.AgainstNull(user, nameof(user));
    Guard.AgainstNull(subscription, nameof(subscription));
    Guard.AgainstInvalidTerms(term, nameof(term));
    if (term == Term.Annually)
    {
        // subscribe annually
        return;
    }
    // subscribe monthly
}

随着项目的不断演进,您可以继续添加额外的辅助方法覆盖别的应用场景,例如空字符串、负数、无效枚举值等。

2. Ardalis.GuardClauses 

Ardalis.GuardClauses是一个第三方的包,用来实现扩展Guard Clauses

2.1 支持如下Guard Clauses

  1. Guard.Against.Null

    描述:如果输入为空抛出异常

  2. Guard.Against.NullOrEmpty

    描述:输入字符串,guid或者array为null或者empty抛出异常

  3. Guard.Against.NullOrWhiteSpace

    描述:输入的字符串为null,empty或者whitespace抛出异常

  4. Guard.Against.OutOfRange

    描述:如果 integer/DateTime/enum 超出提供的范围抛出异常

  5. Guard.Against.EnumOutOfRange

    描述:如果提供的枚举值超出了定义枚举值的范围则抛出异常

  6. Guard.Against.OutOfSQLDateRange

    描述:输入的日期类型超出SQL Server Datetime 范围则抛出异常

  7. Guard.Against.Zero

    描述:输入数字为0则抛出异常

  8. Guard.Against.Expression

    描述:自定义表达式

  9. Guard.Against.InvalidFormat

    描述:使用正则表达式或者函数自定义格式

10.Guard.Against.NotFound 

   描述:使用id/key查找,如果没有发现对象,抛出异常

2.2 拓展自己的Guard Clauses

你可以通过如下代码来扩展你自己的Guard:

// Using the same namespace will make sure your code picks up your 
// extensions no matter where they are in your codebase.
namespace Ardalis.GuardClauses
{
    public static class FooGuard
    {
        public static void Foo(this IGuardClause guardClause,
            string input, 
            [CallerArgumentExpression("input")] string? parameterName = null)
        {
            if (input?.ToLower() == "foo")
                throw new ArgumentException("Should not have been foo!", parameterName);
        }
    }
}
// Usage
public void SomeMethod(string something)
{
    Guard.Against.Foo(something);
    Guard.Against.Foo(something, nameof(something)); // optional - provide parameter name
}

github: 

https://github.com/bingbing-gui/dotnet-guide/blob/main/DesignPatterns/GuardClause%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md

参考文章:

1.https://deviq.com/design-patterns/guard-clause

2.https://github.com/ardalis/GuardClauses

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值