• Nenhum resultado encontrado

Padrão de Projeto State

N/A
N/A
Protected

Academic year: 2021

Share "Padrão de Projeto State"

Copied!
5
0
0

Texto

(1)

padrão state_

MÁQUINAS DE ESTADO COM O

Padrão de

Projeto State

D

esenvolver software é trabalhar com abstrações o

tempo todo, pois estas nos ajudam a gerenciar a complexidade nos sistemas que desenvolvemos. Uma das abstrações mais poderosas que podemos empre-gar são as máquinas de estado. Com elas, é possível !"#$%&' "()#&*%*' +!(*%*,' "#*"#' *( -$#*' .!&/0!.*' até complexos autômatos para uso em compiladores #' %-$(+%12#*' "#' (34#$(563+(%' %&4(7+(%$8' 9' !"#$!' "#' máquina de estados é extremamente interessante por ser de fácil construção, sucinto e altamente co-municativo, facilitando bastante a comunicação com os usuários do sistema e especialistas de domínio.

No entanto, o que acontece é que muitos utili-zam a modelagem de máquinas de estados apenas em nível conceitual, perdendo-o de vista no momen-to da implementação. Muimomen-tos até fazem a modela-gem em máquina de estados de uma maneira mais formal usando algo como UML1, mas apenas para propósitos de documentação de requisitos e comu-3(+%1:!8';!' ! #34!'"%'+!"(7+%1:!,'%' <=>(3%'"#' estados é convertida em diversos ifs, que se espa-lham por variadas partes do sistema, aumentando as lacunas sintática e semântica entre o modelo de domínio “discutido pelas pessoas” e o modelo de "! ?3(!' @+!"(7+%"!A8' B#&"#C*#' %' !-!&4>3("%"#' "#' representar um conceito da linguagem do domínio explicitamente em código, como advogado pelo Do-main-driven Design2. Mas não precisa ser assim! 1. Máquinas de estados são um recurso de modelagem também previsto !"#$%&#$'()*("(&#$(#+ ',-$#.%$-/' )#0( )!()-#1+.023

2. Para mais detalhes sobre Domain-driven Design, recomendo fortemente

Neste artigo, mostraremos como implementar máquinas de estado por meio do padrão de projeto State. Primeiramente, faremos um resumo do padrão e, em seguida, ilustraremos seu uso por meio de um exemplo prático associado com o uso da Java Persis-tence API. Ressaltamos que este artigo assume que o leitor já possui um conhecimento básico do funcio-3% #34!'"#'D3> *'#'"#'EBFGH(I#&funcio-3%4#'J-%&%'!*'=>#' desejam mais informações, ver seção de referências).

O padrão State

O padrão State é um dos 23 padrões de proje-to da Gang of Four (GoF), já velho conhecido da co-munidade (o livro foi publicado em 1994). Apesar de alguns desses padrões hoje serem até meio contro-versos (como é o padrão Singleton, por exemplo), o padrão State (juntamente com seu irmão gêmeo Strategy) continua sendo de elevada aplicabilidade nos sistemas desenvolvidos atualmente.

A ideia do State é representar os estados de uma máquina de estados por meio de classes que imple-mentam uma interface comum, a qual contém as re-gras de transição. Cada classe implementa, então, a ação devida quando a transição é acionada por essa interface. O outro elemento do padrão é o chamado “contexto”, que é a classe da qual o estado faz parte e também é a classe com a qual o restante do sistema interage. Quando uma regra de negócio é acionada

3!'+!34#K4!'#'#*4#')#&(7+%'=>#'L'3#+#**<&(%'%'&#%$(M%-a leitur3!'+!34#K4!'#'#*4#')#&(7+%'=>#'L'3#+#**<&(%'%'&#%$(M%-a do livro homônimo de Eric Ev3!'+!34#K4!'#'#*4#')#&(7+%'=>#'L'3#+#**<&(%'%'&#%$(M%-ans.

cinto de utilidades

(2)

Alexandre Gazola | alexandregazola@gmail.com | @alexandregazola

Bacharel em Ciência da Computação pela Universidade Federal de Viçosa (UFV) e mestre em Informática pela PUC-Rio. Traba-/4(#5%"%#6 (/'&7(#$-#8'&7-"(&# %#9:;<8=#$-&- >%/>-#-"#?(>(#$-&$-#@AAB#-#C%&&!'#(&#5-*7',5(DE-&#8F?G#-#8FHF;3#I#(*7'5!/'&7(# e editor-técnico da revista MundoJ, além de manter um blog em http://alexandregazola.wordpress.com. Bacharel em Ciências da F%"C!7(DJ%##C-/(#+ -&C=#K%'#' &7*!7%*#%,5'(/#$(#8! #.'5*%&L&7-"&#-#$(#M*(5/-#<$!5(7'% 3#67!(/"- 7-#5% 7*'N!'#C(*(#(/)! &#C*%O-7%&# open source, como KDE e Mentawai, e é da equipe de arquitetura da CodeIT Solutions, uma empresa especializada na prestação de serviços de desenvolvimento de software para as indústrias de seguros.

Máquinas de estados são uma construção extremamente comum

para se modelar requisitos de diversos sistemas. Normalmente, são

fáceis de desenhar (em forma de modelos) e interessantes

instru-mentos para comunicação e validação com os usuários por serem de

simples entendimento. No entanto, muitos desenvolvedores perdem

(# ' 7-)*'$($-# $-&&(# (N&7*(DJ%# %# "%"- 7%# $(# 5%$',5(DJ%3# :-&7-#

artigo, abordamos o padrão de projeto State, bastante útil para

mo-delagem de máquinas de estado em código. Como sempre, damos

uma ênfase mais prática e direta, mostrando uma opção de

imple-mentação do padrão em Java usando Enums e a Java Persistence

API.

ção de uma transição de estado, o contexto dispara #34:!'%'4&%3*(1:!'3!'#*4%"!,'!'=>%$'7+%'#3+%&&#5%-do de realizar a ação correspondente e determinar !'-&NK( !'#*4%"!8'O'( -!&4%34#'=>#'!'+!34#K4!'*#P%' passado como parâmetro ao estado para que o esta-do possa decidir o estaesta-do seguinte, setanesta-do um obje-4!'"%'+$%**#'%"#=>%"%'3!'+!34#K4!8'F'75>&%'Q'#K(I#'%' estrutura geral do padrão State.

Vamos à seção seguinte, na qual utilizaremos um exemplo concreto para o qual aplicaremos o padrão.

Aplicando o padrão State na prática

Como ilustração, imaginemos uma aplicação -%&%'+!34&!$%&'!'.!&/0!.'"#'*>I (**:!,'%-&!)%1:!' e publicação de artigos para um periódico qualquer. Neste exemplo, estamos interessados em modelar os estados e transições de um artigo. Após discussão

com os usuários, chegamos ao diagrama de estados #K(I("!'3%'75>&%'R8'

O modelo apresentado é bastante autoexplica-tivo. Os estados são representados pelos retângu-los e as transições por setas. Tudo começa quando um artigo é submetido pelo sistema. Quando isso acontece, ele vai para o estado “Recebido”. A partir desse estado, o sistema realiza algumas validações automáticas no artigo, como, por exemplo, ortogra-7%,'S!& %4!*'#4+8'9'*>+#**!'!>'3:!'"#**%')%$("%1:!' automática determina se o artigo irá para o estado “Aceito para revisão” ou “Recusado”. No estado “Re-cusado”, o usuário pode fazer alterações à vontade e realizar uma nova submissão. Uma vez que um artigo esteja “Aceito para revisão”, um humano fará a lei-tura e revisão do artigo, determinando se o mesmo está satisfatório, se apresenta erros básicos ou se, simplesmente, foi recusado. Cada um desses eventos faz com que o artigo vá, respectivamente, para os estados “Aceito para publicação”, “Aceito com correções” e “Recusado”.

Implementação tradicional

do padrão

Agora, vamos ao que interessa: o código! A Listagem 1 apresenta a in-terface para os estados do nosso mo-delo, contendo as possíveis transições da máquina. Na Listagem 2 trazemos

Figura 1. Estrutura geral do padrão State. CONTEXT + REQUEST ( ) STATE.HANDLE ( ) STATE + HANDLE ( ) CONCRETESTATE A + HANDLE ( ) CONCRETESTATE B + HANDLE ( )

(3)

um exemplo simples de implementação da interface para o estado “Recebido”. Repare que o código é bas-tante simples, apenas cabendo ao estado em questão decidir qual é o estado seguinte. As transições que não fazem sentido para um dado estado lançam a ex-ceção não checada UnsupportedOperationException.

Listagem 1.

Interface para os estados possíveis de um artigo.

publicinterface EstadoArtigo {

void submetido(Artigo artigo);

void validadoComSucesso(Artigo artigo); void validadoSemSucesso(Artigo artigo); void satisfatorio(Artigo artigo);

void apresentaErrosBasicos(Artigo artigo); void recusado(Artigo artigo);

}

Listagem 2.

Implementação do estado recebido.

publicclass Recebido implements EstadoArtigo {

publicvoid submetido(Artigo artigo) {

thrownew UnsupportedOperationException(“O

artigo está em um estado inválido para a

ocorrência do evento ‘submetido’: “ + this); }

publicvoid validadoComSucesso(Artigo artigo) { artigo.setEstado(new AceitoParaRevisao()); }

publicvoid validadoSemSucesso(Artigo artigo) { artigo.setEstado(new Recusado());

} /*

! "#$%&' $()*+*' +% &,)#-.%/# &$01#$#,)%+*' lançando UnsupportedOperationException igual a submetido() ...

*/

}

A Listagem 3 exibe o código da classe principal (o contexto do padrão State), por meio da qual o restan-te da aplicação inrestan-terage (por isso, os estados podem ter visibilidade restrita apenas ao pacote em que está a classe de contexto). Repare que a classe artigo não precisa ter conhecimento de qual é o estado seguinte ao do evento ocorrido, isso passa a ser uma responsa-bilidade do estado com que o artigo está atualmente +!375>&%"!8'9'!$( !&7* !'S%M'%' <5(+%T';> %'( -plementação tradicional, provavelmente não haveria os métodos de transição na classe Artigo, e a lógica "#'"#+(*:!'"#'#*4%"!*'#*4%&(%'+!"(7+%"%'# '(S*'"(S>*%-mente por outras partes do sistema (com os estados modelados talvez até como constantes inteiras, acre-dite se quiser!). A Listagem 4 mostra um exemplo de como poderia ser esse código sem o padrão.

Listagem 3.

Classe Artigo (faz o papel de contexto no padrão State).

publicclass Artigo {

private EstadoArtigo estado;

publicvoid setEstado(EstadoArtigo estado) { this.estado = estado;

}

Figura 2. Modelagem da máquina de estados para o sistema de publicação de artigos.

RECEBIDO RECUSADO

ACEITO PARA REVISÃO

ACEITO PARA

PUBLICAÇÃO ACEITO COM CORREÇÕES INÍCIO submetido FIM validado com sucesso satisfatório satisfatório apresenta erros básicos recusado recusado submetido validado sem sucesso

(4)

publicvoid submetido() { estado.submetido(this); }

publicvoid validadoComSucesso() { estado.validadoComSucesso(this); }

publicvoid validadoSemSucesso() { estado.validadoSemSucesso(this); }

2! +#$%&' $()*+*' +# )-%,'&34* *$&)&+*' !2

}

Listagem 4.

Exemplo de implementação tradicional sem usar o padrão State.

publicclass RevisaoArtigoService {

/* mais código aqui */

publicvoid revisar(Artigo artigo) { /* mais código aqui */

if (artigo.getEstado().equals( ACEITO_PARA_REVISAO)) { if (estaSatisfatorio(artigo)) { artigo.setEstado(ACEITO_PARA_PUBLICACAO); } elseif (correcaoPodeSerFeita(artigo)) { artigo.setEstado(ACEITO_COM_CORRECOES); } else { artigo.setEstado(RECUSADO); } } } }

Implementação do padrão com Enum e

JPA

Uma forma interessante de se implementar os es-tados é por meio do uso dos tipos enumerados (Enu-ms) oferecidos pelo Java. A ideia é simples: cria-se uma enum com métodos para cada transição e cons-tantes que implementem esses métodos de acordo com suas respectivas lógicas.

Normalmente, numa aplicação real, precisare-mos armazenar os dados dos artigos e seus estados em um banco de dados. Vamos assumir que queremos usar a Java Persistence API (JPA) com Hibernate para armazenar os objetos no banco de dados, incluindo aí os estados da máquina. O problema, neste caso, é que o Hibernate não oferece um suporte adequado à persistência de enums (existe anotação para isso, mas existem problemas para seu uso – usar strin-gs para mapeamento de chaves etc.). Mesmo numa implementação de máquina de estados sem enums,

! " #$! %&'#&()*!(+#'#,-'#!'.#'#/("01% 20#&03(&'#4# questão dos objetos transientes que já existem den-tro do banco (quando um estado necessita realizar uma transição, ele criaria uma nova instância de um estado já existente).

Para contornar esse problema, podemos repre-sentar as constantes em duas partes, usando uma classe principal (esta, mapeada via Hibernate), a qual delega para um enum que representa o estado da máquina de estados propriamente dito. A Listagem 5 mostra um exemplo de como poderia ser esse códi-go. Desta forma, é possível utilizar as constantes da classe EstadoArtigo (a lógica da máquina de estados 0-25#0%! 6-,+ & #(%201% .0%207#0#%'-#"0%0$!( 1.'-# também das facilidades de persistência oferecidas pelo Hibernate. Repare que implementamos no con-texto da enum todos os métodos de transição, mas com uma implementação default (como se fosse uma classe abstrata). Dessa forma, as constantes enume-1 & -#-8#6enume-10!(- .#(.6+0.0%2 enume-1# -#2enume-1 %-(9:0-#;,0#$-zerem sentido. Lembramos que a classe de contexto, Artigo, permanece com o mesmo código.

Listagem 5.

Implementação do padrão State com Enums e suporte de JPA/Hibernate.

@Entity

publicclass EstadoArtigo {

publicstatic !"# EstadoArtigo RECEBIDO = new EstadoArtigo(1, EstadosMaquinaEstados. RECEBIDO);

publicstatic !"# EstadoArtigo RECUSADO = new EstadoArtigo(2, EstadosMaquinaEstados. RECUSADO);

publicstatic !"# EstadoArtigo ACEITO_PARA_REVISAO= new EstadoArtigo(3, EstadosMaquinaEstados. ACEITO_PARA_REVISAO);

publicstatic !"# EstadoArtigo

ACEITO_PARA_PUBLICACAO=new EstadoArtigo(4, EstadosMaquinaEstados.ACEITO_PARA_PUBLICACAO); publicstatic !"# EstadoArtigo

ACEITO_COM_CORRECOES = new EstadoArtigo(5, EstadosMaquinaEstados.ACEITO_COM_CORRECOES);

@Id

privateint !"#$%!&'(")*+

private EstadosMaquinaEstados estadoMaquina;

private EstadoArtigo(int !"#$%!&'(")*, EstadosMaquinaEstados estadoMaquina) { this-!"#$%!&'(")* . !"#$%!&'(")*+

this.estadoMaquina = estadoMaquina; }

protectedvoid submetido(Artigo artigo) { estadoMaquina.submetido(artigo); }

protectedvoid validadoComSucesso(Artigo artigo) { estadoMaquina.validadoComSucesso(artigo); }

protectedvoid validadoSemSucesso(Artigo artigo) { estadoMaquina.validadoSemSucesso(artigo); }

(5)

protectedvoid satisfatorio(Artigo artigo) { estadoMaquina.satisfatorio(artigo); }

protectedvoid apresentaErrosBasicos(Artigo artigo) { estadoMaquina.apresentaErrosBasicos(artigo); }

protectedvoid recusado(Artigo artigo) { estadoMaquina.recusado(artigo); }

privatestatic enum EstadosMaquinaEstados {

RECEBIDO { @Override

void validadoComSucesso(Artigo artigo) { artigo.setEstado(EstadoArtigo. ACEITO_PARA_REVISAO); }

@Override

void validadoSemSucesso(Artigo artigo) { artigo.setEstado(EstadoArtigo.RECUSADO); } }, ACEITO_PARA_REVISAO { @Override

void satisfatorio(Artigo artigo) { artigo.setEstado(EstadoArtigo. ACEITO_PARA_PUBLICACAO); }

@Override

void apresentaErrosBasicos(Artigo artigo) { artigo.setEstado(EstadoArtigo.

ACEITO_COM_CORRECOES); }

@Override

void recusado(Artigo artigo) {

artigo.setEstado(EstadoArtigo.RECUSADO); }

},

/* implementacao das transicoes omitidas para os seguintes estados... */

RECUSADO,

ACEITO_PARA_PUBLICACAO, ACEITO_COM_CORRECOES;

void submetido(Artigo artigo) { lancarExcecaoDefault(“submetido”); }

void validadoComSucesso(Artigo artigo) { lancarExcecaoDefault(“validadoComSucesso”); }

void validadoSemSucesso(Artigo artigo) { lancarExcecaoDefault(“validadoSemSucesso”); }

void recusado(Artigo artigo) { lancarExcecaoDefault(“recusado”); }

void satisfatorio(Artigo artigo) { lancarExcecaoDefault(“satisfatorio”); } void apresentaErrosBasicos(Artigo artigo) { lancarExcecaoDefault(“apresentaErrosBasicos”); }

privatevoid lancarExcecaoDefault(String evento) { thrownew UnsupportedOperationException( “O artigo está em um estado inválido para a

ocorrência do evento “ + evento + “:” + this); }

} }

