POO: Objetos quot;Auto-instanciáveisquot;
Primeiramente, quero colocar que criar objetos na inicialização da aplicação é algo que deve ser feito com critério, uma vez que memória é alocada consumindo recursos, isto deve ser realizado apenas quando necessário (para objetos que realmente precisamos ´instanciados´ desde sempre)
Geralmente quando precisamos destes objetos sem ter que nos preocupar com sua inicialização, estes são objetos ´centrais´ que possuem uma única instância para ser utilizada por toda aplicação. Por isso, e apenas para estes casos, posto a seguir um código exemplo utilizando o padrão de projeto Singleton.
Nesta implementação o objeto não é ´instanciado´ na seção Initialization, mas sim APENAS quando for usado pelo primeira vez, em qualquer parte da aplicação. Vamos ao código:
unit MeuObjeto; uses SysUtils, Classes; type TMeuObjeto = class (TObject) private fNome: string; fLista: TStrings; public constructor Create; destructor Destroy; override; class function Instance: TMeuObjeto; property Nome: string read fNome write fNome; property Lista: TStrings read fLista; end; function MeuObjeto: TMeuObjeto; implementation var InstanciaMeuObjeto: TMeuObjeto = nil; function MeuObjeto: TMeuObjeto; begin Result := TMeuObjeto.Instance; end; constructor TMeuObjeto.Create; begin inherited Create; fLista := TStringList.Create; fNome := ´´; end; destructor TMeuObjeto.Destroy; begin fLista.Free; inherited; end; class function TMeuObjeto.Instance: TMeuObjeto; begin if InstanciaMeuObjeto=nil then InstanciaMeuObjeto := TMeuObjeto.Create; Result := InstanciaMeuObjeto; end; initialization finalization if InstanciaMeuObjeto<>nil then InstanciaMeuObjeto.Free; end.
Comentários sobre o código:
Note que foi criada uma FUNÇÃO como ponto de acesso global a instância do objeto e não uma variável. Isto garante maior poder e controle sobre a inicialização e destruição da mesma. Exemplo: Mesmo que um programador ´desavisado´ em determinado momento chame:
MeuObjeto.Free;
na próxima chamada de MeuObjeto ele estará lá novamente (uma nova instância, claro -- mas não haverá uma exceção de violação de acesso de memória)
Mas principalmente, função permite que o objeto seja criado APENAS quando for chamado pela primeira vez (e não na inicialização do programa, como ocorre com código da inicialization)
Outro ponto importante de notar é que a ´variável´ que contém a instância de nosso objeto fica em IMPLEMENTATION garantindo assim que nenhuma outra unit possa acessá-la diretamente (garantindo encapsulamento)
Ainda, a criação do método de classe ´Instance´ é opcional. Seu código poderia estar direto na função de entrada ´MeuObjeto´, caso não desejasse ter o método de classe. No exemplo acima:
TMeuObjeto.Instance.Nome := ´Teste´;
é igual a
MeuObjeto.Nome := ´Teste´;
Por fim, é importante notar que o destructor da classe foi re-escrito para destruir o objeto fLista criado no constructor, e que a seção finalization foi escrita para garantir que uma vez que InstanciaMeuObjeto tenha sido criado, seja destruído ao final da aplicação.
Vale salientar que este mesmo padrão pode ser implementado para Forms e DataModules (ou qualquer outra classe).
Vou ficando por aqui, esperando ter sido útil de alguma forma. Abraços a todos.
Anderson
Afarias
Respostas
Osocram
07/09/2009
Não tenho o costume de usar o Initialization e o Finalization para instanciar ou destruir objetos, E sim para registrar Classes.
Mas assim que minha revista chegar, vou dar uma olhada no artigo p discutir melhor.
Afarias
07/09/2009
Osocram
07/09/2009
Recebi hj a minha revista, apenas com alguns dias de atraso.
Bom particularmente não gosto desse tipo de abordagem.
O que geralmente gosto, e estou tentando fazer nos sistema é que cada Objeto meu saiba se criar e se destruir.
Por exemplo meu sistema é MDI, mas as vezes tenho que usar alguns form SDI (não MDI) então qdo é assim eu criei uma classe no qual fiz uma ´Método de Classe´ geralmente .Execute; no qual faz o create do form e como é showmodal logo apos isso ele mesmo ja se destroi.
Ficando apenas assim a chamada do mesmo.
TMeuFormSDI.Execute;
Mas se for ver apenas estamos aplicando diferentemente os mesmos recursos que eles mostraram na materia.
Aquilo que colocaram deve ser usado com cautela. eu so vi uma aplicação para aquilo, até o momento, que seria para criar uma classe de usuario no sistema que ao logar eu guardaria todos os privilegios.
Mas nada impediria de eu criar isso no braço tbm usando um metodo Execute.
Afarias
07/09/2009
|recursos que eles mostraram na materia.
Não vejo bem por ai...
|Aquilo que colocaram deve ser usado com cautela.
Na minha modesta opnião, não deve ser usado de jeito nenhum. Além do problema conceitual, os códigos exemplo estão cheios de falhas como memory leaks, etc.
|eu so vi uma aplicação para aquilo, até o momento, que seria para criar
|uma classe de usuario no sistema que ao logar eu guardaria todos os
|privilegios.
Para isso servem os Singletons como no exemplo q mostrei.
A implementação de o singleton que dei de exemplo pode até não parecer muito diferente do código da matéria, mas tem detalhes q dão uma diferença muito grande se observados com cuidado -- tanto a nível conceitual quanto de implementação.
|Mas nada impediria de eu criar isso no braço tbm usando um metodo
|Execute.
Gosto desta abordagem e tb utilizo. Mas existem classes q são manipuladas, trocam informações, etc, onde esta abordagem não se aplica.
Neste caso que vc citou, vc cria um método de classe q encapsula uma ação completa -- criação da instancia, execução de uma tarefa e destruição!
Isto vai além da questão de inicialização de objetos (como uso de padrões singleton, factories, etc) -- e o método é específico para uma tarefa.
T+
Wdrocha
07/09/2009
E para que os colegas do fórum entendam melhor o q ele falow sobre o Singleton, ele é um padrão de projeto utilizado quando queremos apenas uma única instância de algum objeto.
Este é apenas um dos muitos padrões utilizados hj no mercado, existem mts outros, quem não conhece seria uma boa pesquisar e ler um pouco sobre Padrões de Projetos.
fica aew a dica...
flw
Marco Salles
07/09/2009
Afarias ,aonde vc ´viu´ memory Leaks nos códigos ???
Marco Salles
07/09/2009
Afarias ,aonde vc ´viu´ memory Leaks nos códigos ???
Knight_of_wine
07/09/2009
Inclusive utilizo uma classe de conexão Singleton muito boa que um amigo criou, assim posso usar minha conexão apenas quando necessito dela.
Isso aumentou a performance de um de meus sistemas em 45¬, algo incrível.
Achei muito massa a iniciativa de discutir um tópico da revista aqui.
Afarias
07/09/2009
Rocha disse tudo.
[quote:bfe68019b8=´Marco Salles´]
Afarias ,aonde vc ´viu´ memory Leaks nos códigos ???
[/quote:bfe68019b8]
1) Nas listagem 3 é criado um construtor para a classe que cria um objeto TStringList que NUNCA é liberado da memória pq não é criado um Destructor que teria esse papel. No texto inclusive é afirmado que o destructor não é necessário!! MAS É!
A não ser que vc utilize interfaces (ref-counted) ou um memory manager habilitado com um garbage collector os objetos criados não são destruídos automaticamente.
2) Nas listagem 5, exemplo com DM, ainda pior... na seção finalization o DataModule NÃO é destruído, apenas ´apontado´ para NIL!
finalization
DMTeste := nil;
Neste caso o ponteiro aponta para nil e a memória que era usada pelo objeto continua sendo utilizada... No texto é alegado que todos os DataModules de um projeto são destruidos ao com o témino da aplicação. Mas está incorreto...
São destruidos automaticamente apenas DataModules (assim como os Forms) criados tendo o objeto Application (ou outro qualquer que seja destruído em algum momento) como OWNER. Sendo que no código o Datamodule em questão NÃO tem um Owner.
DMTeste := TDMTeste.Create(nil);
Rsss.. Não achei outro espaço para isso... Achei q era aqui mesmo :wink:
T+
Marco Salles
07/09/2009
1) Nas listagem 3 é criado um construtor para a classe que cria um objeto TStringList que NUNCA é liberado da memória pq não é criado um Destructor que teria esse papel. No texto inclusive é afirmado que o destructor não é necessário!! MAS É!
A não ser que vc utilize interfaces (ref-counted) ou um memory manager habilitado com um garbage collector os objetos criados não são destruídos automaticamente.
2) Nas listagem 5, exemplo com DM, ainda pior... na seção finalization o DataModule NÃO é destruído, apenas ´apontado´ para NIL!
finalization
DMTeste := nil;
Neste caso o ponteiro aponta para nil e a memória que era usada pelo objeto continua sendo utilizada... No texto é alegado que todos os DataModules de um projeto são destruidos ao com o témino da aplicação. Mas está incorreto... [/quote:0c834bbb29]
Afarias , ja tinha percebido uma das falhas e tinha ate reportado o erro em um
email em particular para o autores e um deles gentilmente me respondeu
Como Resposta Obtive que a partir do DataModule foi INVENÇÃO do Pessoal
da Revista ..( Que estã precisando Aprender Delphi )
Agora gostaria de ser solidário aos Autores e isto não encaro como falta .
Errar todos nos erramos . Cometemos Erros e deslizes . Nos aprendemos muito
mais com os erros do que com os Acertos .
O que não podemos aqui em hipotese nenhuma é tomar este Artigo como
o Vilão da História . Vários são os Artigos que podemos considerar como
ruims e ate hj não tomou proporções como a que este esta tomando.
Gostaria de respeitar os autores , e se escrerem outro certamente eu os
lerei , mesmo que seja para critica-los ou não.
A Culpa no meu entender é a falta de um Intercambio mais eficiente entre a Revista e O Forum .
Afarias
07/09/2009
Veja, esta é uma revista TÉCNICA ao meu ver, e revistas técnicas (e PAGAS) devem apresentar matérias técnicas CORRETAMENTE ou não tem sentido.
Claro q falhas existem, mas tanto autores quanto principalmente os editores técnicos da revista deveriam estar mais atentos ou fica a impressão *no mínimo* de falta de atenção com o leitor.
Mas, voltando...
Meu objetivo com este tópico nem foi criticar -- isto eu fiz em e-mail enviado a REVISTA (não aos autores) q nunca foi respondido. Nem citei autores ou editores...
Meu meu tópico original apenas apresenta uma técnica em alternativa ao que fora colocado na matéria em questão -- na esperança que iniciantes desavisados fiquem atentos.
T+
Weber
07/09/2009
O artigo ´Objetos Auto-instanciáveis´ veio a existir a partir de uma discussão que tivemos sobre classes que criei para facilitar o trabalho da equipe de desenvolvimento, entre estas classes por exemplo tenho uma que chamo de TDBUpdate, os métodos da classe podem ser chamados a qualquer momento de qualquer lugar do sistema sem se preocupar se ela foi instanciada ou não.
Ao meu ver foi uma infelicidade o exemplo aplicado no artigo e não o artigo como um todo.
Vejo tudo como afarias já colocou, a classe deve ser instanciada quando existir realmente e necessidade e não no Initialization, no meu ponto de vista usando o Initialization não houve “auto-instancia”.
Veja abaixo parte de um código com “auto-instancia”
{ TDBUpdate } procedure TDBUpdate.CheckCreate; begin if not Assigned(DBUpdate) then DBUpdate := TDBUpdate.Create(nil); end; procedure TDBUpdate.TableCreate(Table: ShortString; Fields: array of ShortString); begin CheckCreate; ... end;
A conversa pode ir muito mais além, um colega meu certificado em java uma vez me disse que eu era louco que um código deste jamais poderia existir.
Afarias
07/09/2009
rssss... estou com esse seu colega :wink:
T+
Knight_of_wine
07/09/2009
Ninguém é dono da verdade, mas podemos sim contribuir para que o pessoal aprimore os artigos e melhorem cada vez mais o conteúdo que pagamos pra ter.
Afarias
07/09/2009
Na minha opinião, não poderia ter dito melhor.
T+
Vitor Rubio
07/09/2009
Usa-se muito singleton na aplicação de abstract factory. Ou seja, quando temos uma fábrica de qualquer outro tipo de classe, a fabrica em si so pode ter uma instancia.
A abordagem do afarias é muito boa, eu mesmo já usei muito. Já usei com function, com class function e com um misto dos dois, como o AFarias fez, s sempre colocando uma variavel estatica na seção implementation, para que ela não seja publica global. Ainda tenho o costume de iniciar o nome da variavel com ´__´ para não ter perigo de mecher nela sem querer.
Mas esse tipo de abordagem tem um defeitinho: se o programador desavisado usar o constructor create você acaba tendo duas instancias dessa classe e ela não é mais um singleton. Alem disso o programador desavisado pode esquecer de destruir a instancia dele causando um memory leak, ou pior, ele pode chamar o destroy do objeto singleton, a partir da função, e destruir a unica instancia da aplicação inteira, sem atribuir nil a variavel, causando um monte de acess violations.
Eu conheço umas duas outras maneiras de criar um singleton seguro no Delphi, inclusive thread-safe. Não são dicas de minha autoria, encontrei em sites e blogs de autores bem melhores do que eu, em inglês. Só dei uma ajeitada e uma comentada em português no código pra ficar mais didático.
Não é um código difícil de entender, mas é daqueles que mostram a criatividade do cara que fez, porque você bate o olho e diz: ´Porque eu não pensei nisso antes???´
Assim que eu conseguir acessar o computador da minha casa eu posto os códigos aqui.
Com relação ao artigo, estou com ele aqui mas ainda não li. Seria interessante dar um feedback ao autor ou à edição no próprio site da dev media.
Acredito que com toda essa polêmica com certeza será lançada numa edição futura ou mesmo no site uma errata ou coisa do gênero.
Pessoal, um apelo: muito cuidado com os memory leaks. Memory Leaks fazem as pessoas mudar de C++ ou Delphi para ambientes gerenciados e interpretados com garbage collector, como Java, C#, ou python, simplesmente porque chega uma hora que não conseguem entender onde está o leak.
Afarias
07/09/2009
Isso pode ser evitado, se desejado. Só não coloquei no exemplo pra não ´poluir´. Ex:
implementation var fInstanciaMeuObjeto: TMeuObjeto = nil; fPermiteInstanciar: Boolean = False; {...} class function TMeuObjeto.Instance: TMeuObjeto; begin if fInstanciaMeuObjeto=nil then begin fPermiteInstanciar := True; try fInstanciaMeuObjeto := TMeuObjeto.Create; finally fPermiteInstanciar := False; end; end; Result := fInstanciaMeuObjeto; end; constructor TMeuObjeto.Create; begin inherited Create; fLista := TStringList.Create; fNome := ´´; if not fPermiteInstanciar then raise Exception.Create(´não é possível criar este objeto´); end;
A instância do Singleton?? Não há esse perigo pq sempre é destruída pelo FINALIZATION
Rapaz... dar um jeito de avisar esse desavisado seria a solução mais fácil.. rsss :wink:
Bom, mas isso tb pode ser evitado de diversas formas (numa classe mais trabalhada). Um exemplo seria modificando o Destructor incluindo depois do ´inherited´ a linha
InstanciaMeuObjeto := nil;
dai, vc poderia fazer:
MeuObjeto.Free; MeuObjeto.Free; MeuObjeto.Free; MeuObjeto.Nome := ´Teste 1´; MeuObjeto.Free; MeuObjeto.Free; MeuObjeto.Nome := ´Teste 2´;
E nunca haver erros.
Uma vez q a implementação do singleton utiliza um método/função de entrada, vc pode ´incrementar´ como desejar.
Com certeza. A primeira coisa q fiz.
Polêmica? Q polêmica?? :lol:
T+
Vitor Rubio
07/09/2009
quando eu disse
me referia a uma instancia que ele mesmo criasse.
Agora liga só nesse singleton:
A classe Tobject tem uma class function chamada NewInstance e uma procedure chamada FreeInstance. Essas duas são as responsáveis por criar ou destruir um objeto DE VERDADE. Por exemplo, sempre o constructor create chama new instance internamente, e o destructor destroy chama freeinstance.
Pode criar um override dessas duas com um showmessage que você vai ver.
Então.... você pode criar um override dessas duas para controlar a criação e destruição do seu singleton. E não preciasa usar uma function ou método especial para isso, você pode usar o constructor create mesmo, na maior cara de pau. O Create, ao invez de te dar uma nova instancia, vai dar a padrão que já estava criada.
A mesma coisa se vc ficar chamando free ou destroy, ele não vai destruir de verdade a não ser que você chame PrepararParaDestruir antes.
Assim você não precisa disparar uma exception se alguém criar ou destruir um singleton. Isso é bom para equipes muito grandes ou venda de bibliotecas pre compiladas.
Esse tipo de singleton não pode ter descendentes, a não ser que varias coisas sejam reescritas, porque senão o descendente será instanciado na variavel publica do ancestral, e o destroy não vai destruir e desalocar objetos especificos do descenente, só do ancestral. (a não ser que você refaça boa parte do descendente)
Agora, sebe essas duas variaveis publicas que você criou? uma para conter ainstancia e outra para dizer se pode destruir? Eu tambem uso assim, mas num delphi 2007 ou posterior que você pode criar variaveis de classe e propriedades de classe fica muito mais show de bola, você não usa variaveis na implementation e ainda pode gerar descendentes do seu singleton que vão funcionar uma belezinha.
Eu ainda ponho o método para preparar para destruir como privado e estático, uma private class procedure. Assim eu só posso chamar ela da propria unit, apenas na seção finalization. Assim eu não inicializo minha aplicação com o singleton criado. Crio ele ao longo do uso da aplicação apenas se precisar e mato sua unica instancia quando finalizo a aplicação.
{ essa unit tem uma classe que implementa o padrão singleton, ou seja, apenas uma instancia desta pode existir. você não pode dar free nesse objeto, mas ele é liberado da memoria automaticamente atraves da seção finalization da unit, executando o metodo privado estatico (a unica utilidade que eu já vi para metodo privado estatico) class procedure PrepararParaLiberar; Depois disso o objeto pode ser destruido normalmente Essa classe não pode ter descendentes. Se tiver, a variavel global que guarda a instancia do singleton conterá apenas uma instancia, ou da classe base ou da classe especializada. Ao ser liberada da memoria o metodo destroy da classe herdada não conseguirá liberar objetos criados e usados somente por esta classe } unit uSingleton; interface uses Dialogs, Classes; type TMySingleton = class(TObject) private FHello: TstringList; //se executar isso o objeto pode ser destruido class procedure PrepararParaLiberar; //para criar ou destruir os componentes do objeto procedure InicializarObjeto; procedure FinalizarObjeto; public procedure SetHello(vHello: string); procedure SayHello; virtual; //esses caras misticos abaixo que realmente criam, alocam memoria, destroem e desalocam memoria por tráz dos constructor e destructor que conhecemos class function NewInstance: TObject; override; procedure FreeInstance; override; constructor Create; destructor Destroy; override; class function InstanciaPadrao: TMySingleton; end; implementation var _MySingletonInstance: TMySingleton = nil; _PreparadoParaLiberar: Boolean = False; { TMySingleton } procedure TMySingleton.SayHello; begin //um metodo bobo pra testar ShowMessage(FHello.Text); end; procedure TMySingleton.SetHello(vHello: string); begin //um outro metodo bobo pra setar a mensagem FHello.Text := vHello; end; constructor TMySingleton.Create; begin //antes de tudo, antes mesmo do inherites, newinstance já é chamado por padrão inherited; //faz o que for preciso de seu ancestral, eu tenho certeza aqui que o NewInstance está sendo executado InicializarObjeto; //inicializo o que precisa end; destructor TMySingleton.Destroy; begin FinalizarObjeto; //destruo as partes ou objetos criados pela minha classe, como stringlists inherited; //a destruição normal do objeto, depois disso freeinstance é chamado normalmente end; class function TMySingleton.InstanciaPadrao: TMySingleton; begin //isso é apenas um atalho em uma class function Result := _MySingletonInstance; end; procedure TMySingleton.FreeInstance; begin //no destructor não vai acontecer nada se _PreparadoParaLiberar for false, e eu não preciso disparar uma excessão // agora se _PreparadoParaLiberar for true //eu faço o que um FreeInstance sempre deveria fazer, uso o inherited, if _PreparadoParaLiberar then begin inherited; //bloqueio a liberação novamente _PreparadoParaLiberar := False; //atribuo nil _MySingletonInstance := nil; end; //agora se precisar pode criar de novo end; class function TMySingleton.NewInstance: TObject; begin //se não está criada cria usando o inherited NewInstance da classe ancestral, TObject e atribui fazendo typecasting if _MySingletonInstance = nil then begin _MySingletonInstance := (inherited NewInstance as TMySingleton); end; //e retorna a mesma result := _MySingletonInstance; end; class procedure TMySingleton.PrepararParaLiberar; begin //esse método só faz isso _PreparadoParaLiberar := True; end; procedure TMySingleton.FinalizarObjeto; begin //aqui você poe somente as coisas que devem acontecer da destruição verdadeira do objeto if _PreparadoParaLiberar then begin FHello.Free; end; end; procedure TMySingleton.InicializarObjeto; begin //aqui você poe tudo o que precisa que aconteça depois do create //lembrando que se o NewInstance já retornar o objeto criado, então //Self.FHello vai ser o Fhello dessa instancia, e vai ser <> de nil if (FHello = nil) then begin FHello := TStringList.Create; end; end; initialization //inicializo minhas variáveis publicas, porque vou mecher nelas posteriormente _MySingletonInstance := nil; _PreparadoParaLiberar := False; finalization if Assigned(_MySingletonInstance) or (_MySingletonInstance <> nil) then try try TMySingleton.PrepararParaLiberar; _MySingletonInstance.Free; except //tratamento de excesão, se precisar end; finally _MySingletonInstance := nil; end; end. { faça o teste derradeiro: coloque um button na form e: procedure TForm1.Button9Click(Sender: TObject); var fmst: TMySingleton; begin fmst := TMySingleton.Create; fmst.SetHello(´teste1´); fmst.SayHello; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst := TMySingleton.Create; fmst.SetHello(´teste2´); fmst.SayHello; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Free; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.Destroy; fmst.SetHello(´teste3´); fmst.SayHello; end; }
Afarias
07/09/2009
Só um detalhe falho no código apresentado é que o utilizador da classe obrigatoriamente deve chamar o construtor antes de puder usá-la (se antes disso tentar chamar ´InstanciaPadrao´ ocorrerá violação de memória)
Como, na minha opinião, não deve haver necessidade de chamar o construtor, deve-se rever o código de ´InstanciaPadrao´
T+
Marco Salles
07/09/2009
é mais ai ´acho´ que pode usar a ´sua estrategia´
type TMySingleton = class(TObject) private bla bla ... public bla ... bla;; // class function InstanciaPadrao: TMySingleton; ********* Aqui end; function InstanciaPadrao: TMySingleton;
E alterando um Pouco o método ... chamando o Create...
class function TMySingleton.NewInstance: TObject; begin //se não está criada cria usando o inherited NewInstance da classe ancestral, TObject e atribui fazendo typecasting if _MySingletonInstance = nil then begin _MySingletonInstance := (inherited NewInstance as TMySingleton).Create; end; //e retorna a mesma result := _MySingletonInstance; end;
Acho que resolve ...
Afarias
07/09/2009
class function TMySingleton.InstanciaPadrao: TMySingleton;
begin
Result := TMySingleton.Create;
end;
É uma forma de resolver esse ´problema´, visto que Create sempre retorna a mesma instância
=)
T+
Vitor Rubio
07/09/2009
O objetivo era que se usasse o Create mesmo, o Create ia criar a primeira instancia quando chamado a primeira vez, mas ia ser o metodo que chamasse a instancia padrão sempre, ou seja, poderia chamar TMySingleton.Create().SayHello, por exemplo. só que ....
... então o ´InstanciaPadrao ´ era desnecessário.... mas eu introduzi um bug quando inseri ele....
Valew pela dica.
Dá pra resolver assim:
class function TMySingleton.InstanciaPadrao: TMySingleton; begin //isso é apenas um atalho em uma class function Result := TMySingleton.Create; end;
Mas, como sabemos, Create é um método, que chama internamente varios outros métodos, empilhando os endereços de retorno das funções no stack e causando um pequeníssimo overhead.
Seria elegante criar um misto com a função tradicional do Afarias assim:
class function TMySingleton.InstanciaPadrao: TMySingleton; begin //isso é apenas um atalho em uma class function if _MySingletonInstance = nil then _MySingletonInstance := TMySingleton.Create; Result := _MySingletonInstance ; end;
Acho que não é uma repetição de concreta de código, na cara lavada, é meio que uma repetição abstrata, mais um artificio de documentação, porque ele é muito parecido com o proprio método NewInstance em sua estrutura, mas encapsula e dá mais um nivel de indireção na chamada do método. Ele torna claro qual é o real objetivo desse método, que não é criar varias vezes ou criar sempre, mas criar apenas quando não está criado.
Diminui um pouco o overhead do stack porque quando já está criado retorna a instancia direto ao invés de chamar o create e empilhar mais coisas no stack.
Só um loop muito grande agora pra dizer se ganhamos um minimo de performance com isso.
Vitor Rubio
07/09/2009
Código:
class function TMySingleton.NewInstance: TObject;
begin
//se não está criada cria usando o inherited NewInstance da classe ancestral, TObject e atribui fazendo typecasting
if _MySingletonInstance = nil then
begin
_MySingletonInstance := (inherited NewInstance as TMySingleton).Create;
end;
//e retorna a mesma
result := _MySingletonInstance;
end;
Acho que resolve .....[/quote:782ad5990b]
Marco, não faz isso porque o NewInstance é ´o verdadeiro create por trás do create´.
Se você fizer isso, veja bem, o newinstance já te traz uma instancia do objeto. Aí você vai acabar chamando o constructor create a partir de uma instancia...
Como Create nesse caso tá trazendo a mesma instancia padrão, funciona, mas você chama create desnecessariamente e fica estranho.
Se não fosse um singleton você acabaria criando duas instancias, uma delas sem referencias, sem que você possa destruir, causando um memory leak.
E se você esquecer o inherited, causa um loop infinito recursivo.
Marco Salles
07/09/2009
Se não fosse um singleton você acabaria criando duas instancias, uma delas sem referencias, sem que você possa destruir, causando um memory leak. [/quote:b3cf6c9277]
Minha resposta foi de prontidão , conhecendo estes ´riscos´ . Apesar de
estranho , apliquei ao seu ´Modelo´ , levando em conta a sugestão de
Afarias , so que simplesmente tb deve resolver.
function InstanciaPadrao: TMySingleton; begin if _MySingletonInstance = nil then _MySingletonInstance := TMySingleton.Create; Result := _MySingletonInstance end;
Mas acredito que suas recomendações tb se aplica neste Modelo.
(por se tratar de um Singleton , etc...
So não entendo porque vc ainda esta usando function InstanciaPadrao
como Método de classe , as chamadas no meu entendimento foge
do modelo proposto do Afarias
TMySingleton.InstanciaPadrao.SetHello(´oi´); //usando metodo de classe InstanciaPadrao.SetHello(´oi´); //Declarando um Função
Afarias
07/09/2009
|varios outros métodos, empilhando os endereços de retorno das funções
|no stack e causando um pequeníssimo overhead.
Acredito que não Vitor.
Quem trata da parte de memória no Create é o NewInstance (que por sua vez chama o InitInstance). Uma vez que vc re-escreveu o NewInstance para verificar se existe já uma instância não haverá nova alocação de memória ou qualquer ´overhead´ ao chamar o Create várias vezes. (que não seja o do código q está em NewInstance de qualquer forma)
|So não entendo porque vc ainda esta usando function InstanciaPadrao
|como Método de classe , as chamadas no meu entendimento foge
|do modelo proposto do Afarias
Não foge marcos. Vc tanto pode usar um método de classe E/OU uma função. Como vc disse a função deixa mais prático (uma espécie de ´alias´), dai é só criá-la:
function MeuSingleton: TMeuSingleton;
begin
Result := TMeuSingleton.InstanciaPadrao;
end;
Os 2 exemplos de implementação são muito semelhantes, como pequenos detalhes de implementação apenas. Até pq o Singleton é um padrão de projeto muito simples, não tem muito o que inventar.
Eu particularmente prefiro a abordagem q exemplifiquei pq acho mais simples, fácil de ler e implementar.
Alguns comentários (detalhes) ainda sobre o codigo ´finalization´:
if Assigned(_MySingletonInstance) or (_MySingletonInstance <> nil) then
esse OR não precisa, pois Assigned(Obj) é igual a Obj<>nil
try ... finally _MySingletonInstance := nil; end;
esse [ try... finally Obj := nil ] no finallization tb é desnecessário uma vez q vc nunca mais precisa saber se o objeto é NIL depois disso.
T+
Vitor Rubio
07/09/2009
Eu costumo fazer com class method simplesmente por estilo e organização. Eu não tenho uma função global, tenho que achar a classe e fazer classe.metodo. O resto dá no mesmo.
Como eu disse, não estou falando de overhead de alocação de memoria, mas overhead de empilhamento de metodos no stack.
Por exemplo, digamos que o metodo4 instancia um objeto, alocando memoria. (ou nem faz isso e faz qualquer outra coisa, tanto faz)
Se o metodo1 chamar o metodo2 que por sua vez chamar o metodo3 que chamar o metodo4 eu empilho muito mais endereços de retorno de metodos no stack do que simplesmente chamar o metodo4 direto. Foi isso o que eu quis dizer. Porque InstanciaPadrao chama create, que chama NewInstance, que chama InitInstance.... e assim vai. Podemos economizar essa chamada verificando a variavel.
Isso é vicio hauahauahuahau, não tem jeito, eu não aprendo
Também é outro vicio... mas aqui por questões de clareza para mim mesmo.
E acredito que se for uma bpl carregada dinamicamente precisamos fazer isso, ou estou enganado?
Afarias
07/09/2009
Tb não é necessário
T+
Marco Salles
07/09/2009
Mas qnd me referi a ´foge´ , não foi mereferindo ao Modelo propriamemte dito.
mas sim a ´Chamada´
é isto que estava me referindo.... Deixar mais prático
Fabriciocolombo
07/09/2009
A principal diferença é que utiliza uma Interface como forma de acesso ao Singleton, eliminando a necessidade de controle de Criação e Destruição da Classe. Dessa forma é impossivel um desenvolvedor desavisado criar uma nova instancia ou destruir a instância.
unit uSingleton; interface uses SyncObjs, Dialogs; type {*----------------------------------------------------------------------------- Interface que define os métodos do Singleton -------------------------------------------------------------------------------} ISingleton = interface [´{93E92DC7-BF57-4BD5-9ACF-B69E75EC37D0}´] procedure FacaAlgo; end; {*----------------------------------------------------------------------------- Retorna a instância de um objeto que implementa a interface ISingleton. -------------------------------------------------------------------------------} function Singleton: ISingleton; implementation var _Singleton: ISingleton = nil; type {*----------------------------------------------------------------------------- Classe interna que implementa a interface ISingleton. -------------------------------------------------------------------------------} TSingleton = class(TInterfacedObject, ISingleton) private public procedure FacaAlgo; end; function Singleton: ISingleton; begin if not Assigned(_Singleton) then //Aqui poderia ter um Critical Section para ficar thread-safe _Singleton := TSingleton.Create as ISingleton; Result := _Singleton; end; { TSingleton } procedure TSingleton.FacaAlgo; begin ShowMessage(´Estou fazendo algo...´); end; end.
Afarias
07/09/2009
Esta implementação é muito interessante... (apesar de ter que definir a interface a implementar a classe). Mas ainda me surgem 2 pequenas observações:
1) O desenvolvedor ´desavisado´ deve entender de interfaces, pq não é difícil para um ´desavisado´ bagunçar o sistema de ´reference counting´ que as interfaces usam como ´garbage collect´ no Delphi e ai ter em mãos erros e memory leaks que nem sabe de onde vem.
2) Com esta abordagem o desenvolvedor não pode implementar um Form ou DataModule como Singleton (já que a definição destes não pode ser feita em ´implementation´)
T+
Vitor Rubio
07/09/2009
A maior diferença está no uso da interface. A variavel local _Singleton e a função
function Singleton: ISingleton; begin if not Assigned(_Singleton) then //Aqui poderia ter um Critical Section para ficar thread-safe _Singleton := TSingleton.Create as ISingleton; Result := _Singleton; end;
são do tipo ISingleton.
Podemos fazer uma junção das duas abordagens.
E assim não precisa se preocupar com a destruição do objeto.
Existem 1000 maneiras de preparar singleton!
Fabriciocolombo
07/09/2009
Também é possivel fazer apenas com a classe, como exemplos postados, já utilizei essa abordagem tbm, mais precisa fazer o controle nos metodos NewInstance e FreeInstance. É um pouco mais trabalhoso, mais também funciona perfeitamente.
.lg.
07/09/2009
Precisei dar uma lida e estudar um pouco para me inteirar e poder argumentar.
Mas tenho uma dúvida. que se daria para ser feito. Fiquei imaginando uma maneira de faze-lo. Não sei se estou pensando de forma complicada ou se poderia ser mais simples.
Eu poderia implementar uma Factory sobre um Singleton!?
Pensei num jogo de tetris, only um bloco representaria o singleton e eu os construísse nós blocos montados, pois pensei que todos os blocos vem da mesma estrutura.
(Gostaria de poder entender se meu pensar está correto ou se eu estou fazendo confusão quanto a sua utilização :? )
Vitor Rubio
07/09/2009
Mas obviamente a sua fábrica pode criar objetos diferentes da mesma linhagem ou que implementem a mesma interface dependendo de seu estado ou dos valores de suas propriedades. Mas isso é uma excessão.
Quando uma fábrica precisa criar objetos de tipos diferentes você usa um abstract factory ou até uma fábrica de fábricas.
Por exemplo, eu poderia ter uma fábrica que criasse clientes e fornecedores, já que ambos seriam do tipo Pessoa. Porem usasse metodos diferentes para criar Cliente e Fornecedor, ou usasse o mesmo metodo mas criasse objetos diferentes de acordo com o valor da propriedade ´Tipo´, que poderia ser cliente ou fornecedor.
Daí eu poderia ter duas instancias dessa fábrica, uma para criar cada tipo de objeto. Mas como eu já disse o mais comum é que se use duas fábricas diferentes descendentes da mesma fábrica abstrata e que ambas sejam singletons. Assim você evita que a fábrica de clientes seja obrigada a conhecer a unit de fornecedores.
Como você está fazendo um jogo de Tetris e cada ´peça´ do tetris é formada por 4 blocos, mudando apenas a posição dos mesmos você pode usar Um singleton de fábrica de blocos para criar os blocos e um builder para montar as peças. Randomicamente o builder sempre criaria 4 blocos usando a factory e os disporia com as faces coladas uns nos outros, formando as peças de Tetris T L Z | [] e assim por diante. O Builder retornaria um objeto formado pelos 4 blocos já conectados e configurados.
.lg.
07/09/2009
Obrigado Vitor.
Vitor Rubio
07/09/2009
Eu sempre achei essa parte a mais difícil :wink:
Vamos abrir um tópico só pra isso? Tenho certeza que terão muitas contribuições, Você pode postar a maneira como você fez.
.lg.
07/09/2009
Eu sempre achei essa parte a mais difícil :wink:
Vamos abrir um tópico só pra isso? Tenho certeza que terão muitas contribuições, Você pode postar a maneira como você fez.[/quote:150d02d13a]
Se quiser toma a iniciativa. Eu ainda não elaborei um metodo que possa fazer isso com multiplos objetos. Porém eu tenho o código que verifica a colisão entre 2 objetos. Até aí construir um ´Space Invaders´ seria facil (meu primeiro jogo).
Estou vendo como vou fazer para o metodo varrer a lista dos objetos gerados. Isto é fazer a factory gerar a lista de objetos já criados. Outros fatores, seria fazer a lógica para o metodo entender quando dois objetos formam uma parede, fazer anular os pontos onde os 2 objetos se encontram. (já elaborei teoricamente).
Se quiser tomar a iniciativa, eu discuto o tópico. A princípio estou com estudos focados em objetos 2D.
Abraços.
Marcosrocha
07/09/2009
Se possível trarei mais artigos em breve. Qualquer coisa estou sempre no fórum à disposição. Abraços.
PS.: Realmente o Weber é doido.
Vitor Rubio
07/09/2009
Num outro artigo que você falar sobre o mesmo assunto, mostra uma outra aplicação ou maneira de implementar, vai mais afundo na coisa que você se ´redime´.
não é o primeiro artigo que gerou ´polêmica´. Eu acho tudo isso bastante saudável e construtivo. Conheci esse forum através da revista e o encaro como uma extensão da mesma, onde todos podemos ser editores.
Acho que quando sair meu primeiro artigo eu vou morrer de medo :oops: :shock:
vou ficar um mês sem sair de casa... hauahuaha
Marco Salles
07/09/2009
Vc é um parceirão e sempre contrinbui de forma limpa, cortes , amigavel,
para este forum. Não estamos aqui a julgar o seu trabalho , mas sim
aperfeicoar nosso pequeno conhecimento
ps: fui eu quem lhe enviou o email
salhamoda = Marco Salles
Marcosrocha
07/09/2009
Com relação ao que o .lg. disse, eu estou tentando desenvolver uma engine de física em Delphi para controlar reações de ângulo de colisões entre esferas de mesmo tamanho, desprezando forças externas. Mas estou preso à fisica, tendo em vista que eu não tive faculdade disto em Sistemas de Informação. Caso haja um tópico específico para isto, estarei feliz em participar.
Abraços
Vitor Rubio
07/09/2009
A versão nova do singleton pode ser vista aqui:
http://blog.vitorrubio.com.br/2011/02/existem-1001-maneiras-de-preparar.html
José
07/09/2009