ActiveDelphi - Índice do Fórum ActiveDelphi
.: O site do programador Delphi! :.
 
 FAQFAQ   PesquisarPesquisar   MembrosMembros   GruposGrupos   RegistrarRegistrar 
 PerfilPerfil   Entrar e ver Mensagens ParticularesEntrar e ver Mensagens Particulares   EntrarEntrar 

Padrão Observer [RESOLVIDO]

 
Novo Tópico   Responder Mensagem    ActiveDelphi - Índice do Fórum -> Delphi
Exibir mensagem anterior :: Exibir próxima mensagem  
Autor Mensagem
edson.amf
Novato
Novato


Registrado: Segunda-Feira, 30 de Março de 2009
Mensagens: 58

MensagemEnviada: Qua Jan 19, 2011 8:29 am    Assunto: Padrão Observer [RESOLVIDO] Responder com Citação

Saudações....
estou testando o padrão de projeto Observer e um erro está ocorrendo.
Quando passo a classe que implementa ISubject no construtor de outra classe que implementa IObserver , por alguma razão a lista que armazena os observadores está sendo liberada da memória. Alguém sabe o por quê?

Abaixo a interface
Código:

unit Unit2;

interface

type
    ISubject = interface;

    IObserver = interface
        ['{AF15A5C6-EB0A-4748-A034-7E2B634EBD7D}']
        procedure update(s: ISubject);
    end;

    ISubject = interface
        ['{85093283-A063-4688-B2D7-D1936D0AED50}']
        procedure addObserver(o:IObserver);
        procedure deleteObserver(o:IObserver);
        procedure notifyObservers();
    end;

implementation

end.


Abaixo a classe que implementa ISubject
Código:

unit Unit3;

interface

uses Unit2, Classes, SysUtils;

type
    TDono = class(TInterfacedObject, ISubject)
    private
      { private declarations }
        fObservers: TInterfaceList;
    protected
      { protected declarations }
    public
      { public declarations }
        constructor create(); overload;
        destructor Destroy(); override;

        procedure addObserver(o:IObserver);
        procedure deleteObserver(o:IObserver);
        procedure notifyObservers();
        procedure alimentar;
    end;

implementation

{ TDono }

procedure TDono.addObserver(o: IObserver);
begin
    Self.fObservers.Add(o);
end;

procedure TDono.alimentar;
begin
    notifyObservers;
end;

constructor TDono.create;
begin
    inherited Create();
    Self.fObservers := TInterfaceList.Create;
end;

procedure TDono.deleteObserver(o: IObserver);
begin
    Self.fObservers.Remove(o);
end;

destructor TDono.Destroy;
begin
    FreeAndNil(Self.fObservers);
    inherited;
end;

procedure TDono.notifyObservers;
var
    i: Integer;
begin
    for i := 0 to Self.fObservers.Count - 1 do
      IObserver(Self.fObservers.Items[i]).update(Self);
end;

end.


Abaixo a classe que implementa IObserver
Código:

unit Unit4;

interface

uses Unit2;

type
    TCao = class(TInterfacedObject, IObserver)
    private
      { private declarations }
    protected
      { protected declarations }
    public
      { public declarations }
        constructor create(s: ISubject);

        procedure update(s: ISubject);
    end;

implementation

{ TCao }

constructor TCao.create(s: ISubject);
begin
    inherited Create();
    s.addObserver(Self);
end;

procedure TCao.update(s: ISubject);
begin
    {TODO -oOwner -cGeneral : ActionItem}
end;

end.


Abaixo o teste
Código:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Unit3, Unit4, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
    dono: TDono;
    cao: TCao;
begin
    try
        dono := TDono.create;
        cao := TCao.create(dono);
        dono.alimentar;
        dono.deleteObserver(cao);
    finally
        FreeAndNil(dono);
        FreeAndNil(cao);
    end;
end;

end.


Editado pela última vez por edson.amf em Qua Jan 19, 2011 10:06 pm, num total de 1 vez
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
rogerbetti
Colaborador
Colaborador


Registrado: Quarta-Feira, 2 de Fevereiro de 2005
Mensagens: 1366
Localização: São Paulo/SP

MensagemEnviada: Qua Jan 19, 2011 9:34 am    Assunto: Responder com Citação

o problema está neste trecho:

Código:
constructor TCao.create(s: ISubject);
begin
    inherited Create();
    s.addObserver(Self);
end;


vc está passando uma interface e chamando um metodo de uma classe, acho que vai fugir do conceito mas tente passar a classe

