Array
(
)

Dúvidas de POO, herança e polimorfismo

Vitor^_^
   - 03 fev 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!


Michael
   - 03 fev 2006

Olá!

Repostas:

1) Virtual e dynamic 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) Override deve ser usado quando se quer extender ou substituir o código de um método de uma classe ascendente, marcado como virtual ou dynamic, por outro;

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

4) Reintroduce é uma forma de se sobrescrever um método marcado como virtual, 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 virtual, e vc quiser extendê-lo, use override e inherited. Se quiser mudar seus parâmetros, use reintroduce e eventualmente, inherited. Por curiosidade, o que seria uma ´maneira suja´? ;-)

[]´s


Massuda
   - 03 fev 2006

O Michael respondeu bem, mas tem um pequeno detalhe que talvez seja interessante saber...
Citação:
...classe, descendente de Tobject ou não?
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...#Código

type
TMinhaClasse = class
...
end;
...isso equivale a escrever...#Código
type
TMinhaClasse = class(TObject)
...
end;
...ou seja, toda classe herda membros/métodos de TObject.


Vitor^_^
   - 03 fev 2006


Citação:
Repostas:
(...)
3) Inherited é usado para acessar o código original de um método marcado como virtual 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?


Massuda
   - 03 fev 2006


Citação:
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?
Sim, mas tem uma diferença. Considere isso...#Código

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.


Michael
   - 03 fev 2006


Citação:
a mesma coisa vale pra dynamic, certo?


Sim.


Citação:
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?


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

Usando virtual/dynamic e override:
#Código

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:
#Código
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? Override é 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 virtual da classe base <nome_classe>´. Sem override vc pensa que está chamando um código de uma classe, mas na verdade está executando outro, escondido.

Vc sempre pode chamar inherited dentro de um método herdado. Deve apenas ter o cuidado de chamar inherited NomeDoMetodoOriginal ao invés de só inherited 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 virtual ao invés de dynamic. 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


Vitor^_^
   - 03 fev 2006

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.


Vitor^_^
   - 03 fev 2006

outras dúvidas teóricas:

1) pra que serve a palavra reservada object? 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 var ? 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!!!


Massuda
   - 03 fev 2006


Citação:
1) pra que serve a palavra reservada object? pelo que percebi, dá pra criar
estruturas tipo records com ela, mas com métodos, porém, diferentemente das classes, é tudo estático.
object é uma herança dos tempos do Turbo Pascal. Não é boa idéia usar.


Citação:
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 var ? Existe o conceito passar um objeto por valor ou referência?
Faz diferença. Quando você passa como var 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...#Código

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.


Citação:
...qual é a finalidade de passar um parâmetro com a palavra reservada const numa procedure?
const nesse contexto é o oposto do var que você perguntou antes. Significa que o valor passado não será alterado. Por exemplo...#Código
procedure Foo(const Obj: TObject);
begin
Obj.Free;
Obj := nil;
end;
...não compila porque você não pode alterar o valor de Obj.


Michael
   - 03 fev 2006

Mais respostas:

1) No Pascal antigo object fazia o papel que class faz hoje. Até o Delphi 2 eu tenho certeza que era suportada ainda, desde que se usasse a notação System.Object. 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:

#Código

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


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

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

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


Sem const 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 const com objetos: vc não vai poder mudar a referência dele, mas pode alterar seus campos e propridades.

#Código
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


Sourcecode
   - 03 fev 2006

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?


Massuda
   - 03 fev 2006

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.