Array
(
)

Criação/Destruição de forms apenas pelo nome

Vitor^_^
   - 13 mar 2006

Uma coisa que eu sempre me perguntei e venho procurando há uns dois anos, mas sem sucesso, era como criar forms sem a unit delas estar no seu uses, sem ter acesso a implementação da classe dessa form, mas simplesmente puder gravar o nome de uma form no banco de daddos e recuperar depois, podendo abrir a form apenas pelo nome dela.

hoje eu aprendi uma maneira de se fazer isso:

basta colocar nas units das forms:

#Código

initialization
RegisterClass(TForm3);

finalization
UnRegisterClass(TForm3);



sendo Tform3 o nome da classe da sua form.

depois, na form que vai ser chamada, mais especificamente na procedure que vai chamar as outras forms, é só fazer:

#Código
procedure TChamadora.btn1Click(Sender: TObject);
var ClasseDoObjejetoChamado: TFormClass;
ObjetoDaClasseChamada: TForm;
begin
ClasseDoObjejetoChamado := TFormClass(GetClass(edt1.Text));
ObjetoDaClasseChamada := ClasseDoObjejetoChamado.create(nil);
with ObjetoDaClasseChamada do
begin
Show;
{
showmodal;
release;
}
end;

end;


eu fiz isso no evento onclick de um botão, mas pode ser uma procedure que aceite como parametro uma string do nome da classe da form. Se for o nome da form, basta concatenar um T antes.

agora eu gostaria de saber se isso é uma boa prática ou se eh uma prática ruim, e se ela leva a algum bug ou perda de performance, memory leak etc.


valew!


Marco Salles
   - 13 mar 2006


Citação:
Uma coisa que eu sempre me perguntei e venho procurando há uns dois anos......
...podendo abrir a form apenas pelo nome dela.



Citação:
hoje eu aprendi uma maneira de se fazer isso:




Citação:
eu fiz isso no evento onclick de um botão, mas pode ser uma procedure que aceite como parametro uma string do nome da classe da form. Se for o nome da form, basta concatenar um T antes.


sinceramente , acho que voce ainda não aprendeu..

Se voce mesmo diz que tem que concatenar um ´T´ antes , de fato voce não esta criando o formulario apenas com o nome

Mas se voce quiser mesmo saber qual a tecnica que se constroi formulario somente pelo nome sem ter que concatenar nada , deve procurar sobre Factory Method


Citação:
agora eu gostaria de saber se isso é uma boa prática ou se eh uma prática ruim, e se ela leva a algum bug ou perda de performance, memory leak etc.


Usando o Factory Method , o que posso falar é que é uma execelente tecnica , resulta em codigos mais limpos , mais facilidade na manutenção , o formulario principal ,passa a não saber como os formularios estão sendo criados.. Enfim uma sperie de vantagens


Marco Salles
   - 13 mar 2006

Ha desculpe a conexão caiu antes que eu conclui-sse

No método Factory Method para criar um Form voce simplesmente vai fazer assim

#Código

procedure TForm1.Button1Click(Sender: TObject);
begin
CriarForm(´form2´);
end;


e os parametros da procedure criarForm saõ assim

#Código
procedure CriarForm(nomeform:String);


se voce quiser se aprofundar mais e não achar nada sobre o método eu posso lhe passar mais detalhes

boa sorte


Vitor^_^
   - 13 mar 2006

Então, eu fiquei empolgado porque aprendi isso aí hoje, e criar uma form através do nome..... mesmo que seja o nome da classe, já quebra meu galho. Mas eu percebi que era uma gambiarra tremenda, por isso eu perguntei se era uma boa prática.

Gostaria de aprender mais sobre Factory Method , visto que nunca ouvi falar, nem sei o que é.


Michael
   - 13 mar 2006

Factory Method é um design pattern criacional catalogado pela famosa GoF (Gang of Four). A função deste modelo é ´criar uma instância de várias classes derivadas´.

Você pode encontrar mais informações - e exemplos em C#, Delphi, Java e C++ - em http://www.oodesign.com.br/patterns/FactoryMethod.html. E também neste excelente site (em inglês) http://www.dofactory.com/Patterns/PatternFactory.aspx.

Também saiu um artigo sobre design patterns na edição 69 da revista ClubeDelphi, que abordou exatamente o modelo Factory Method. O exemplo usado pelo autor parece ser o mesmo citado pelo colega Marcos Salles, onde cada formulário se registra na ´fábrica´ de objetos. Só não gosto de uma coisa nesta abordagem: usar strings para identificar os forms, pois caso o nome seja digitado incorretamente o compilador não irá gerar nenhum aviso, por razões óbvias. Prefiro passar a definição classe que quero que a fábrica crie para mim.