Código:
constructor TCao.create(s: TDono);
begin
    inherited Create();
    s.addObserver(Self);
end;
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
edson.amf
Novato
Novato


Registrado: Segunda-Feira, 30 de Março de 2009
Mensagens: 58

MensagemEnviada: Qua Jan 19, 2011 10:33 am    Assunto: Responder com Citação

Bom, realmente funciona se a classe for passada ao invés da interface.
Mas eu não entendi o porque disso acontecer.
Se formos analisar o método notifyObservers(), veremos que é a mesma coisa. Eu tenho uma lista de interfaces e executo o método update(), que é comum a todas as classes que implementam IObserver.
Realmente estou curioso, se alguém souber explicar favor compartilhar aki conosco.
E obrigado pela colaboração rogerbetti.
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
rogerbetti
Colaborador
Colaborador


Registrado: Quarta-Feira, 2 de Fevereiro de 2005
Mensagens: 1366
Localização: São Paulo/SP

MensagemEnviada: Qua Jan 19, 2011 10:43 am    Assunto: Responder com Citação

acho que o problema é de lógica, quando vc chama um metodo de uma interface que é comum ele o executa na classe, mas o problema é em que instancia do objeto criado?

veja que dentro de seu método, dando um cast no self, vc não consegue chegar na instancia dono, talvez deva ter um parametro a mais com o ponteiro do objeto
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
marcosalles
Moderador
Moderador


Registrado: Quarta-Feira, 26 de Março de 2008
Mensagens: 1695
Localização: Muriaé Mg

MensagemEnviada: Qua Jan 19, 2011 12:15 pm    Assunto: Responder com Citação

rogerbetti escreveu:
o problema está neste trecho:

Código:
constructor TCao.create(s: ISubject);
begin
    inherited Create();
    s.addObserver(Self);
end;


vc está passando uma interface e chamando um metodo de uma classe, acho que vai fugir do conceito mas tente passar a classe

Código:
constructor TCao.create(s: TDono);
begin
    inherited Create();
    s.addObserver(Self);
end;


conceitualmente todos os dois metodos fogem do Padrão . A idéia é que a
classe obervada não tome conhecimento da classe obervadora . No caso TCao
Não deve conhecer a TDono.

constructor TCao.create();
begin
inherited Create();
// TDono.create.addObserver(self);
end;


A saida recomendada é que esta adição seje feita pelo proprio Framework no
caso especifico o formprincipal

Tire o método create da classe TCao e a referencia com uDono

Reescreva


Código:
var
    dono:TDono;
    cao:IObserver;
begin
        dono := TDono.create;
        cao := TCao.create();
    try
        dono.addObserver(cao);
        dono.alimentar;
        dono.deleteObserver(cao);
    finally
        FreeAndNil(dono);
    end;


claro que vc poderia usar interface para dono , desde que declarar os metodos
da Classe TDono na uses da Interface

Citação:
ISubject = interface
['{85093283-A063-4688-B2D7-D1936D0AED50}']
procedure addObserver(o:IObserver);
procedure deleteObserver(o:IObserver);
procedure notifyObservers();

procedure alimentar;

end;


com isto ganharia a contagem de referencia do Delphi

Código:
var
    dono:ISubject;
    cao:IObserver;
begin
        dono := TDono.create;
        cao := TCao.create();
        dono.addObserver(cao);
        dono.alimentar;
        dono.deleteObserver(cao);

_________________
http://marcosalles.wordpress.com

Desenvolvo FreeLancer e presto Consultoria
Orientação Online DataSnap DbX ClientDataSet
POO , Padrões de Projeto e dúvidas de Delphi em
Geral
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Visitar a homepage do Usuário
edson.amf
Novato
Novato


Registrado: Segunda-Feira, 30 de Março de 2009
Mensagens: 58

MensagemEnviada: Qua Jan 19, 2011 1:00 pm    Assunto: Responder com Citação

Saudações....
Certamente a idéia é que a classe observada não conheça as classes observadoras, mas não existe problema que a classe observadora conheça um pouco sobre a observada.
No nosso caso:
-> Classe observada: TDono
-> Classe observadora: TCao
para TDono, não importa quem é seu observador desde que ele implemente a interface IObserver, que é atravez do métoco update() que ela notificará seus observadores. Daí temos 2 tipos de se usar o update, com parâmetro (update(arg: TObejct)) e sem parâmetro(update(arg: TObject)), em outras palavras do tipo "puxa" e do tipo "empurra". Conceitualmente é mais correto usar o tipo "puxa", pq alguns observadores podem usar alguns valores e outros não. Daí o método update ficaria assim:
Código:

procedure TCao.update(s: ISubject);
begin
      if s is TDono
           ShowMessage(TDono(s).getTipoComida);
end;


por isso eu digo que o observador pode conhecer um pouco a classe observada. Mas o fato é que o meu modelo não funciona mesmo no Delphi. Eu fiz a mesma aplicação em Java e funciona perfeitamente.
Acho que a solução é mesmo fazer uma adaptação para esse padrão aki.
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
rogerbetti
Colaborador
Colaborador


Registrado: Quarta-Feira, 2 de Fevereiro de 2005
Mensagens: 1366
Localização: São Paulo/SP

MensagemEnviada: Qua Jan 19, 2011 1:41 pm    Assunto: Responder com Citação

o problema é mais simples, esta passando no destroy do TDono depois do TDono.addObserver
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
marcosalles
Moderador
Moderador


Registrado: Quarta-Feira, 26 de Março de 2008
Mensagens: 1695
Localização: Muriaé Mg

MensagemEnviada: Qua Jan 19, 2011 1:55 pm    Assunto: Responder com Citação

rogerbetti escreveu:
o problema é mais simples, esta passando no destroy do TDono depois do TDono.addObserver


mas eu acho que ja esta resolvido , ou ele passa a classe no lugar da interface algo que discordo conceitualmente ( pq tanto a classe obervada quanto aclasse observadora devem se comunicar atraves da interface ) ou ele tira o create da classe observadora TCao e passa a adicionar pelo framwork como foi reportado

Código:
var
    dono:TDono;
    cao:IObserver;
begin
        dono := TDono.create;
        cao := TCao.create();
    try
        dono.addObserver(cao);
        dono.alimentar;
        dono.deleteObserver(cao);
    finally
        FreeAndNil(dono);
    end;

_________________
http://marcosalles.wordpress.com

Desenvolvo FreeLancer e presto Consultoria
Orientação Online DataSnap DbX ClientDataSet
POO , Padrões de Projeto e dúvidas de Delphi em
Geral
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Visitar a homepage do Usuário
edson.amf
Novato
Novato


Registrado: Segunda-Feira, 30 de Março de 2009
Mensagens: 58

MensagemEnviada: Qua Jan 19, 2011 1:56 pm    Assunto: Responder com Citação

exatamente.
Qndo eu faço
Código:

    dono.addObserver(cao);

passa pelo destroy da classe TDono, daí minha lista fObservers fica com nil.
E quando finalmente é executado o notifyObservers
Código:

    for i := 0 to Self.fObservers.Count - 1 do

gera uma excessão pq o fObserves está com nil.

Mas porque isso está acontecendo??? Question [/quote]
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
marcosalles
Moderador
Moderador


Registrado: Quarta-Feira, 26 de Março de 2008
Mensagens: 1695
Localização: Muriaé Mg

MensagemEnviada: Qua Jan 19, 2011 4:52 pm    Assunto: Responder com Citação

Então amigão . Vc nao entendeu qnd eu disse do conceito . Que usar a classe
nao é recomendado pelas biografias . Mas seguindo seu Padraão , utilizando o

Citação:
constructor TCao.create(s: ISubject);
begin
inherited Create;
s.addObserver(Self);
end;


testei aqui utilizando Variaveis como INTERFACES e não como Classes e
aparentemente funcionou

Veja

Código:
unit Unit2;

interface

type
    ISubject = interface;

    IObserver = interface
        ['{AF15A5C6-EB0A-4748-A034-7E2B634EBD7D}']
        procedure update(s: ISubject);
    end;

    ISubject = interface
        ['{85093283-A063-4688-B2D7-D1936D0AED50}']

        procedure addObserver(o:IObserver);
        procedure deleteObserver(o:IObserver);
        procedure notifyObservers();
        procedure Alimentar; //NAO É NECESSÁRIO , MAS EVITARA UM Type Cast
    end;

implementation


end.


Nada Muda na Unit3

Código:
unit Unit3;

interface

uses Unit2, Classes, SysUtils;

type
    TDono = class(TInterfacedObject,ISubject)
    private
      { private declarations }
        fObservers: TInterfaceList;
    protected
      { protected declarations }
    public
      { public declarations }
        constructor create(); overload;
        destructor Destroy(); override;

        procedure addObserver(o:IObserver);
        procedure deleteObserver(o:IObserver);
        procedure notifyObservers();
        procedure alimentar;
    end;

implementation


{ TDono }

procedure TDono.addObserver(o: IObserver);
begin
    Self.fObservers.Add(o);
end;

procedure TDono.alimentar;
begin
    notifyObservers;
end;

constructor TDono.create;
begin
    inherited Create();
    Self.fObservers := TInterfaceList.Create;
end;

procedure TDono.deleteObserver(o: IObserver);
begin
    Self.fObservers.Remove(o);
end;

destructor TDono.Destroy;
begin
    FreeAndNil(Self.fObservers);
    inherited;
end;

procedure TDono.notifyObservers;
var
    i: Integer;
begin
    for i := 0 to Self.fObservers.Count - 1 do
      IObserver(Self.fObservers.Items[i]).update(Self);
end;

end.


Nada Muda na Unit4 ( Versão Original com o Contructor)

Código:
unit Unit4;

interface

uses Unit2;

type
    TCao = class(TInterfacedObject, IObserver)
    private
      { private declarations }
    protected
      { protected declarations }
    public
      { public declarations }
        constructor create(s: ISubject); overload;
        procedure update(s: ISubject);

    end;

implementation


{ TCao }

constructor TCao.create(s: ISubject);
begin
    inherited Create;
     s.addObserver(Self);
end;

procedure TCao.update(s: ISubject);
begin
    {TODO -oOwner -cGeneral : ActionItem}
end;

end.


Alteração na Declaração de Variáveis

Código:
procedure TForm6.Button1Click(Sender: TObject);
var
    dono: ISubject;  // A GRANDE ou pequena mudança
    cao:IObserver; // A GRANDE ou pequena mudança
begin
    try
        dono := TDono.create;
        cao := TCao.create(dono);
        Tdono(dono).alimentar;   //Para não precisar de Tdono(dono).alimentar
        dono.deleteObserver(cao);
    finally
     //
    end;
end;


funcionou perfeitamente . Pelo que eu pude perceber o problema esta na
maneira com o que o delphi utiliza este digamos assim Garbage Collector
Contagem de referencia utilizando Interfaces , misturando variaveis
de Classes com variáveis de interfaces ...Nao Acredito que seje Modelo mas
o modo como o compilador delphi gerencia essas variávels . tanto é que vc
disse que no Java não teve problema
_________________
http://marcosalles.wordpress.com

Desenvolvo FreeLancer e presto Consultoria
Orientação Online DataSnap DbX ClientDataSet
POO , Padrões de Projeto e dúvidas de Delphi em
Geral
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Visitar a homepage do Usuário
edson.amf
Novato
Novato


Registrado: Segunda-Feira, 30 de Março de 2009
Mensagens: 58

MensagemEnviada: Qua Jan 19, 2011 10:05 pm    Assunto: Responder com Citação

exatamente, funcionou certinho.
marcosalles e rogerbetti, Obrigado pela ajuda T+
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
gilsonnrodrigues
Moderador
Moderador


Registrado: Quinta-Feira, 14 de Abril de 2005
Mensagens: 9009
Localização: Governador Valadares-MG

MensagemEnviada: Qui Jan 20, 2011 8:01 am    Assunto: Responder com Citação

mas vcs entenderam o pq do problema de desalocação.

falou-se sobre conceitos, etc.

mas nada tem a ver com nada.

vc herdou de TInterfacedObject, correto?

então.


veja esse código:


Código:
function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;


entenderam agora?

td vez q uma referencia a um objeto q implementa essa interface é liberada da memoria ele verifica se FRefCount é 0 e se for ele cham o destroy da instancia.

um tipo de "garbage collection".

por isso qd vc fez uma referencia q só liberou dps, "resolveu".
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
marcosalles
Moderador
Moderador


Registrado: Quarta-Feira, 26 de Março de 2008
Mensagens: 1695
Localização: Muriaé Mg

MensagemEnviada: Qui Jan 20, 2011 9:45 am    Assunto: Responder com Citação

Citação:
falou-se sobre conceitos, etc.

calma gilsonrodrigues ... Falou do conceito de usar a definição da classe Observadora na classe obervada

Citação:
constructor TCao.create(s: TDono); //ENGESSANDO
begin
inherited Create();
s.addObserver(Self);
end;


Isto conceitualmente esta Errado .. Mas funciona como foi reportado pelo autor
do tópico... Porém do jeito conceitualmente correto (aceito pelo Java)
Citação:
constructor TCao.create(s: ISubject);
begin
inherited Create();
s.addObserver(Self);
end;

O Delphi estava reclamando ... estava perdendo Referencia antes do esperado

Reclamar no sentido que vc reportou

