Você está na página 1de 3

Delphi e o Padro Observer Muitas vezes, nos mais diversos sistemas, precisamos implementar algum comportamento que dependa

de um fator especfico. Quando esse desenvolvimento feito atravs de objetos, desejamos que os elementos envolvidos estejam o mnimo possvel interligados, aplicando a regra de baixo acoplamento; ou seja, quanto menos os objetos souberem um do outro, melhor. Uma situao real Temos um sistema de e-commerce que ao ser feita uma venda, o cliente que comprou o produto recebe um e-mail com uma confirmao, o setor de logstica notificado, atravs do envio automatizado de um arquivo texto, do local de entrega do produto. A maneira convencional Como todo desenvolvedor RAD, de primeira mo j vemos tudo resumido em uma procedure, que ser disparada no evento OnClick do boto Confirmar Compra, gerando um algoritmo parecido com o da Listagem 1: 1. Validar a comprar. 2. Se a compra no vlida para o processamento e notifica usurio. 3. Se for vlida, notifica usurio e envia um arquivo-texto. O principal problema que vejo nessa abordagem de que um requisito da nossa regra de negcios est implementado na camada de apresentao do sistema. Qualquer mudana nessa rea pode acarretar erros nas regras de negcio ou implicar em duplicao de cdigo. Hoje nosso sistema acessado atravs de uma pgina. E se amanh tivermos que disponibilizar essa mesma rotina para algum parceiro que gostaria de integrar nossa rotina de vendas em seu sistema interno? Eu faria isso atravs de web services. E quanto quele cdigo que fizemos no boto Confirmar Venda da pgina? Teramos que replic-lo no web service? Mudana de pensamento Quero apresentar hoje o padro Observer. Ele indicado para tratar situaes de dependncia como a mencionada no inicio do artigo. Sua utilizao oferece o benefcio de relacionar duas ou mais classes sem criar um vnculo explcito entre elas. Este padro abstrai a relao dos objetos definindo-os como Observer e Subject. Observer so objetos que ficam aguardando por uma mudana no estado de determinado objetos para poderem tomar uma ao. Subject so os objetos que esto sendo observados. Olhando para nossa situao, o objeto Venda um Subject e a notificao ao usurio e envio de arquivo so os Observers. Vamos colocar logo a mo na massa e implement-lo para resolver nosso problema. Primeiramente vamos criar as duas classes base que representam o padro em si, TSubject e TObserver, na listagem 2: TObserver = class public FSubject: TSubject; procedure Update; virtual; abstract; end; TSubject = class public FObservers: TObjectList; procedure Attach(const Observer: TObserver); procedure Detach(const Observer: TObserver); procedure DetachAll; procedure Notify; end; Na listagem 3, a implementao do nosso Subject: procedure TSubject.Attach(const Observer: TObserver); begin if FObservers = nil then FObservers := TObjectList.Create; Observer.FSubject := Self; fObservers.Add(Observer); end;

procedure TSubject.Detach(const Observer: TObserver); begin if fObservers <> nil then begin fObservers.Remove(Observer); if FObservers.Count = 0 then FObservers := nil; end; end; procedure TSubject.DetachAll;

var i, k: Integer; begin if FObservers <> nil then begin k := FObservers.Count; for i := Pred(k) downto 0 do Detach(FObservers[i] as TObserver); end; end; procedure TSubject.Notify; var i: Integer; begin if FObservers <> nil then for i := 0 to FObservers.Count -1 do (fObservers[i] as TObserver).Update; end; Temos agora que implementar nossas classes de negcio, que tero o comportamento do padro Observer. Veja, na listagem 4: TVenda = class(TSubject) private FCliente: TCliente; FProduto: TProduto; FDataVenda: TDateTime; FQtdeVendida: integer; function GetConfirmacao: string; function GetDadosEntrega: string; procedure SetCliente(const Value: TCliente); procedure SetProduto(const Value: TProduto); procedure SetDataVenda(const Value: TDateTime); procedure SetQtdeVendida(const Value: integer); public constructor Create; destructor Destroy; override; property Cliente: TCliente read FCliente write SetCliente; property Produto: TProduto read FProduto write SetProduto; property DataVenda: TDateTime read FDataVenda write SetDataVenda; property QtdeVendida: integer read FQtdeVendida write SetQtdeVendida; procedure Finalizar; end; TLogistica = class(TObserver) public procedure Update; override; end; TConfirmacao = class(TObserver) public procedure Update; override; end; O mtodo Finalizar, de TVenda, na listagem 5, chama o mtodo herdado Notify, que varre a lista de observers, notificando a cada um que a compra foi finalizada. procedure TVenda.Finalizar; begin // Aqui, validamos a venda e, estando OK... Notify; // ... Notificamos os observers, quaisquer que sejam. end; Posteriormente, cada classe que implementa TObserver executa o mtodo Update sua maneira, conforme listagem 6. { TLogistica } procedure TLogistica.Update; begin if FSubject is TVenda then ShowMessage(TVenda(FSubject).GetDadosEntrega); end;

{ TConfirmacao } procedure TConfirmacao.Update; begin if FSubject is TVenda then ShowMessage(TVenda(FSubject).GetConfirmacao); end; Com essas classes implementadas, mais TCliente e TProduto, temos o que chamado de Business Object Model, ou Modelo de objetos de negcio. Nossas regras esto encapsuladas em classes, de tal forma que podemos variar a interface com o usurio. Na Listagem 7, temos parte do cdigo do exemplo que est anexado. procedure TForm1.FormCreate(Sender: TObject); begin (** Preparando a venda... O preenchimento das classes FCliente e FProduto poderiam se dar de qualquer forma, como por um formulrio comum, uma pgina Web, ou at mesmo por um WebService. *) FCliente := TCliente.Create; FCliente.Nome := 'Paulo Roberto Quicoli'; FCliente.Endereco := 'Av. do Centro, S/N'; FCliente.Email := 'pauloquicoli@gmail.com'; FProduto := TProduto.Create; FProduto.Nome := 'TV de Plasma 42"'; FProduto.Valor := 7899.00; FAvisaUsuario := TConfirmacao.Create; FAvisaLogistica := TLogistica.Create; FVenda := TVenda.Create; { Veja que adicionamos aqui os observers que esperam pela concluso da venda } FVenda.Attach(FAvisaUsuario); FVenda.Attach(FAvisaLogistica); end; procedure TForm1.btFinalizarClick(Sender: TObject); begin (** Aqui finalizamos a compra. Digo mais uma vez que isso poderia se dar por um formulrio comum, uma pgina web ou WebService. *) FVenda.Cliente := FCliente; FVenda.Produto := FProduto; FVenda.DataVenda := now; FVenda.QtdeVendida := 1; FVenda.Finalizar; end; Concluso Encapsular nossas regras de negcio em classes melhor coisa que podemos fazer para nossos projetos. Veja na Listagem 7, no cdigo do evento btFinalizarClick no mencionamos a regra exigida, que era notificar usurio e enviar um arquivo para a logstica. Tudo ficou no nosso modelo de negcios que, desenvolvido da forma sugerida, poder ser reutilizado por outras interfaces. A implementao do padro Observer foi a mais simples possvel, juntamente com o exemplo, para que a ideia do seu funcionamento pudesse ser assimilada de forma fcil. http://www.devmedia.com.br/padrao-observer/2065