Por fim, se quiser se aprofundar em design patters - algo extremamente aconselhável - visite os mesmos sites que indiquei acima, mas através destas URL´s:

http://www.oodesign.com.br/patterns/
http://www.dofactory.com/Patterns/Patterns.aspx

A diferença é que estes links levarão vc para a lista de todos os design patterns da GoF. Aliás, os exemplos do site oodesign são os mesmos do Data & Object Factory, mas traduzidos para o português. Infelizmente nem todos os patterns constam na lista do site brasileiro ainda. Mas devem aparecer em breve.

[]´s


Vitor^_^
   - 13 mar 2006

Interessante isso, eu nunca tinha ouvido falar nessa técnica, e a clubedelphi 69 eu ainda não li. Eu tava dando uma olhada aqui no wikipédia, procurando por factory methods, e pelo que eu pude entender, na unit que você vai criar o factory method, no esqueleto dela, deve haver as condições para criação do objeto de cada classe. o método retorna o objeto criado, mas sua implementação precisa conhecer as classes, porque faz referenca a elas. Isso significa que a seção uses dessa unit vai crescendo a medida que o meu sistema cresce tambem.

Agora, vamos a um exemplo: e se eu coloco novas forms do meu software dentro de dll´s ou bpl´s que vão ser exibidas através de menus criados dinamicamente? eu não tenho essas units novas na seção uses do meu programa que já está rodando no cliente. Dá pra resolver isso também, ou nem tem haver com o que estamos conversando em si?


Marco Salles
   - 13 mar 2006

Mais tarde se voce ainda quiser eu lhe passo um exemplo

Agora duas coisas..


Citação:
Eu tava dando uma olhada aqui no wikipédia, procurando por factory methods, e pelo que eu pude entender, na unit que você vai criar o factory method, no esqueleto dela, deve haver as condições para criação do objeto de cada classe. o método retorna o objeto criado, mas sua implementação precisa conhecer as classes, porque faz referenca a elas. Isso significa que a seção uses dessa unit vai crescendo a medida que o meu sistema cresce tambem.


Não .. o interresante é que isto não é verdade.. Na uses onde esta declarado o metodo , não setem que declarar esses objetos


Citação:
Só não gosto de uma coisa nesta abordagem: usar strings para identificar os forms, pois caso o nome seja digitado incorretamente o compilador não irá gerar nenhum aviso, por razões óbvias.


Mas voce pode gerar um aviso com um bloco try except , porque quando o nome esta errado uma exceção sera lançada


Marco Salles
   - 13 mar 2006

Mais tarde se voce ainda quiser eu lhe passo um exemplo

Agora duas coisas..


Citação:
Eu tava dando uma olhada aqui no wikipédia, procurando por factory methods, e pelo que eu pude entender, na unit que você vai criar o factory method, no esqueleto dela, deve haver as condições para criação do objeto de cada classe. o método retorna o objeto criado, mas sua implementação precisa conhecer as classes, porque faz referenca a elas. Isso significa que a seção uses dessa unit vai crescendo a medida que o meu sistema cresce tambem.


Não .. o interresante é que isto não é verdade.. Na uses onde esta declarado o metodo , não se declarar esses objetos.


Citação:
Só não gosto de uma coisa nesta abordagem: usar strings para identificar os forms, pois caso o nome seja digitado incorretamente o compilador não irá gerar nenhum aviso, por razões óbvias.


Mas voce pode gerar um aviso com um bloco try except , porque quando o nome esta errado uma exceção sera lançada


Vitor^_^
   - 13 mar 2006

Se você puder mandar um exemplo bem basicão com classes de forms eu agradeço. Os exemplos que o site oodesign tinha pra download não foram suficientes pra me esclarecer totalmente.

Valew!


Marco Salles
   - 13 mar 2006

antes do exemplo so um comentário.. Ha uns tempos atrás eu participei com perguntas e posteriormente com respostas , sobre a criação de formulários passando o nome do form.. Porem , nada tecnico e sim usando sempre de artificios.. Porem isto me chamou a atençao e nas indas e vindas de pesquisa e procuras ,encontrei algo em design patterns..Isto antes da edição 69 do delphi ser editada. Porem veio um artigo nesta edição que fala do modelo de criaçao de objetos a patir de suas subclasses, que é basicamente o modelo de factory method. Li o artigo , porque gosto desta teorias , mas não me acrescentou nada de novo daquilo que ja conhecia ...Mas deixo aqui registrado , que o artigo é simples e foi muito bem editado Prof : Paulo Roberto Quicoli e mereçe os nosso singelos cumprimentos


Como foi dito anteriormente o modelo é muito simples , mas muito profundo em seus objetivos

Segue o exemplo na integra que pode ser baseado no artigo da ediçao 69 :

Crie uma Unit .. e declare o seguintes typo


#Código

unit Unit1;

