Entendendo o Padrão State
O padrão State é um dos 23 padrões de design descritos no clássico livro “Design Patterns: Elements of Reusable Object-Oriented Software”, escrito por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, também conhecido como “Gang of Four” (GoF). Este padrão visa permitir que um objeto altere seu comportamento quando seu estado interno muda. Isso é alcançado através da separação dos estados em classes separadas e permitindo que o objeto delegue o comportamento correspondente ao seu estado atual.
A essência fundamental do padrão State reside na concepção de que um programa pode existir em um número finito de estados distintos. Dentro de cada estado específico, o comportamento do programa é definido de forma distinta, e a transição de um estado para outro pode ocorrer instantaneamente. Contudo, tais transições são governadas por regras predeterminadas e finitas, conhecidas como transições. Dependendo do estado atual do programa, algumas transições podem ser permitidas, enquanto outras podem ser proibidas. Este conceito de transição entre estados define a lógica do comportamento dinâmico do programa, permitindo uma adaptação flexível às diferentes condições e requisitos em tempo de execução.
Uso de condicionais
Comumente, as máquinas de estado são construídas com várias instruções condicionais (como if ou switch) que determinam o comportamento apropriado com base no estado atual do objeto. Na prática, esse estado é geralmente representado por um conjunto de valores dos campos ou propriedades do objeto. Mesmo sem estar familiarizado com o termo “máquinas de estado finito”, é provável que você já tenha implementado um sistema que funcione dessa maneira em algum momento.
Mas qual o problema de usá-las?
A maior fraqueza de uma máquina de estado baseada em condicionais se torna evidente quando o sistema evolui e mais estados e comportamentos dependentes do estado são adicionados à classe do documento. Com o aumento da complexidade, os métodos tornam-se repletos de condicionais extensos que selecionam o comportamento apropriado com base no estado atual. Esse tipo de código se torna difícil de manter, pois qualquer modificação na lógica de transição requer alterações em todos os condicionais de estado em diversos métodos.
À medida que o projeto progride, o problema se agrava. É desafiador prever todos os estados e transições possíveis durante a fase de design inicial. Portanto, uma máquina de estado que inicialmente era concisa e organizada, construída com um conjunto limitado de condicionais, pode se transformar em uma estrutura inchada e confusa ao longo do tempo.
O uso do State
O padrão State propõe a criação de novas classes para cada estado possível de um objeto, onde cada classe encapsula os comportamentos específicos desse estado. Em vez de implementar diretamente todos os comportamentos no objeto original, conhecido como contexto, este armazena uma referência a um dos objetos de estado que representa o estado atual e delega todas as operações relacionadas ao estado a esse objeto.
Para transicionar o contexto para outro estado, substituímos o objeto de estado ativo por um novo objeto que represente o estado desejado. Isso só é viável se todas as classes de estado seguirem a mesma interface, e o contexto interagir com esses objetos apenas por meio dessa interface padronizada. Essa abordagem promove uma estrutura mais modular e flexível, onde cada estado é tratado como uma unidade independente, facilitando a manutenção e a evolução do sistema ao longo do tempo.
Componentes do Padrão State
Context (Contexto): É a classe que possui um estado interno variável. O contexto mantém uma referência para uma instância de um estado concreto e encaminha todas as solicitações para o estado atual.
State (Estado): É uma interface que define os métodos que encapsulam o comportamento associado a um estado particular do contexto. Cada estado concreto implementa essa interface.
ConcreteState (Estado Concreto): São as classes que implementam a interface State. Cada classe representa um estado específico do contexto e define o comportamento correspondente.
Benefícios do Padrão State
Flexibilidade: O padrão State permite que objetos alterem seu comportamento dinamicamente, sem alterar sua estrutura interna. Isso torna o código mais flexível e adaptável a mudanças nos requisitos.
Organização: Ao dividir o comportamento em classes de estado separadas, o padrão promove uma melhor organização do código. Cada estado é encapsulado em sua própria classe, tornando o código mais claro e fácil de entender.
Manutenção: Como o comportamento relacionado a cada estado é isolado em classes separadas, a manutenção do código se torna mais fácil. Alterações no comportamento de um estado não afetam diretamente os outros estados ou o contexto.
Exemplo de aplicação:
Interface Estado (State):
- A interface Estado define os métodos que representam as ações que podem ser executadas em cada estado da máquina de venda automática, como inserir moeda, selecionar produto e dispensar produto.
Classes de Estado Concreto:
- As classes EstadoSemMoeda e EstadoComMoeda são exemplos de estados concretos da máquina. Cada uma implementa a interface Estado e define o comportamento correspondente ao estado em que a máquina se encontra. Por exemplo, o EstadoSemMoeda permite apenas a inserção de moeda, enquanto o EstadoComMoeda permite a seleção e a dispensa do produto.
Classe Contexto (MaquinaVenda):
- A classe MaquinaVenda é o contexto da máquina de venda automática, que mantém uma referência para o estado atual. Ela possui métodos para executar as ações disponíveis na máquina, como inserirMoeda, selecionarProduto e dispensarProduto.
- As transições de estado são realizadas através do método setState na classe MaquinaVenda. Quando uma moeda é inserida no estado SemMoeda, a máquina transita para o estado ComMoeda, e vice-versa quando um produto é dispensado.
Exemplo de Uso:
- No exemplo de uso na classe Principal, criamos uma instância da MaquinaVenda e realizamos uma sequência de ações, como selecionar um produto após inserir uma moeda. Cada ação gera uma saída correspondente, demonstrando o comportamento da máquina de acordo com seu estado atual.
Dessa forma, o padrão State permite que o comportamento da máquina de venda automática seja representado de forma modular e flexível, com cada estado encapsulando seu próprio conjunto de comportamentos e ações. Isso torna o código mais fácil de entender, manter e expandir à medida que novos estados e funcionalidades são adicionados.
Fontes:
https://refactoring.guru/design-patterns/state