$%!&'()*"+,)&- !"'&

Máquinas de estados são uma abstração recor-rente em diversos tipos de sistema, constituindo um poderoso instrumento de modelagem e comunicação para usuários e desenvolvedores. Neste artigo, vimos como podemos levar as vantagens desse modelo até o nível do código por meio do padrão State, evitando--se, assim, o espalhamento das lógicas de transição por diferentes partes do sistema. Destacamos tam-bém uma possibilidade de implementação particular para o caso em que é necessária a persistência dos es-2 &'-#!'.#<=>#?/("01% es-20#,- %&'#es-2(6'-#0%,.01 &'-@

“Procura conhecer o estado das tuas ovelhas; cui-da bem dos teus rebanhos.” (Pv 27:23)

!"#$%&'!()**#+'$!,!-.#/#'*$!01!2#3$)4.#!546#7*80+%#'*#9! :01*;)+#!,!<)'&!01!=03+ !2#1)7*0+%'&!*0!>)**#+'$!,!?0$@3)!A#+%#B$CD !2#1)7*0+%'&E!F/>+0B%'&!*@#!"#$%&'!01!-G%$*%'&!H09#!,! I)+*%'!=0;.#+ !?)B)!(#+$%$*#'7#!;%*@!J%4#+')*#!,!H@+%$*%)'!K)3#+!#! <)B%'!A%'&

