Padrão de estado C#

Resumo : neste tutorial, você aprenderá como usar o padrão C# State para permitir que um objeto altere seu comportamento quando seu estado interno mudar.

Introdução ao padrão C# State

O padrão State é um padrão de design comportamental que permite que um objeto mude seu comportamento quando seu estado interno muda.

O padrão State é útil quando o comportamento de um objeto depende de seu estado e precisa mudar dinamicamente quando seu estado muda.

O diagrama UML a seguir ilustra o padrão State:

Padrão de estado C#

O padrão State normalmente consiste nos seguintes participantes:

  • Contexté um objeto que mantém o estado atual e fornece uma interface para os clientes interagirem com o objeto.
  • IStateé uma interface que define métodos que os estados concretos devem implementar.
  • ConcreteStateé a classe concreta que fornece uma implementação específica da IStateinterface. Cada ConcreteStateclasse representa o comportamento diferente do objeto.

Quando o estado do contexto muda, ele delega o comportamento ao ConcreteStateobjeto atual. A ConcreteStateclasse implementa o comportamento associado ao estado. Permite que o objeto mude seu comportamento dinamicamente sem alterar sua interface.

Exemplo de padrão de estado C#

Suponha que você precise gerenciar faturas com vários estados: não pagas, pagas, canceladas e reembolsadas:

Exemplo de padrão de estado C# - Estados da fatura

O programa a seguir ilustra como usar o padrão State para gerenciar o comportamento de faturas que mudam dinamicamente com base em seus estados:

namespace StatePattern;

public interface IInvoiceState
{
    void Pay(Invoice invoice);
    void Cancel(Invoice invoice);
    void Refund(Invoice invoice);
}

public class Invoice
{
    public int Number
    {
        get;
    }
    public decimal Amount
    {
        get;
    }
    public string Description
    {
        get;
    }
    public IInvoiceState State
    {
        get; set;
    }

    public Invoice(int number, decimal amount, string description)
    {
        Number = number;
        Amount = amount;
        Description = description;

        // Set the invoice as Unpaid
        State = new UnpaidState();
    }


    public void Pay()
    {
        State.Pay(this);
        State = new PaidState();
    }

    public void Cancel()
    {
        State.Cancel(this);
        State = new CanceledState();
    }

    public void Refund()
    {
        State.Refund(this);
        State = new RefundedState();
    }
}


public class UnpaidState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Paying invoice {invoice.Number}...");

    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Canceling invoice {invoice.Number}...");

    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} is unpaid and cannot be refunded.");

    }
}

public class PaidState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has already been paid.");
    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} cannot be cancelled.");
    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been refunded.");
    }
}

public class CanceledState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been canceled and cannot be paid.");
    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has already been canceled and cannot be canceled again.");
    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been canceled and cannot be refunded.");
    }
}

public class RefundedState : IInvoiceState
{
    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} was refunded and cannot be cancelled.");

    }
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} was refunded and cannot be paid.");

    }
    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} cannot be refunded again.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var invoice = new Invoice(123, 1000m, "Software Dev Services");

        invoice.Pay();
        invoice.Refund();
    }
}Linguagem de código:  C#  ( cs )

Saída:

Paying invoice 123...
Invoice 123 has been refunded.Linguagem de código:  C#  ( cs )

Como funciona.

Primeiro, defina a IInvoiceStateinterface que possui três métodos Pay, Cancele Refund:

public interface IInvoiceState
{
    void Pay(Invoice invoice);
    void Cancel(Invoice invoice);
    void Refund(Invoice invoice);
}Linguagem de código:  C#  ( cs )

Segundo, defina a classe que Invoicepossui quatro propriedades Number,,, e . Observe que isso é para fins de simplicidade. Na aplicação real, uma fatura pode conter mais informações. O construtor da classe inicializa a propriedade do objeto:AmountDescriptionStateInvoiceStateUnpaidState

public class Invoice
{
    public int Number
    {
        get;
    }
    public decimal Amount
    {
        get;
    }
    public string Description
    {
        get;
    }
    public IInvoiceState State
    {
        get; set;
    }

    public Invoice(int number, decimal amount, string description)
    {
        Number = number;
        Amount = amount;
        Description = description;

        // Set the invoice as Unpaid
        State = new UnpaidState();
    }


    public void Pay()
    {
        State.Pay(this);
        State = new PaidState();
    }

    public void Cancel()
    {
        State.Cancel(this);
        State = new CanceledState();
    }

    public void Refund()
    {
        State.Refund(this);
        State = new RefundedState();
    }
}Linguagem de código:  C#  ( cs )

Terceiro, defina a UnpaidStateclasse que implementa a IInvoiceStateinterface:

public class UnpaidState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Paying invoice {invoice.Number}...");

    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Canceling invoice {invoice.Number}...");

    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} is unpaid and cannot be refunded.");

    }
}Linguagem de código:  C#  ( cs )

Uma fatura não paga pode ser paga ou cancelada, mas não pode ser reembolsada.

Quarto, defina a PaidStateclasse que também implementa a IInvoiceStateinterface:

public class PaidState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has already been paid.");
    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} cannot be cancelled.");
    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been refunded.");
    }
}Linguagem de código:  C#  ( cs )

Uma fatura paga não pode ser paga novamente ou cancelada, mas pode ser reembolsada.

Quinto, defina a CanceledStateclasse que implementa a IInvoiceStateinterface:

public class CanceledState : IInvoiceState
{
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been canceled and cannot be paid.");
    }

    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has already been canceled and cannot be canceled again.");
    }

    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} has been canceled and cannot be refunded.");
    }
}Linguagem de código:  C#  ( cs )

Uma fatura cancelada não pode ser cancelada, reembolsada ou paga.

Da mesma forma, uma fatura reembolsada não pode ser cancelada, reembolsada ou paga:

public class RefundedState : IInvoiceState
{
    public void Cancel(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} was refunded and cannot be cancelled.");

    }
    public void Pay(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} was refunded and cannot be paid.");

    }
    public void Refund(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number} cannot be refunded again.");
    }
}Linguagem de código:  C#  ( cs )

Por fim, crie um objeto fatura e chame o método Pay()and Refund()sequencialmente:

public class Program
{
    public static void Main(string[] args)
    {
        var invoice = new Invoice(123, 1000m, "Software Dev Services");

        invoice.Pay();
        invoice.Refund();
    }
}Linguagem de código:  C#  ( cs )

Resumo

  • Use o padrão State para permitir que um objeto mude seu comportamento quando seu estado interno mudar.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *