Exibir mensagem anterior :: Exibir próxima mensagem |
Autor |
Mensagem |
edson.amf Novato
Registrado: Segunda-Feira, 30 de Março de 2009 Mensagens: 58
|
Enviada: Qua Jan 19, 2011 8:29 am Assunto: Padrão Observer [RESOLVIDO] |
|
|
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 |
|
|
rogerbetti Colaborador
Registrado: Quarta-Feira, 2 de Fevereiro de 2005 Mensagens: 1366 Localização: São Paulo/SP
|
Enviada: Qua Jan 19, 2011 9:34 am Assunto: |
|
|
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 |
|
|
edson.amf Novato
Registrado: Segunda-Feira, 30 de Março de 2009 Mensagens: 58
|
Enviada: Qua Jan 19, 2011 10:33 am Assunto: |
|
|
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 |
|
|
rogerbetti Colaborador
Registrado: Quarta-Feira, 2 de Fevereiro de 2005 Mensagens: 1366 Localização: São Paulo/SP
|
Enviada: Qua Jan 19, 2011 10:43 am Assunto: |
|
|
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 |
|
|
marcosalles Moderador
Registrado: Quarta-Feira, 26 de Março de 2008 Mensagens: 1695 Localização: Muriaé Mg
|
Enviada: Qua Jan 19, 2011 12:15 pm Assunto: |
|
|
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 |
|
|
edson.amf Novato
Registrado: Segunda-Feira, 30 de Março de 2009 Mensagens: 58
|
Enviada: Qua Jan 19, 2011 1:00 pm Assunto: |
|
|
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 |
|
|
rogerbetti Colaborador
Registrado: Quarta-Feira, 2 de Fevereiro de 2005 Mensagens: 1366 Localização: São Paulo/SP
|
Enviada: Qua Jan 19, 2011 1:41 pm Assunto: |
|
|
o problema é mais simples, esta passando no destroy do TDono depois do TDono.addObserver |
|
Voltar ao Topo |
|
|
marcosalles Moderador
Registrado: Quarta-Feira, 26 de Março de 2008 Mensagens: 1695 Localização: Muriaé Mg
|
Enviada: Qua Jan 19, 2011 1:55 pm Assunto: |
|
|
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 |
|
|
edson.amf Novato
Registrado: Segunda-Feira, 30 de Março de 2009 Mensagens: 58
|
Enviada: Qua Jan 19, 2011 1:56 pm Assunto: |
|
|
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??? [/quote] |
|
Voltar ao Topo |
|
|
marcosalles Moderador
Registrado: Quarta-Feira, 26 de Março de 2008 Mensagens: 1695 Localização: Muriaé Mg
|
Enviada: Qua Jan 19, 2011 4:52 pm Assunto: |
|
|
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 |
|
|
edson.amf Novato
Registrado: Segunda-Feira, 30 de Março de 2009 Mensagens: 58
|
Enviada: Qua Jan 19, 2011 10:05 pm Assunto: |
|
|
exatamente, funcionou certinho.
marcosalles e rogerbetti, Obrigado pela ajuda T+ |
|
Voltar ao Topo |
|
|
gilsonnrodrigues Moderador
Registrado: Quinta-Feira, 14 de Abril de 2005 Mensagens: 9009 Localização: Governador Valadares-MG
|
Enviada: Qui Jan 20, 2011 8:01 am Assunto: |
|
|
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 |
|
|
marcosalles Moderador
Registrado: Quarta-Feira, 26 de Março de 2008 Mensagens: 1695 Localização: Muriaé Mg
|
Enviada: Qui Jan 20, 2011 9:45 am Assunto: |
|
|
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 |
|
|
gilsonnrodrigues Moderador
Registrado: Quinta-Feira, 14 de Abril de 2005 Mensagens: 9009 Localização: Governador Valadares-MG
|
Enviada: Qui Jan 20, 2011 12:51 pm Assunto: |
|
|
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 |
|
|
marcosalles Moderador
Registrado: Quarta-Feira, 26 de Março de 2008 Mensagens: 1695 Localização: Muriaé Mg
|
Enviada: Qui Jan 20, 2011 1:35 pm Assunto: |
|
|
[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 |
|
|
|