Arquitetura e Desenvolvimento de software — Parte 16 — State

Gustavo Bellini Bigardi
6 min readJul 8, 2019

Olá pessoal!! Tudo bem?

Ando sumido aqui do blog, mas no mês passado foi um pouco corrido por conta de alguns eventos, assim como neste mês de Julho, mas estou retomando as atividades aqui.

Continuando nossa série sobre Design Patterns, vamos conhecer agora o padrão State.

State

State pertence ao grupo de padrões comportamentais. Utilizamos ele quando queremos justamente que o comportamento de um objeto mude, de acordo com o seu estado atual.

Quando temos um objeto, onde além de lidarmos com a mudança de estado do mesmo ainda precisamos definir comportamentos diferentes para estados diferentes, tudo no mesmo código, tornamos nosso código mais complicado de entender e manter. Além disto, testes acabam ficam inviáveis.

Lógico que aqui fica aquele conselho de sempre: Analise se realmente seu código está ficando complexo, se realmente precisa do uso deste pattern, caso contrário, irá criar mais classes, mais complexidade se uma agregação real de valor ao seu software.

Neste caso, temos implementações diferentes do estado de nossa classe. Antes de irmos para o exemplo concreto, vamos analisar o UML que representa esse padrão, de forma bem simples:

A classe contexto no UML pode ser uma das classes de domínio de nosso software, como Carro, Livro, etc., onde temos uma interface para o estado e as implementações de comportamento para cada estado.

Vamos trabalhar com um exemplo, excluindo a questão de necessidade para fins didáticos, onde vamos trabalhar com um carro alugado. Nossa classe precisa ter comportamentos diferentes de acordo com o estado dele, sendo Disponível, Alugado ou Em Manutenção.

Para esta situação, temos nosso a classe Car, definida a seguir, com o controle de estado e comportamento ainda definido sem a aplicação do padrão State.

Podemos notar que temos nela apenas os métodos de negócio básicos, Book, Return, Maintenance, mas nada de controle do estado realmente. Para isso, enviamos ao nosso CarState o estado que queremos transicionar.

Primeiramente, precisamos criar a interface CarState, conforme a seguir:

Agora, para cada um dos três estados que podemos ter, vamos implementar uma classe para eles, como a seguir:.

Notem que para cada implementação de estado, temos tratamentos, comportamentos, diferentes para cada situação. Se o carro está disponível, não permitimos que uma devolução seja chamada, pois não faz sentido. Todo este código que poderia estar em diversos IF's por nossa classe principal (Car), agora está bem organizado e fácil de testar, pois podemos escrever testes específicos para cada estado.

Em alguns casos, podemos trabalhar com estados em formato de Singletons, já que cada chamada a uma mudança informa o domínio atual para a classe específica de estado. Outro detalhe interessante, é que a forma em que o Estado pode ser compartilhado pode ser definida através do pattern Flyweight.

Algumas vantagens de se utilizar este padrão são:

  • Com o uso do padrão State, os benefícios do polimorfismo ficam bem claros, além de ser fácil adicionar novos estados para suportar novos comportamentos.
  • No padrão State, separamos o comportamento da classe com seu controle de estado, já que o comportamento muda dependendo do estado, removemos a dependência IF's, Switch e outros condicionais que poluem nosso código e até dificultam a criação de testes com boa cobertura.
  • Com este padrão, ainda temos um aumento da coesão, já que o código específico do comportamento de acordo com o estado está separado em classes concretas para cada estado, em um determinado package ou namespace da aplicação.

Mas como em tudo que estudamos, temos seus contras também:

  • Já que separamos nossos estados em diversas classes concretas, para poder implementar de forma separada e simples os comportamentos para cada um deles, acaba sendo uma desvantagem o aumento de classes e código que geramos conforme a complexidade / número de estados diferentes aumentarem, o que aumenta o tamanho final do pacote (.jar, .dll, .exe, etc) que iremos publicar. Mas em muitos casos, esse aumento não gera um impacto grande, perto do ganho que temos na organização do código, facilitando a manutenção e evolução do mesmo.

Vamos a ficha resumo do pattern para finalizar.