Citação:
td vez q uma referencia a um objeto q implementa essa interface é liberada da memoria ele verifica se FRefCount é 0 e se for ele cham o destroy da instancia.


o problema que para mim , para o autor do tópico , é que ainda existia
Referencia a um Objeto . Isto é , naquele momento FRefCount não deveria ser
Zero , porém o Delphi esta vendo como Zero . Olhando o codigo a olho nu
não da para tomar esta conclusão . Tando que o mesmo funciona em Java

que dizer : As Causa nos conhecemos e concordamos os efeitos ou que o
gerou ainda estão meio indefinidas ou indeterminadas .


Citação:

A Saida : Para manter o Modelo , conceitualmente correto , foi trocado
var
dono:TDono;
cao:Tcao;
para >>>
dono: ISubject; // A GRANDE ou pequena mudança
cao:IObserver; // A GRANDE ou pequena mudança

Dessa forma manteve a definiçao Original do Constructor


Agora que fique claro o seguinte :

Por falta de tempo, prática, ou até mesmo conhecimento, aplicamos muita
vezes uma forma eficaz ( que funciona ), que não está errada, mas deixa a
desejar .

Neste exemplo a Forma "Eficaz" é usar no constructor TCao.create(s: TDono);
Porque deixa a desejar ??? Engessa o Modelo . Define que somente dono vai
Observar Cao . E isto ate funciona mas é desaconselhável
_________________
http://marcosalles.wordpress.com

Desenvolvo FreeLancer e presto Consultoria
Orientação Online DataSnap DbX ClientDataSet
POO , Padrões de Projeto e dúvidas de Delphi em
Geral
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Visitar a homepage do Usuário
gilsonnrodrigues
Moderador
Moderador


Registrado: Quinta-Feira, 14 de Abril de 2005
Mensagens: 9009
Localização: Governador Valadares-MG

MensagemEnviada: Qui Jan 20, 2011 12:51 pm    Assunto: Responder com Citação

eu sei. entendi.

Citação:
o problema que para mim , para o autor do tópico , é que ainda existia
Referencia a um Objeto . Isto é , naquele momento FRefCount não deveria ser
Zero , porém o Delphi esta vendo como Zero


não nenhum problema com o delphi e sim com o TInterfacedObject.

pq ele se baseia no _addRef e no Release q só são acionados qdo atribui e "desatribui" referencias do tipo da interface implementada e não da do tipo da classe implementadora.


se vc não herdar de Tinterfacedobject não teria problema. funcionaria perfeitamente.
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular
marcosalles
Moderador
Moderador


Registrado: Quarta-Feira, 26 de Março de 2008
Mensagens: 1695
Localização: Muriaé Mg

MensagemEnviada: Qui Jan 20, 2011 1:35 pm    Assunto: Responder com Citação

[quote="gilsonnrodrigues"]eu sei. entendi.

Citação:

se vc não herdar de Tinterfacedobject não teria problema. funcionaria perfeitamente.


pode funcionar ou ate mesmo funciona , mas vc perderia algo a meu modo de
ver genial que é exatamente esta contagem de referencia . Pois :
a interface é liberada da memória automaticamente.
"SE E SOMENTE SE a sua classe for descendente de TInterfacedObject. "
Claro que existe a possibilidade de implementar manualmente estes metodos
que fazem a contagem mas teriamos um trabalho a mais nesta implementação
, como exemplo cito a TInterfacedPersistent da propria VCL que não tem
estes métodos

Então a saida foi esta , exatamente definir estas variaveis referenciando esta
interface , assim so no final do método é que se perde a referencia e portanto
o controle de referencia é chamado e destroe estes objetos
_________________
http://marcosalles.wordpress.com

Desenvolvo FreeLancer e presto Consultoria
Orientação Online DataSnap DbX ClientDataSet
POO , Padrões de Projeto e dúvidas de Delphi em
Geral
Voltar ao Topo
Ver o perfil de Usuários Enviar Mensagem Particular Visitar a homepage do Usuário
Mostrar os tópicos anteriores:   
Novo Tópico   Responder Mensagem    ActiveDelphi - Índice do Fórum -> Delphi Todos os horários são GMT - 3 Horas
Página 1 de 1

 
Ir para:  
Enviar Mensagens Novas: Proibido.
Responder Tópicos Proibido
Editar Mensagens: Proibido.
Excluir Mensagens: Proibido.
Votar em Enquetes: Proibido.


Powered by phpBB © 2001, 2005 phpBB Group
Traduzido por: Suporte phpBB