19.11.2002
|
čo sa budeme dnes učiť
- bez ďalšej vlastnosti - polymorfizmus -
by bola práca s objektmi veľmi obmedzená
- polymorfizmus je silný mechanizmus
zdieľania metód
- vďaka kompatibilite inštancií môžeme
pracovať s polymorfnými objektmi aj polymorfnými
parametrami
trieda TKruh a TStvorec
Na 13. prednáške - úvod do objektového
programovania - sme robili projekt, v ktorom sme zadefinovali
triedu TKruh:
už známa definícia triedy TKruh:
|
var
g:TCanvas;
type
TKruh = class
private
x,y,r:integer;
f:TColor;
vid:boolean;
public
constructor Create(xx,yy,rr:integer);
procedure ukaz;
procedure skry;
procedure ZmenFarbu(ff:TColor);
procedure posun(dx,dy:integer);
end;
constructor TKruh.Create(xx,yy,rr:integer);
begin
x:=xx; y:=yy; r:=rr; vid:=false; f:=clBlack;
end;
procedure TKruh.ukaz;
begin
g.Pen.Color:=f;
g.Brush.Style:=bsClear;
g.Ellipse(x-r,y-r,x+r,y+r);
vid:=true;
end;
procedure TKruh.skry;
begin
g.Pen.Color:=clWhite;
g.Brush.Style:=bsClear;
g.Ellipse(x-r,y-r,x+r,y+r);
vid:=false;
end;
procedure TKruh.ZmenFarbu(ff:TColor);
begin
f:=ff;
if vid then ukaz;
end;
procedure TKruh.posun(dx,dy:integer);
var
b:boolean;
begin
b:=vid; if vid then skry;
inc(x,dx); inc(y,dy);
if b then ukaz;
end;
|
- na tri tlačidlá sme priradili
vytvorenie troch inštancií tejto triedy - štvrté
tlačidlo hýbalo s týmito kruhmi, piate
rušilo všetky inštancie:
a túto triedu sme používali napr.
takto:
|
var
a,b,c:TKruh;
poc:integer;
procedure TForm1.FormCreate(Sender: TObject);
begin
g:=Image1.Canvas;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
a:=TKruh.Create(100,100,50);
with a do begin ZmenFarbu(clGreen); ukaz; end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
b:=TKruh.Create(300,200,100); b.ZmenFarbu(clRed); b.ukaz;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
c:=TKruh.Create(200,250,70); c.ukaz;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
poc:=100;
Timer1.Enabled:=true;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if a<>nil then a.posun(3,2);
if b<>nil then b.posun(-5,-1);
if c<>nil then c.posun(2,-3);
dec(poc);
if poc<=0 then Timer1.Enabled:=false;
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
if a<>nil then
with a do begin
if vid then skry;
Free; a:=nil;
end;
if b<>nil then
with b do begin
if vid then skry;
Free; b:=nil;
end;
if c<>nil then
with c do begin
if vid then skry;
Free; c:=nil;
end;
end;
|
dodefinujeme novú triedu TStvorec - podtriedu
TKruh:
|
type
TStvorec = class(TKruh)
public
procedure ukaz;
procedure skry;
procedure ZmenFarbu(ff:TColor);
procedure posun(dx,dy:integer);
end;
procedure TStvorec.ukaz;
begin
g.Pen.Color:=f;
g.Brush.Style:=bsClear;
g.Rectangle(x-r,y-r,x+r,y+r);
vid:=true;
end;
procedure TStvorec.skry;
begin
g.Pen.Color:=clWhite;
g.Brush.Style:=bsClear;
g.Rectangle(x-r,y-r,x+r,y+r);
vid:=false;
end;
procedure TStvorec.ZmenFarbu(ff:TColor);
begin
f:=ff;
if vid then ukaz;
end;
procedure TStvorec.posun(dx,dy:integer);
var
b:boolean;
begin
b:=vid; if vid then skry;
inc(x,dx); inc(y,dy);
if b then ukaz;
end;
|
jedna z premenných - inštancií kruhu
bude teraz štvorec:
|
var
a:TKruh;
b:TStvorec;
c:TKruh;
...
procedure TForm1.Button2Click(Sender: TObject);
begin
b:=TStvorec.Create(300,200,60);
b.ZmenFarbu(clRed);
b.ukaz;
end;
|
- takto vylepšený projekt funguje správne
- druhé tlačidlo vytvorí štvorec a štvrté
tlačidlo rozhýbe všetky útvary.
- ak porovnáme metódy ZmenFarbu a posun
v oboch triedach, zistíme, že sú úplne
identické, takže sa ich pokúsime v triede
TStvorec vyhodiť: kompilátoru to nevadí
- pochopí, že sa majú zdediť, ale projekt
už nefunguje správne ...
Statické metódy
Virtuálne metódy
- zrejme sa to nedá urobiť počas kompilácie,
lebo TKruh.posun nemôže "tušiť", že
niekedy v budúcnosti si niekto niekde inde naprogramuje
veľa potomkov, ktoré budú volať TKruh.posun
Polymorfizmus
- metóda sa zdieľa v rôznych stupňoch
objektovej hierarchie ->
- napr. vtedy, ak rôzne triedy zdedia "spoločnú"
metódu (napr. metódu posun), ale detaily
pre rôzne objekty zodpovedajú ich zvláštnostiam
(napr. metóda skry)
Včasná a neskorá väzba
- kompilátor - okamžité rozhodnutie pri
statických metódach
- odložené rozhodnutie pri virtuálnych
metódach
- zatiaľ nevieš, ktorú metódu (ktorého
objektu), keď príde čas volania, opýtaj
sa inštancie (premennej typu objekt)
- pri statických metódach kompilátor
hľadá zodpovedajúce meno v hierarchii
smerom k predkom od danej úrovne (kde je definovaná
metóda)
- pri virtuálnych metódach nie kompilátor,
ale počas behu sa hľadá zodpovedajúce
meno v hierarchii smerom k predkom, ale od úrovne
inštancie
- včasná väzba = early binding - pri statických
- neskorá väzba = late binding - pri virtuálnych
Virtuálne metódy
- v deklarácii objektu virtual
- ak je v nejakej triede virtuálna metóda
=> všetci potomkovia, ak majú metódu
tohoto mena, mali by mať override (prekryje virtuálnu
metódu) a s rovnakou hlavičkou - bez override
sa táto definícia bude chápať ako
"predefinovanie" virtuálnej na statickú
...
- každý objekt obsahuje VMT (Virtual Method
Table):
- konštruktor objektu inicializuje napojenie na túto
tabuľku
- každá inštancia obsahuje len referenciu na
túto (jedinú pre danú triedu) tabuľku
- je to prvý položka v zázname
- hovoríme, že konštruktor môže byť použitý
aj na inicializovanie stavových premenných
- jeho "hlavná" funkcia je vyhradiť
pamäť pre objekt a inicializovať VMT !!!
Konštruktory a deštruktory
Kompatibilita tried (objektových typov)
kompatibilita je jednosmerná => odvodený
typ dedí kompatibilitu so svojimi predkami (kompatibilita
s predkami nie potomkami)
- medzi inštanciami
- medzi formálnymi a skutočnými parametrami
Príklady:
máme deklarácie:
|
var
a,c:TKruh;
b:TStvorec;
|
potom je povolené:
|
a:=TKruh.Create(...);
b:=TStvorec.Create(...);
c:=b; // c referencuje tú istú inštanciu ako b
c:=TStvorec.Create(...);
procedure zmen(k:TKruh);
begin
k.ZmenFarbu(clBlue);
k.posun(1,1);
end;
|
a tiež môže byť volané:
|
zmen(b); //
|
===> formálny parameter k je polymorfný objekt (rôznorodý) ===> potomok môže kdekoľvek nahradiť predka (predok môže zastupovať potomka)
Polymorfný objekt
- keď do procedúry príde ako parameter
nejaký objekt, môže to byť vlastne ľubovoľný
potomok tohoto objektu (zatiaľ nevieme zistiť ktorý)
- umožňuje spracovať objekty, ktorých typ nie
je známy
- umožňuje využiť metódy predkov so špecifickými
zmenami - úpravami
Spôsob (metóda) návrhu hierarchie
objektov
- vytypujeme spoločné vlastnosti a metódy
=> bázová trieda (typ)
- rozhodneme, ktoré metódy budú
virtual
- pre budúceho používateľa umožníme
rozširovanie metód
- môžeme vyrobiť aj "prázdne"
virtuálne metódy - pre perspektívne
rozširovanie
- rozširovanie (rozšíriteľnosť = extensibility)
- umožňuje meniť niektoré časti bez zdrojových
textov
- vďaka virtuálnym metódam možno meniť
činnosť niektorých procedúr
príklad s polymorfným štvorcom by mal
vyzerať takto:
|
type
TKruh = class
...
procedure ukaz; virtual;
procedure skry; virtual;
...
end;
TStvorec = class(TKruh)
public
procedure ukaz; override;
procedure skry; override;
end;
|
použijeme:
|
var
a:TKruh;
b:TKruh;
c:TKruh;
...
procedure TForm1.Button2Click(Sender: TObject);
begin
b:=TStvorec.Create(300,200,60); b.ZmenFarbu(clRed); b.ukaz;
end;
|
príklad na polymorfné pole
zadefinujeme novú triedu TTroj - potomok
TKruh:
|
{ Trojuholnik }
type
TTroj = class(TKruh)
public
procedure ukaz; override;
procedure skry; override;
end;
procedure TTroj.ukaz;
begin
g.Pen.Color:=f;
g.Brush.Style:=bsClear;
g.PolyLine([Point(x-r,y),Point(x+r,y),Point(x,y-r),Point(x-r,y)]);
vid:=true;
end;
procedure TTroj.skry;
begin
g.Pen.Color:=clWhite;
g.Brush.Style:=bsClear;
g.PolyLine([Point(x-r,y),Point(x+r,y),Point(x,y-r),Point(x-r,y)]);
vid:=false;
end;
|
pole p bude polymorfné pole:
|
const
n=20;
var
p:array[1..n] of TKruh;
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
begin
g:=Image1.Canvas;
for i:=1 to n do p[i]:=nil;
randomize;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i,x,y,r:integer;
begin
for i:=1 to n do begin
x:=random(Image1.Width);
y:=random(Image1.Height);
r:=random(80)+10;
case random(3) of
0: p[i]:=TKruh.Create(x,y,r);
1: p[i]:=TStvorec.Create(x,y,r);
2: p[i]:=TTroj.Create(x,y,r);
end;
end;
for i:=1 to n do p[i].ukaz;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
poc:=100;
Timer1.Enabled:=true;
end;
procedure TForm1.Button5Click(Sender: TObject);
var
i:integer;
begin
for i:=1 to n do
if p[i]<>nil then
with p[i] do begin
if vid then skry;
Free;
p[i]:=nil;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
i:integer;
begin
Timer1.Enabled:=false;
for i:=1 to n do
if p[i]<>nil then p[i].posun(i mod 5-2,i mod 3-1);
dec(poc);
if poc>0 then Timer1.Enabled:=true;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i:integer;
begin
for i:=1 to n do p[i].Free;
end;
|
Poznámky:
- zatiaľ o prvkoch polymorfného poľa p nevieme
zistiť, akého sú naozaj typu
- vieme, že buď sú to inštancie triedy kruh
alebo jeho potomkovia
- každý prvok "sám seba vie"
správne ukázať, skryť a zafarbiť, posunúť
|