/referências

!LJ#+)'M)!#!H0/>0$%MN0!,!0$!>+%'7O>%0$!>0+!*+P$!90$! >)9+Q#$RS!#9T!UV!9)!I3'90?!,!-93)+90!<3#++) !L?(W!XE!5$!'0B0$!+#73+$0$!%'$>%+)90$!'0!J%4#+')*#RS!#9T! UV!9)!I3'90?!,!()3.0!:%.B#%+)!#!2)>@)#.!Y)7#+9) !L-'3/$!9#$/%$*%Z7)9)$RS!#9T!X[!9)!I3'90?!,!W.#G)'9+#! Gazola !:\+%#!L"#$%&'!>)**#+'$!>)+)!3/!I3'90!2#).RS!#9$T!X]S!XX! #!XU!9)!I3'90?!,!209+%&0!^0$@%/)

Referências

Documentos relacionados

[r]

Seja M n+1 uma variedade Riemanniana com curvatura de Ricci n˜ ao-negativa a qual admite um campo de vetores parcialmente con- forme fechado K e campo associado W.. Usaremos a

Neste cap´ıtulo trataremos das f´ ormulas para a primeira varia¸c˜ ao da ´ area e de volume, apresentaremos problemas variacionais para hipersuperf´ıcies com curvatura m´ edia

Repare-se que no §49 o autor já indicara a relação entre o grau de cozedura das carnes e o seu valor terapêutico, o que não deixa dúvidas ao público (especializado ou não)

encontrou na tradição indiana o suporte de linguagem numérica mais adequado. O suporte encontrado permitiu-lhe chegar a soluções mais gerais de resolução de equações de

Por isso, o Governo Federal através do Banco Central do Brasil instituiu por meio do Decreto nº 7.397, de 22 de dezembro de 2010, a Estratégia Nacional para Educação

Limitador de velocidade (SAS): o veículo oferece limitador de velocidade como padrão e atende aos requisitos do Latin NCAP. Detecção de ponto cego (BSD): o veículo oferece BSD, mas

O Padrão de Desempenho 1 estabelece a importância: (i) da avaliação integrada para identificar os impactos e riscos socioambientais e as oportunidades dos projetos; (ii) do