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

13/03/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:

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:

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!


Vitor^_^

Respostas

13/03/2006

Marco Salles

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


hoje eu aprendi uma maneira de se fazer isso:



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

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 [b:c97fde76fb]Factory Method [/b:c97fde76fb], 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


Responder Citar

13/03/2006

Marco Salles

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

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


e os parametros da procedure criarForm saõ assim

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


Responder Citar

13/03/2006

Vitor^_^

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 [b:30519434b0]Factory Method [/b:30519434b0], visto que nunca ouvi falar, nem sei o que é.


Responder Citar

13/03/2006

Michael

[b:f0a32fbba6]Factory Method[/b:f0a32fbba6] é um design pattern [b:f0a32fbba6]criacional[/b:f0a32fbba6] catalogado pela famosa [b:f0a32fbba6]GoF[/b:f0a32fbba6] (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 [b:f0a32fbba6]Marcos Salles[/b:f0a32fbba6], 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 [b:f0a32fbba6]oodesign [/b:f0a32fbba6]são os mesmos do [b:f0a32fbba6]Data & Object Factory[/b:f0a32fbba6], mas traduzidos para o português. Infelizmente nem todos os patterns constam na lista do site brasileiro ainda. Mas devem aparecer em breve.

[]´s


Responder Citar

13/03/2006

Vitor^_^

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?


Responder Citar

13/03/2006

Marco Salles

Mais tarde se voce ainda quiser eu lhe passo um exemplo

Agora duas coisas..

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

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


Responder Citar

13/03/2006

Marco Salles

Mais tarde se voce ainda quiser eu lhe passo um exemplo

Agora duas coisas..

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.

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


Responder Citar

13/03/2006

Vitor^_^

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!


Responder Citar

13/03/2006

Marco Salles

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


unit Unit1;

interface
uses Forms,  Classes, SysUtils, Controls;


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;


function FormFactory: TFormFactory;


var
 _FormFactory: TFormFactory;

{ TFormFactory }


para cada método escreva

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

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&93;).Create(nil) as TForm;
end;


destructor TFormFactory.Destroy;
begin
  FList.Free;
  inherited;
end;


[b:5d64ef4e0f]as funçoes são muito simples e entuitivas[/b:5d64ef4e0f]

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;


function FormFactory: TFormFactory;
begin
  result := _FormFactory;
end;


Não esquecer de :

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

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

var
  Form1: TForm1;

implementation

uses FormFactoryU;

{$R *.dfm}


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

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:
[b:5d64ef4e0f]Claro que o form2 esta na secção avalaibleForms[/b:5d64ef4e0f]

Então Vitor bom apetite...


Responder Citar

30/03/2006

Vitor^_^

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:

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:

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!


Responder Citar