Dúvidas de POO, herança e polimorfismo

03/02/2006

Tenho algumas dúvidas sobre algumas coisas básicas do Delphi, em relação a POO. Li algumas informaçãoes no capítulo 2 do livro ´Delphi, a bíblia´ e apareceram mais dúvidas Quando eu crio uma classe e depois uma classe descendente da primeira, e quero modificar alguns métodos:

1) quando eu devo usar virtual e quando usar dynamic na classe ancestral?

2) quando eu devo usar override na descendente?

3) quando eu devo usar inherited na implementação do método herdado? (atualmente eu uso sempre que desejo executar primeiro a parte original do código da classe pai)

4) quando eu devo usar o reintroduce?

5) quando eu não devo usar override, e caso eu possa modificar métodos assim nos herdeiros, uso o inherited? e se eu quiser mudar os parâmetros?

6) qual a maneira correta, limpa e POO de se criar um método Create para uma classe, descendente de Tobject ou não?



Valew pessoal!


Vitor^_^

Respostas

03/02/2006

Michael

Olá!

Repostas:

1) [b:98afc038ce]Virtual [/b:98afc038ce]e [b:98afc038ce]dynamic [/b:98afc038ce]têm a mesma função. A diferença é que o primeiro vai gerar um código mais rápido, mas que consome um pouco mais de memória, e o segundo um código que vai utilizar menos memória;

2) [b:98afc038ce]Override [/b:98afc038ce]deve ser usado quando se quer extender ou substituir o código de um método de uma classe ascendente, marcado como [b:98afc038ce]virtual [/b:98afc038ce]ou [b:98afc038ce]dynamic[/b:98afc038ce], por outro;

3) [b:98afc038ce]Inherited [/b:98afc038ce]é usado para acessar o código original de um método marcado como [b:98afc038ce]virtual [/b:98afc038ce]na classe base. Deve ser usado quando há essa necessidade;

4) [b:98afc038ce]Reintroduce [/b:98afc038ce]é uma forma de se sobrescrever um método marcado como [b:98afc038ce]virtual[/b:98afc038ce], mas como parâmetros diferentes do original. Essa palavra-chave só existe para suprimir um warning do compilador para métodos na situação apresentada. Sem ele o código funciona do mesmo jeito, mas vc sempre verá um aviso quando for compilar;

5) As respostas anteriores respondem essa, não? ;-)

6) Bom, isso depende da situação. Se o construtor original estiver marcado como [b:98afc038ce]virtual[/b:98afc038ce], e vc quiser extendê-lo, use [b:98afc038ce]override [/b:98afc038ce]e [b:98afc038ce]inherited[/b:98afc038ce]. Se quiser mudar seus parâmetros, use [b:98afc038ce]reintroduce [/b:98afc038ce]e eventualmente, [b:98afc038ce]inherited[/b:98afc038ce]. Por curiosidade, o que seria uma ´maneira suja´? ;-)

[]´s


Responder Citar

03/02/2006

Massuda

O Michael respondeu bem, mas tem um pequeno detalhe que talvez seja interessante saber...[quote:7fae6fba86=´vitor^_^´]...classe, descendente de Tobject ou não?[/quote:7fae6fba86]O modelo de objeto do Delphi é implementado de modo que uma classe só pode ser derivada de outra pré existente. Portanto, existe uma classe base comum à todas as classes Delphi, seja TComponent, TTable ou TSuaClasse: TObject. Quando você escreve...
type
  TMinhaClasse = class
    ...
  end;
...isso equivale a escrever...
type
  TMinhaClasse = class(TObject)
    ...
  end;
...ou seja, toda classe herda membros/métodos de TObject.


Responder Citar

03/02/2006

Vitor^_^

Repostas: (...) 3) [b:c8a9194b7c]Inherited [/b:c8a9194b7c]é usado para acessar o código original de um método marcado como [b:c8a9194b7c]virtual [/b:c8a9194b7c]na classe base. Deve ser usado quando há essa necessidade; (...)


a mesma coisa vale pra dynamic, certo?

outra coisa: se eu quiser sobrescrever métodos que não foram declarados
com virtual ou dynamic, basta simplesmente redeclarar e usar o inherited caso necessário, mas sem usar o override?


Responder Citar

03/02/2006

Massuda

[quote:f2fc2d9db8=´vitor^_^´]outra coisa: se eu quiser sobrescrever métodos que não foram declarados
com virtual ou dynamic, basta simplesmente redeclarar e usar o inherited caso necessário, mas sem usar o override?[/quote:f2fc2d9db8]Sim, mas tem uma diferença. Considere isso...
type
  TPai = class
  public
    procedure Proc1;
    procedure Proc2; virtual;
    procedure Proc3;
  end;

  TFilho = class(TPai)
  public
    procedure Proc1;
    procedure Proc2; override;
  end;

...

procedure TPai.Proc3;
begin
  Proc1;
  Proc2;