interface
uses Forms, Classes, SysUtils, Controls;


#Código
type
TFormFactory = class
private
FList: TStrings;
public
constructor Create;
destructor Destroy; override;
function CreateForm(const aFormName: string): TForm;
procedure RegisterForm(const aFormName: string; aFormClass: TComponentClass);
end;


#Código
function FormFactory: TFormFactory;


#Código
var
_FormFactory: TFormFactory;

{ TFormFactory }


para cada método escreva

#Código
constructor TFormFactory.Create;
begin
inherited Create;
FList := TStringList.Create;
end;


Atente para esta função ...Uma exceçãopode ser lançada , quando o nome passado para a criação do formulario não fizer parte do Objetos da lista

#Código
function TFormFactory.CreateForm(const aFormName: string): TForm;
var
i : integer ;
begin
result := nil;
i := FList.IndexOf(aFormName);
Assert(i <> -1, ´Formulário ´+ aFormName +´ não existe´);
Result := TComponentClass(FList.Objects[i]).Create(nil) as TForm;
end;


#Código
destructor TFormFactory.Destroy;
begin
FList.Free;
inherited;
end;


as funçoes são muito simples e entuitivas

#Código
procedure TFormFactory.RegisterForm(const aFormName: string;
aFormClass: TComponentClass);
var
i: integer;
begin
i := FList.IndexOf(aFormName);
if i = -1 then
FList.AddObject(aFormName, TObject(aFormClass));
end;


#Código
function FormFactory: TFormFactory;
begin
result := _FormFactory;
end;


Não esquecer de :

#Código
initialization
_FormFactory := TFormFactory.Create;

finalization
_FormFactory.Free;

end.


Ta , nossa interface para a criação de objetos esta toda definida... Slave a unit como achar melhor , se quiser adicional no seus projetos etc.. sirva-se a vontade

o macete agora e definir esta uses na aplicaçao Principal e Tb nos formularios que voce ira criar .. Por exemplo um form2

#Código
var
Form2: TForm2;

implementation

uses FormFactoryU; //Nome da unit onde se tem os Métodos
//Igual da apostila

{$R *.dfm}

initialization
FormFactory.RegisterForm(´Form2´, TForm2);
end.


Com isso na compilação , é adicionado ao FList.AddObject o Nome e a classe do form... Isto é feito uma unica vez , para cada form que for registrado.. E depois com o proprio nome na hora de criar esse form , o Método TFormFactory.CreateForm é executado e ai se faz uma conversão ... Claro , que se o nome do form não existir na Lista de objetos uma exceção do tipo EAssertionFailed Sera lançada , que pode ser capturada e tratada , apesar do delphi suportar quase todas exceçoes

Por fim no formulario principal podemos fazer assim

#Código
var
Form1: TForm1;

implementation

uses FormFactoryU;

{$R *.dfm}


Tudo igual da apostila com exceção do try except , para tratar

#Código
procedure TForm1.Button1Click(Sender: TObject);
begin
try
FForm := FormFactory.CreateForm(´Form2´);
FForm.ShowModal;
FreeAndNil(FForm);
except
on EAssertionFailed do
showmessage(´Nome errado´);
end;
end;


:arrow:
Claro que o form2 esta na secção avalaibleForms

Então Vitor bom apetite...


Vitor^_^
   - 30 mar 2006

Andei meio ocupado então só agora pude ler e responder o tópico.

Cara, muito bom mesmo, muito útil e interessante. Só que se você comaprar com a solução que eu tinha pensado, verá que no fundo, não é muito diferente, a diferença é que o código do button1 deveria, logicamente, estar numa função que recebesse só o nome da form como parametro e devolvesse a form. Tambem do método que eu falei, vc trabalha soh com as classes e não com os objetos> se você olhar nos vontes da procedure registerclass, do delphi, vai encontrar uma estrutura parecida, em certo ponto, com a estrutura da classe TformFactory:

#Código

procedure RegisterClass(AClass: TPersistentClass);
begin
RegGroups.Lock;
try
while not RegGroups.Registered(AClass) do
begin
RegGroups.RegisterClass(AClass);
if AClass = TPersistent then Break;
AClass := TPersistentClass(AClass.ClassParent);
end;
finally
RegGroups.Unlock;
end;
end;


e o metodo RegisterClass(AClass) da variavel global reggroups:

#Código
procedure TRegGroups.RegisterClass(AClass: TPersistentClass);
var
Group: TRegGroup;
begin
Group := FindGroup(AClass);
if Group <> nil then Group.RegisterClass(AClass);
end;


obviamente, usando o factorymethod fica bem mais lógico e limpo. Vou começar a usar. Só tenho uma dúvida: para não precisar da fariavel global e da função qu acessa ela, os métodos para registrar a form e criar a form não poderiam ser métodos de classe?

Valew!