Guard clause

1 min

TLDR: Happy path by většinou neměla být zanořená do if větví.

Příklad 1

Namísto

public void ProcessOrder(Order order)
{
  if (order != null)
  {
    if (order.Items != null && order.Items.Count > 0)
    {
      if (order.CustomerId > 0)
      {
        CalculateTotals(order);
        SaveToDatabase(order);
        SendConfirmationEmail(order);
      }
      else
      {
        throw new ArgumentException("Order must have a valid customer ID", nameof(order));
      }
    }
    else
    {
      throw new ArgumentException("Order must contain at least one item", nameof(order));
    }
  }
  else
  {
    throw new ArgumentNullException(nameof(order), "Order cannot be null");
  }
}

Použijte:

public void ProcessOrder(Order order)
{
  // Guard clauses
  if (order == null)
  {
    throw new ArgumentNullException(nameof(order), "Order cannot be null");
  }
  if (order.Items == null || order.Items.Count == 0)
  {
    throw new ArgumentException("Order must contain at least one item", nameof(order));
  }

  if (order.CustomerId <= 0)
  {
    throw new ArgumentException("Order must have a valid customer ID", nameof(order));
  }

  // Happy path
  CalculateTotals(order);
  SaveToDatabase(order);
  SendConfirmationEmail(order);
}

Guard clauses

  • Guard clause kontroluje nevalidní/chybný stav na začátku metody.
  • Kód je lépe čitelný – vždy víte, kam se podívat, abyste našli hlavní logiku metody.
  • Po čase se naučíte ignorovat guard clause, pokud je zrovna nepotřebujete. To se hodí, protože vás v drtivé většině případů zajímá pouze happy path.

Příklad 2 - co když není zřejmé, co je happy path?

Následující příklad může být trochu zavádějící:

internal static decimal? GetPayAmount(Client? client)
{
  if (client == null)
  {
    return null;
  }
  
  if (client.IsRetired)
  {
    return RetiredPayAmount();
  }
  
  return NormalPayAmount();
}

Čtenář kódu si nemusí všimnout, že vrácení RetiredPayAmount je také happy path a není to chybový nebo nevalidní stav. Abychom takovému přehlédnutí předešli, můžeme na tuto skutečnost upozornit pomocí if-else(-elseif) větve:

internal static decimal? GetPayAmount(Client? client)
{
  if (client == null)
  {
    return null;
  }
  
  if (client.IsRetired)
  {
    return RetiredPayAmount();
  }
  else
  {
    return NormalPayAmount();
  }  
}

Závěr

  • Používejte guard clause – zjednoduší to kód.
  • Pokud není přesně definováno, co je happy path, poukažte na tuto skutečnost pomocí if-elseif-else větve.