end;

...
Se eu criar um objeto TFilho e executar TFilho.Proc3, o Proc2 que vai ser executado é o do TFilho enquanto o Proc1 será do TPai, pois Proc1 não é virtual.


Responder Citar

03/02/2006

Michael

[quote:3b1a09f546=´vitor^_^´]a mesma coisa vale pra dynamic, certo?[/quote:3b1a09f546]

Sim.

[quote:3b1a09f546=´vitor^_^´]outra coisa: se eu quiser sobrescrever métodos que não foram declarados
com virtual ou dynamic, basta simplesmente redeclarar e usar o inherited caso necessário, mas sem usar o override?[/quote:3b1a09f546]

Isso é um pouco complexo de entender, mas acho que com um exemplo fica mais claro:

Usando virtual/dynamic e override:
TCachorro = class
    procedure Latir; virtual;
  end;

  TPitBull = class(TCachorro)
    procedure Latir; override;
  end;

  TPoodle = class(TCachorro)
    procedure Latir; override;
  end;

...

var
  Cachorro: TCachorro;
begin
  Cachorro := TPitBull.Create;
  Cachorro.Latir;  // chama TPitBull.Latir
  Cachorro.Free;
  Cachorro := TPoodle.Create;
  Cachorro.Latir;  // chama TPoodle.Latir
  Cachorro.Free;
end;


Sem override:
TCachorro = class
    procedure Latir; virtual;
  end;

  TPitBull = class(TCachorro)
    procedure Latir; 
  end;

  TPoodle = class(TCachorro)
    procedure Latir; 
  end;

...

var
  Cachorro: TCachorro;
begin
  Cachorro := TPitBull.Create;
  Cachorro.Latir;  // chama TCachorro.Latir;
  Cachorro.Free;
  Cachorro := TPoodle.Create;
  Cachorro.Latir;  // chama TCachorro.Latir;
  Cachorro.Free;
end;


Percebeu a diferença? [b:3b1a09f546]Override [/b:3b1a09f546]é uma maneira de garantir que o método de uma classe será sempre chamado, mesmo que a instância usada seja de um tipo ascendente a ela. É por isso que a mensagem de aviso do Delphi é ´método <nome_método> esconde o método [b:3b1a09f546]virtual [/b:3b1a09f546]da classe base <nome_classe>´. Sem [b:3b1a09f546]override [/b:3b1a09f546]vc pensa que está chamando um código de uma classe, mas na verdade está executando outro, escondido.

Vc sempre pode chamar [b:3b1a09f546]inherited [/b:3b1a09f546]dentro de um método herdado. Deve apenas ter o cuidado de chamar [b:3b1a09f546]inherited [/b:3b1a09f546]NomeDoMetodoOriginal ao invés de só [b:3b1a09f546]inherited [/b:3b1a09f546]caso vc mude a assinatura dele na classe descendente, pois o compilador não será capaz de distinguir um do outro.

Uma última dica: a não ser que o ganho seja real, use sempre [b:3b1a09f546]virtual [/b:3b1a09f546]ao invés de [b:3b1a09f546]dynamic[/b:3b1a09f546]. Um exemplo: ´vc tem uma classe que muitos métodos passíveis de serem sobrescritos (overriden) que são herdados em muitas classes, mas ocasionalmente chamados´ (traduzido do help do Delphi).

[]´s


Responder Citar

03/02/2006

Vitor^_^

Massuda e Michael, muito obrigado pelas dicas!

Agora uma ironia, pragente rir um pouco: sempre que eu me aventurei a fazer meus programas certinhos, tentando fazer OO, eu usava o virtual e o override da maneira certa, sem querer, porque copiava de exemplos, revistas ou do próprio fonte da VCL. Copiava sem saber direito como funciona.

Criei até alguns componentes assim...

de vez em quando eu esquecia de colocar override num método herdado, de vez em quando eu esquecia de colocar virtual num método ancestral.... e as vezes eu tinha que sobrescrever o método create de algumas forms, usando parâmetros. Devo ter feito muita besteira nesse meio tempo, mas por incrível que pareça tá tudo funcionando. (ou pelo menos ainda não deu uma zica braba) Tô até com medo de mexer agora hauahaua.


Responder Citar

03/02/2006

Vitor^_^

outras dúvidas teóricas:

1) pra que serve a palavra reservada [b:bcb6af0e17]object[/b:bcb6af0e17]? pelo que percebi, dá pra criar
estruturas tipo records com ela, mas com métodos, porém, diferentemente das classes, é tudo estático.

2) se as variaveis objetos (instâncias de classes, como edit1) são ponteiros para um local na memória onde está a instância dessse objeto, faz diferença, ao passar esse objeto como parâmetro de uma procedure ou função, usar ou não a palavra reservada [b:bcb6af0e17]var[/b:bcb6af0e17] ? Existe o conceito passar um objeto por valor ou referência?