Ficha Resumo

  • Nome: State;
  • Objetivo / intenção: Tornar nosso código mais organizado e coeso quando temos comportamentos diferentes baseados em estados diferentes;
  • Motivação: Quando temos diversos estados diferentes em nosso domínio e o comportamento dele varia de acordo com o estado atual, tendemos a colocar IF's e outros condicionais em nosso código, tornando ele extenso e difícil de entender / manter. A idéia do pattern e fornecer um modelo para organizarmos e spararmos o que é código relacionado a transição de estado e o que é de comportamento baseado no estado atual;
  • Aplicabilidade: Utilizamos principalmente em domínios que possuem estados diferentes onde o comportamento pode mudar de acordo com cada estado, construindo basicamente uma máquina de estados, mas mantendo uma organizção e separação de responsabilidades em classes concretas para cada estado;
  • Estrutura: No desenho abaixo, temos um exemplo UML de aplicação do pattern para o controle de estado de uma conexão TCP, como exemplo real inclusive. Podemos ver que para cada estado da conexão, temos os mesmos métodos, mas podemos já visualizar que eles terão comportamentos diferentes. Por exemplo, se estamos no estado TCPClosed, o método Close() precisa retornar uma exceção ou alguma forma de notificar que a conexão já encontra-se fechada, mas no estado TCPEstablished, ele precisa fechar a conexão realmente. Cada implementação está isolada em uma clase diferente.
  • Consequências: O State resolve problemas como evitar aquele código extenso cheio de IF's que ninguém consegue entender, assim como facilitar a manutenção, evolução e testes da aplicação, tendo apenas como contrapartida a questão de aumentar consideravelmente a quantidade de classes que precisamos criar, dado que criamos uma classe concreta para cada estado que queremos representar e controlar os comportamentos para ele.
  • Implementações: Abaixo temos um código em TypeScript, onde vamos usar como exemplo o empréstimo de livros de uma biblioteca. Todas as classes estão no mesmo arquivo apenas para facilitar a visualização, ok?

Tente executar o código acima com os comandos a seguir. É neccessário ter o compilador do TypeScript, o tsc, instalado. Você pode instalar ele com o primeiro comando, comentado / opcional caso já tenha instalado.

  • Usos conhecidos: Muito utilizado em aplicações que trabalham com máquina de estado em seus objetos de domínio, para facilitar a separação e testabilidade do código.
  • Padrões relacionados: Flyweight, Bridge, Strategy;

Concluindo

Este foi o padrão State, com o qual podemos organizar de forma bem mais clara o nosso código quando precisamos trabalhar com comportamentos diferentes baseados em estados diferentes.

Na próxima parte da série iremos abordar o parão Command.

Avisos extras

Algumas séries de artigos que escrevo que utilizam mais código, como a série sobre React, estão ficando um pouco complexas de publicar no formato de artigo devido ao tamanho de cada um, com muitos screenshots de código, etc.

Por conta disso, estou preparando a continuidade da série React, assim como outras que irei fazer e pequenas dicas do dia-a-dia em formato de vídeo no Youtube. Além de facilitar a apresentação do conteúdo, fica fácil para vocês de acompanharem e irem codificando junto do vídeo.

Ainda irei escrever artigos aqui, apenas alguns assuntos serão publicados em formato de vídeo no meu canal, ok? Para não perder nenhum vídeo novo, que irei começar a publicar a partir de Agosto (ainda aprendendo a editar os vídeos para publicar com qualidade pra vocês!), aproveitem e se inscrevam em meu canal do Youtube, e não esqueçam de clicar no sino ao lado do botão de inscrição para poder receber notificações quando eu publicar algo novo.

Inclusive pretendo gravar e publicar algumas das palestras que faço em alguns eventos durante o ano, para quem não teve chance de ir e quer assistir online.

Outro ponto interessante para quem é de Jundiaí e região, agora o JunDevelopers está diretamente no Meetup.com, para podermos divulgar e organizar melhor nossos Meetups locais. Aproveitem e já se inscrevam lá também.

Um abraço a todos e até a próxima.

--

--