Fala ai Radizeiros e Radizeiras, tudo bom com vocês?
Quem nunca precisou tratar os estados de um venda, saber se seu caixa está aberto ou fechado, se aquele item está ativo ou não, e ai você acaba distribuído objetos para tudo que é lado, polui seu banco de dados com esses estados que alteram a cada momento, isso pode acarretar problemas posteriormente, se já não está acontecendo com você.
Quando trabalhamos orientado a objetos, usando os padrões de projetos de forma correta, tudo fica mais fácil de trabalhar.
Sabemos que trabalhar orientado a objeto é trabalhar com estados e comportamentos dos objetos, o padrão State, que estarei mostrando neste post, fornece meios mais simples de controlar esses estados.
Neste post irei lhe mostrar como validar dados usando o padrão de projeto state.
Olhando o diagrama você pode não compreender como esse padrão poderá lhe ajudar, mas como eu gosta de mostrar tudo usando exemplos reais.
Nesse exemplo irei vender itens usando o padrão de projeto State.
No diagrama ele pede uma interface, essa interface é apenas para os estados dos itens, para as operações que irão variar de acordo com o estado.
Por exemplo:
Possuo um item, e quais são os estados que o meu item pode ter?
O meu item pode estar ativo, inativo, vendido, e já que o item está vendido no cupom ele pode ter um comportamento diferente, se o item esta inativo, ele terá outro comportamento, se o item está ativo, ele irá se comportar de outra forma.
Visto isso iremos ter esses 3 estados, Ativo, Inativo, Vendido.
Agora vamos ao código
Primeiro devo criar minhas interfaces que terão o item e as operações.
iItem = interface ['{F7FD84E6-35D6-4609-A40C-E7FF93B3372E}'] function State : iState<iItemOperacoes>; function Operacoes : iItemOperacoes; end; iItemOperacoes = interface ['{621F9457-EDFD-4251-B9BE-7E29FA3ACF4D}'] function Vender : iItemOperacoes; function Cancelar : iItemOperacoes; function Desconto : iItemOperacoes; function Devolver : iItemOperacoes; end;
A interface iItemOperacoes compartilha as operações de cada estado, quando o item está inativo ele irá ter todas essas operações, porém cada uma com o comportamento diferente.
Neste ponto iremos criar a classe que implementa a interface iItem:
type TModelItem = class(TInterfacedObject, iItem, iItemOperacoes, iState<iItemOperacoes>) private FState : iItemOperacoes; public constructor Create; destructor Destroy; override; class function New: iItem; function Vender: iItemOperacoes; function Cancelar: iItemOperacoes; function Desconto: iItemOperacoes; function Devolver: iItemOperacoes; function SetState(Value : iItemOperacoes) : iItemOperacoes; function State : iState<iItemOperacoes>; function Operacoes : iItemOperacoes; end; implementation { TModelItem } function TModelItem.Cancelar: iItemOperacoes; begin FState.Cancelar; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de cancelamento retorna para ativo Result := Self; end; constructor TModelItem.Create; begin FState := TModelItemAtivo.New; end; function TModelItem.Desconto: iItemOperacoes; begin FState.Desconto; //implementa o codigo de cancelamento Result := Self; end; destructor TModelItem.Destroy; begin inherited; end; function TModelItem.Devolver: iItemOperacoes; begin FState.Devolver; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de devolução retorna para ativo Result := Self; end; class function TModelItem.New: iItem; begin Result := Self.Create; end; function TModelItem.Operacoes: iItemOperacoes; begin Result := self; end; function TModelItem.SetState(Value: iItemOperacoes): iItemOperacoes; begin Result := Self; FState := Value; end; function TModelItem.State: iState<iItemOperacoes>; begin Result := Self; end; function TModelItem.Vender: iItemOperacoes; begin FState.Vender; //implementa o codigo de cancelamento FState := TModelItemVendido.New;//Após realizar todos as operações de venda recebe o estado de vendido Result := Self; end;
Implementamos os métodos e tratamos os métodos referente aos estados.
Mas você deve esta se perguntando, afinal de contas como irei armazenar os estados do item?
Calma radizeiro, para que possamos implementar o estado iremos precisar de uma interface, neste caso iremos criar uma interface genérica, onde todas as classes do nosso código que quisermos que elas implementem o estado ela irá implementar essa interface.
... iState<T> = interface ['{FAF29342-D2DE-4C63-84B0-F96AC498518A}'] function SetState(Value : T) : T; end; ...
Viu o que foi feito?
Nós criamos o item(interface = iItem), separamos as operações do item em uma outra interface(inerface = iItemOpecacoes), e criamos uma interface de estado(interface = iState).
Uma pequena analise
Na nossa classe de item você pode ver que colocamos a interface iState<iItemOperacoes>, pois as operações que são responsaveis por tratar os estados.
... type TModelItem = class(TInterfacedObject, iItem, iItemOperacoes, iState<iItemOperacoes>) private FState : iItemOperacoes; ...
Nossa classe item implementa os métodos de nossa interface de estados, para que possamos trabalhar esses métodos iremos precisar de um objeto, o qual chamamos de FState do tipo iItemOperacoes.
type ... private FState : iItemOperacoes; ... function Vender: iItemOperacoes; function Cancelar: iItemOperacoes; function Desconto: iItemOperacoes; function Devolver: iItemOperacoes; ...
Olha o que foi feito até agora
Criei um item, separei as funções que dependem de um estado, lembra o que disse, se for um ativo irá ter um comportamento, inativo outro, e por ai vai, cada ação que depende de um estado do objeto nós separamos em uma interface diferente, tenho meu item, suas ações separadas, e por ultimo possuo uma interface genérica que irá trabalhar com qualquer estado de qualquer objeto, em nosso caso ela esta implementando o iItemOperacoes
... function TModelItem.Cancelar: iItemOperacoes; begin FState.Cancelar; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de cancelamento retorna para ativo Result := Self; end; function TModelItem.Desconto: iItemOperacoes; begin FState.Desconto; //implementa o codigo de cancelamento Result := Self; end; function TModelItem.Devolver: iItemOperacoes; begin FState.Devolver; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de devolução retorna para ativo Result := Self; end; function TModelItem.Vender: iItemOperacoes; begin FState.Vender; //implementa o codigo de cancelamento FState := TModelItemVendido.New;//Após realizar todos as operações de venda recebe o estado de vendido Result := Self; end; ...
Você pode observar que dentro de cada método da operação estou chamando sua ação dentro do seu estado, esse processo é feito porque todas ações que são compartilhadas pela interface de operações, vender, cancelar, desconto e devolver, antes delas fazerem qualquer coisa nós iremos chamar essa ação dentro do seu estado, porque ela irá ter o comportamento de acordo com o estado que estiver setado.
Você deve estar pensado, nossa por que tanto código para isso, lembre que programar de forma funcional você evita de ter problemas futuros em suas manutenções e futuras implementações, depois que você ver esse padrão funcionando você irá querer usar em tudo que é lugar…
... type TModelItemAtivo = class(TInterfacedObject,iItemOperacoes) private public constructor Create; destructor Destroy; override; class function New : iItemOperacoes; function Vender : iItemOperacoes; function Cancelar : iItemOperacoes; function Desconto : iItemOperacoes; function Devolver : iItemOperacoes; end; implementation uses System.SysUtils; { TModelItemAtivo } function TModelItemAtivo.Cancelar: iItemOperacoes; begin Result := self; raise Exception.Create('Este item ainda não foi vendido'); end; constructor TModelItemAtivo.Create; begin end; function TModelItemAtivo.Desconto: iItemOperacoes; begin Result := self; raise Exception.Create('Este item ainda não foi vendido'); end; destructor TModelItemAtivo.Destroy; begin inherited; end; function TModelItemAtivo.Devolver: iItemOperacoes; begin Result := self; raise Exception.Create('Este item ainda não foi vendido'); end; class function TModelItemAtivo.New: iItemOperacoes; begin Result := Self.Create; end; function TModelItemAtivo.Vender: iItemOperacoes; begin Result := Self; end;
Em nossa classe de item ativo implementa a interface de operações, e tratamos seus comportamentos.
Você pode observar que nos métodos cancelar,desconto e devolver não podem ser implementados pois o item ainda não foi vendido.
Agora eu deleguei para uma outra classe, a classe de estado toda responsabilidade caso o item esteja ativo.
Veja as demais classe que implantem os estados.
Inativo:
type TModelItemInativo = class(TInterfacedObject,iItemOperacoes) private public constructor Create; destructor Destroy; override; class function New : iItemOperacoes; function Vender : iItemOperacoes; function Cancelar : iItemOperacoes; function Desconto : iItemOperacoes; function Devolver : iItemOperacoes; end; implementation uses System.SysUtils; { TModelItemInativo } function TModelItemInativo.Cancelar: iItemOperacoes; begin Result := self; raise Exception.Create('Este item está inativo'); end; constructor TModelItemInativo.Create; begin end; function TModelItemInativo.Desconto: iItemOperacoes; begin Result := self; raise Exception.Create('Este item está inativo'); end; destructor TModelItemInativo.Destroy; begin inherited; end; function TModelItemInativo.Devolver: iItemOperacoes; begin Result := self; raise Exception.Create('Este item está inativo'); end; class function TModelItemInativo.New: iItemOperacoes; begin Result := Self.Create; end; function TModelItemInativo.Vender: iItemOperacoes; begin Result := Self; raise Exception.Create('Este item está inativo'); end;
Vendido:
type TModelItemVendido = class(TInterfacedObject,iItemOperacoes) private public constructor Create; destructor Destroy; override; class function New : iItemOperacoes; function Vender : iItemOperacoes; function Cancelar : iItemOperacoes; function Desconto : iItemOperacoes; function Devolver : iItemOperacoes; end; implementation uses System.SysUtils; { TModelItemVendido } function TModelItemVendido.Cancelar: iItemOperacoes; begin Result := self; end; constructor TModelItemVendido.Create; begin end; function TModelItemVendido.Desconto: iItemOperacoes; begin Result := self; end; destructor TModelItemVendido.Destroy; begin inherited; end; function TModelItemVendido.Devolver: iItemOperacoes; begin Result := self; end; class function TModelItemVendido.New: iItemOperacoes; begin Result := Self.Create; end; function TModelItemVendido.Vender: iItemOperacoes; begin Result := Self; {Foi colocado somente para fins didádicos} raise Exception.Create('Este item já foi vendido'); end;
Criamos nossas classes que implementa os estados, e agora vamos ver esses estados funcionando.
A magica acontecendo…
Em nossa view possuímos os botões que iremos tratar os exemplos de comportamento.
Em nossa classe de item você pode ver que de acordo com a ação ele irá verificar se pode fazer algo e irá alterar seu estado.
... function TModelItem.Cancelar: iItemOperacoes; begin FState.Cancelar; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de cancelamento retorna para ativo Result := Self; end; function TModelItem.Desconto: iItemOperacoes; begin FState.Desconto; //implementa o codigo de cancelamento Result := Self; end; function TModelItem.Devolver: iItemOperacoes; begin FState.Devolver; //implementa o codigo de cancelamento FState := TModelItemAtivo.New;//Após realizar todos as operações de devolução retorna para ativo Result := Self; end; function TModelItem.Vender: iItemOperacoes; begin FState.Vender; //implementa o codigo de cancelamento FState := TModelItemVendido.New;//Após realizar todos as operações de venda recebe o estado de vendido Result := Self; end; ...
Em nossa view essas operações de Ativar, Vendido e Inativo está sendo feito para fins didático, mas em seu software esse processo pode ser feito automático.
... private { Private declarations } FItem : iItem; ... procedure TForm2.Button1Click(Sender: TObject); begin //Ativar FItem.State.SetState(TModelItemAtivo.New); end; procedure TForm2.Button2Click(Sender: TObject); begin //Vendido FItem.State.SetState(TModelItemVendido.New); end; procedure TForm2.Button3Click(Sender: TObject); begin //Inativo FItem.State.SetState(TModelItemInativo.New); end; ...
Veja como fica o código que tratar as operações de vender, cancelar, desconto, devolver.
... procedure TForm2.Button4Click(Sender: TObject); begin FItem.Operacoes.Vender; end; procedure TForm2.Button5Click(Sender: TObject); begin FItem.Operacoes.Cancelar; end; procedure TForm2.Button6Click(Sender: TObject); begin FItem.Operacoes.Desconto; end; procedure TForm2.Button7Click(Sender: TObject); begin FItem.Operacoes.Devolver; end; ...
Ao tentar dar desconto em um item já ativo, olha a mensagem que é retornada:
Viu como é legal esse padrão?
Você vendo funcionando é outra coisa né? RSRS
Nós criamos estados para comportamentos das classes, no caso do item.
Como ele se comporta quando está ativo? Vamos só na classe de ativo e vejo como é o comportamento dele.
E assim para todos os seus estados, vendo suas regras de validações.
Desta forma você poderá tratar cada comportamento das operações como melhor se aplica ao seu senário, seja para pegar um estado de um caixa, ou da venda, não importa, esse padrão se aplica a tudo objeto que precisa ser guardado seu estado.
Você pode baixar no link abaixo todos os fontes desse artigo:
POR QUE FAZER ESTE TREINAMENTO?
Por mais que achamos que estamos construindo o melhor produto do planeta, sempre existe espaço para melhorar, ou alguém que já está fazendo algo melhor, se você ainda não utiliza Design Patterns (Padrões de Projeto) no seu software, você está anos luz atrás de muitos de seus concorrentes.
Nesse treinamento você irá aprender de forma prática como utilizar e para que serve cada um dos 23 padrões do GOF, com exemplos reais de aplicação das técnicas para resolver problemas do dia a dia, Como:
* Padrões para Fluxo de Nota Fiscal e Venda.
* Construção de Factorys para criação de classes e redução de acoplamento.
* Padrões para repetição e clonagem de itens em vendas e documentos fiscais.
* Padrões para redução de consumo de memória.
* Padrões para criação de cache e aumento de performance da aplicação.
* Padrões para agregação de funcionalidades em tempo de execução.
* Padrões para geração de arquivos fiscais (SPED, Sintegra) de forma prática.
* Padrões para validação de permissão a nível de usuário
* Padrões para Estratégias a nível de tomada de decisão por parâmetro.
* Padrões para comunicação de múltiplas camadas do software.
* e muito mais…
Dê um salto de qualidade na sua carreira e no seu produto!
CLIQUE AQUI PARA SABER MAIS SOBRE A CERTIFICAÇÃO ESPECIALISTA EM PADRÕES DE PROJETO EM DELPHI