3) no código fonte da VCL, alguns métodos de componentes estão assim:
procedure tcomponente.blablabla(valor : tipo);

e outros estão assim:
procedure tcomponente.blablabla(const valor : tipo);

qual é a finalidade de passar um parâmetro com a palavra reservada const numa procedure?


valew!!!


Responder Citar

03/02/2006

Massuda

[quote:414dfabbd3=´vitor^_^´]1) pra que serve a palavra reservada [b:414dfabbd3]object[/b:414dfabbd3]? pelo que percebi, dá pra criar
estruturas tipo records com ela, mas com métodos, porém, diferentemente das classes, é tudo estático.[/quote:414dfabbd3][b:414dfabbd3]object[/b:414dfabbd3] é uma herança dos tempos do Turbo Pascal. Não é boa idéia usar.

[quote:414dfabbd3=´vitor^_^´]2) se as variaveis objetos (instâncias de classes, como edit1) são ponteiros para um local na memória onde está a instância dessse objeto, faz diferença, ao passar esse objeto como parâmetro de uma procedure ou função, usar ou não a palavra reservada [b:414dfabbd3]var[/b:414dfabbd3] ? Existe o conceito passar um objeto por valor ou referência? [/quote:414dfabbd3]Faz diferença. Quando você passa como [b:414dfabbd3]var[/b:414dfabbd3] singifica que o valor passado está sujeito a ser modificado. No caso, o que será modificado não é o objeto em si, mas sim a referência ao objeto. Por exemplo...
procedure Foo(var Obj: TObject);
begin
  Obj.Free;
  Obj := nil;
end;
...não apenas irá destruir o objeto como também nilzar a referencia ao objeto que foi passada para Foo.

[quote:414dfabbd3=´vitor^_^´]...qual é a finalidade de passar um parâmetro com a palavra reservada const numa procedure?[/quote:414dfabbd3][b:414dfabbd3]const[/b:414dfabbd3] nesse contexto é o oposto do [b:414dfabbd3]var[/b:414dfabbd3] que você perguntou antes. Significa que o valor passado não será alterado. Por exemplo...
procedure Foo(const Obj: TObject);
begin
  Obj.Free;
  Obj := nil;
end;
...não compila porque você não pode alterar o valor de Obj.


Responder Citar

03/02/2006

Michael

Mais respostas:

1) No Pascal antigo [b:4775e4f666]object [/b:4775e4f666]fazia o papel que [b:4775e4f666]class [/b:4775e4f666]faz hoje. Até o Delphi 2 eu tenho certeza que era suportada ainda, desde que se usasse a notação [b:4775e4f666]System.Object[/b:4775e4f666]. Hoje não sei se ainda é possível fazer isso. De qualque forma não é aconselhado fazer uso dela neste contexto. O uso mais frequente desta palavra reservada é na criação de procedures para serem usadas como ponteiros de eventos:

type
  MeuEvento = procedure (Sender: TObject; MeuParametro: string) of object;


2) Objetos sempre são passados por referência. [b:4775e4f666]Var [/b:4775e4f666]não influencia em nada (velocidade de acesso, por exemplo), salvo algumas exceções. Isso foi discutido há um tempo atrás aqui. [b:4775e4f666]Massuda[/b:4775e4f666], poste o link, se vc ainda o tiver;

3) A palavra reservada [b:4775e4f666]const [/b:4775e4f666]em parâmetros de procedures/funções impede que o valor deles seja alterado dentro da rotina. Veja:

procedure Teste(const S: string);
begin
  S := ´Teste´; // Erro aqui por causa do const
end;


Sem [b:4775e4f666]const [/b:4775e4f666]vc pode alterar o valor do parâmetro localmente. Isso é usado quando vc precisa manter o valor original dentro do procedimento, e quer ter certeza que ele não será alterado, em virtude de esquecimento, por exemplo.

Há uma consideração a ser feita quando se usa [b:4775e4f666]const [/b:4775e4f666]com objetos: vc não vai poder mudar a referência dele, mas pode alterar seus campos e propridades.

procedure Teste(const A: TStringList);
var
  B: TStringList;
begin
  B := TStringList.Create;
  A := B; // Isso não compila;
  B.Text := ´Teste´; // Isso sim
end;


[]´s

[]´s


Responder Citar

03/02/2006

Sourcecode

Olha que interessante, System.Object hoje, é a classe base do dotnet: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemobjectclasstopic.asp seria isso uma mera coincidência?


Responder Citar

03/02/2006

Massuda

Não é coincidência. Anders Hejlsberg, criador do Delphi, hoje trabalha ma Microsoft, participou do desenvolvimento do .Net e é o pai do C#.

Outro ex-Borland que hoje trabalha na Microsoft é Chuck Jazdzewski, que chefiou o desenvolvimento do Delphi até a versão 6.


Responder Citar