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:
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.
é a classe concreta que fornece uma implementação específica daConcreteState
IState
interface. Cada
classe representa o comportamento diferente do objeto.ConcreteState
Quando o estado do contexto muda, ele delega o comportamento ao
objeto atual. A ConcreteState
classe implementa o comportamento associado ao estado. Permite que o objeto mude seu comportamento dinamicamente sem alterar sua interface.ConcreteState
Exemplo de padrão de estado C#
Suponha que você precise gerenciar faturas com vários estados: não pagas, pagas, canceladas e reembolsadas:
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 IInvoiceState
interface que possui três métodos Pay
, Cancel
e 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 Invoice
possui 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:Amount
Description
State
Invoice
State
UnpaidState
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 UnpaidState
classe que implementa a IInvoiceState
interface:
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 PaidState
classe que também implementa a IInvoiceState
interface:
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 CanceledState
classe que implementa a IInvoiceState
interface:
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.