15. Programová jednotka KorUnit


Posledná zmena: 12.11.2002

Banner Text 12.11.2002

    čo sme sa doteraz naučili

    • trieda TKor je definovaná v unite KorUnit
    • definuje niekoľko stavových premenných (x, y, smer, FP, ...) a niekoľko metód (Create, Dopredu, ...)

    čo sa budeme dnes učiť

    • ako je naozaj definovaná trieda TKor - obsahuje súkromné stavové premenné a verejné stavové premenné typu "vlastnosť"
    • pri niektorých metódach je pripojené rezervované slovo override, overload alebo virtual
    • uvidíme spôsob, ako sa vykresľujú na obrazovku tvary korytnačiek

KorUnit

  • podrobne sa pozrieme na programovú jednotku KorUnit

deklarácia triedy TKor:

type
  TKor = class
  private
    FX,FY,FSmer:real;
    FDole:boolean;
    FFP:TColor;
    FHP:integer;
  public
    constructor Create; overload;
    constructor Create(nx,ny:real; uhol:real = 0); overload;
    destructor Destroy; override;

    procedure Dopredu(dlzka:real); virtual;
    procedure Vpravo(uhol:real); virtual;
    procedure Vlavo(uhol:real); virtual;
    procedure ZmenSmer(uhol:real); virtual;
    procedure ZmenXY(nx,ny:real); virtual;
    procedure PresunXY(nx,ny:real); virtual;
    procedure PH; virtual;
    procedure PD; virtual;
    procedure ZmenPero(ndole:boolean); virtual;
    procedure ZmenFP(farba:TColor); virtual;
    procedure ZmenHP(hrubka:integer); virtual;

    procedure Pis(text:string); virtual;
    function  Smerom(nx,ny:real):real; virtual;
    procedure Vypln(farba:TColor); virtual;
    function  Vzd(nx,ny:real):real; virtual;
    function  Blizko(nx,ny:real):boolean; virtual;

    property  X:real read FX;
    property  Y:real read FY;
    property  Smer:real read FSmer write ZmenSmer;
    property  Dole:boolean read FDole write ZmenPero;
    property  FP:TColor read FFP write ZmenFP;
    property  HP:integer read FHP write ZmenHP;
  end;
  • väčšinu vecí buď poznáme, alebo sme s nimi už pracovali; môžeme tu vidieť niekoľko noviniek:
    • všetky stavové premenné a metódy sú rozdelené do dvoch veľkých skupín: private a public
    • okrem konštruktora Create je tu definovaný aj deštruktor Destroy (s kľúčovým slovom override)
    • skoro za všetkými definíciami metód je kľúčové slovo virtual, ktorého význam sa budeme učiť neskôr
    • konštruktor Create má predvolené parametre (default hodnoty)
    • niektoré stavové premenné (napr. X, Y, Smer a pod.) sú uvedené kľúčovým slovom property - vlastnosť
  • postupne sa zoznámime s týmito novinkami

Privátne definície

  • vieme, že v programovej jednotke (unit) môžeme deklarácie uviesť nielen v interface časti, ale aj za implementation, a teda vytvoriť buď verejné definície alebo súkromné (len pre potreby unitu)
  • podobne je to pri definovaní triedy: definície stavových premenných a metód môžeme rozdeliť do dvoch skupín (neskôr uvidíme aj ďalšie) private a public: na súkromné, ktoré sú určené len pre potreby samotného objektu a zvonku sú neprístupné, a na verejné, ktoré zverejňujeme pre používateľov (programátorov) tejto triedy
    • to, že private stavové premenné a metódy sú zvonku neprístupné znamená, že ak nejakú triedu zadeklarujeme v inteface časti nejakého unitu, tak ostatné unity, ktoré túto deklaráciu vidia, môžu pracovať len s public deklaráciami ale v implementation časti samotného unitu sú všetky deklarácie rovnocenné
  • pri práci s formulárom (napr. v triede TForm1) môžeme vidieť, že niektoré prvky (stavové premenné a metódy) nie sú v ani jednej z týchto skupín - okrem týchto dvoch skupín je aj ďalšia: published, ktorá je skoro rovnaká ako public, ale zatiaľ pre jednoduchosť predpokladajme, že ju Delphi potrebujú pre komponenty a metódy, ktoré sú zviazané s nejakým formulárom

Predvolené parametre

  • pri volaní procedúry (aj metódy) môžem uviesť menší počet skutočných parametrov ako je formálnych
  • ak Delphi poznajú predvolené (náhradné) hodnoty týchto parametrov, tak nehlásia chybu, ale doplnia ich týmito hodnotami za nás - predvolenými hodnotami môžu byť len konštanty alebo konštantné výrazy (niečo, čo vie kompilátor vypočítať už počas kompilácie)
  • len hodnotové alebo const parametre môžu byť predvolené (default) - nie var-parametre
  • za predvoleným parametrom (v zozname formálnych parametrov) môžu nasledovať už len predvolené
  • korytnačku môžeme skonštruovať napr. takto:
       k:=TKor.Create(0,0,0);
       k:=TKor.Create(x,y);
  • predvolený parameter je aj v definícii procedúry zmaz:
       procedure zmaz(fpoz:TColor = clWhite);
  • t.j. keď je volaná bez parametrov, farbou pozadia fpoz bude biela, inak sa použije zadaný parameter

Viac variantov jednej procedúry

  • je to spôsob, pomocou ktorého môžeme nazvať rôzne procedúry jedným menom, resp. keď jedna procedúra má viac variantov - aby pascal pri volaní takejto procedúry správne rozhodol, ktorý variant má použiť, musia sa tieto varianty líšiť buď počtom parametrom alebo typmi parametrov
  • v deklaráciách triedy - v deklarácii metódy - za hlavičku procedúry zapíšeme rezervované slovo overload
    • toto slovo zapíšeme za všetky varianty
  • vidíme dve definície pre Create - oba varianty sa líšia počtom parametrov

Vlastnosti (property)

  • stavové premenné s kľúčovým slovom property - sú špeciálne atribúty triedy (nie sú to pamäťové položky triedy ako v zázname)
  • "skutočným" stavovým premenným je vyhradené nejaké pamäťové miesto (môžeme zistiť jej hodnotu, resp. ju meniť) - to je analógia položkám záznamov
  • "virtuálna" stavová premenná - vlastnosť musí mať priradené nejaké akcie na čítanie, resp. modifikovanie takejto stavovej premennej
  • pomocou tohoto mechanizmu môžeme kontrolovať prístup ku "skutočným" stavovým premenným alebo môžeme pre takéto stavové premenné vypočítať ich hodnotu, až keď budú požadované (čítané)
  • syntax:
       property meno:typ read  položka|metóda write položka|metóda;
  • položka|metóda je buď obyčajná položka (skutočná stavová premenná) objektu (hoci aj privátna) alebo nejaká (hoci aj privátna) metóda
  • typ položky sa musí zhodovať s typom vlastnosti
  • pre read - metóda musí byť bezparametrová funkcia rovnakého typu ako typ vlastnosti
  • pre write - metóda musí byť jednoparametrová procedúra s hodnotovým alebo const parametrom rovnakého typu ako typ vlastnosti
  • jedna z častí read alebo write môže chýbať => potom tento prístup nie je povolený (napr. nedá sa meniť hodnota stavovej premennej iba čítať)
  • vždy, keď sa v programe bude čítať resp. meniť hodnota takejto "virtuálnej" stavovej premennej - vlastnosti, tak sa buď prečíta hodnota, resp. priradí do položky, alebo sa zavolá príslušná metóda

napr. korytnačka má tieto vlastnosti:

    property X:real read FX;
    property Y:real read FY;
    property Smer:real read FSmer write ZmenSmer;
    property FP:TColor read FFP write ZmenFP;
    ...
  • čo znamená, že takúto stavovú premennú X nemôžeme meniť (napr. priraďovacím príkazom) - len čítať (vtedy dostaneme hodnotu súkromnej premennej FX)
  • vlastnosť Y je na tom rovnako
  • vlastnosť Smer môžeme čítať aj meniť - pri čítaní tejto premennej sa vráti hodnota súkromnej premennej FSmer, pri priradení do tejto premennej, napr. k.Smer:=115; sa zavolá metóda ZmenSmer(115);
  • rovnako funguje napr. aj vlastnosť FP (farba pera) - čítanie tejto premennej vráti hodnotu súkromnej premennej FFP a priradenie napr. k.FP:=clRed; zavolá k.ZmenFP(clRed);
  • V ďalšom príklade stavová premenná - vlastnosť prem v skutočnosti manipuluje so súkromnou premennou fprem, len pri jej čítaní sa automaticky zvýši počítadlo poc

počítadlo čítania premennej:

type
  xx=class  
  private
    fprem:real;
    poc:integer;
    function hodnota:real;
  public
    property prem:real read hodnota write fprem;
  end;

function xx.hodnota:real;
             // funkcia počíta počet prístupov ku stavovej premennej
begin
  Result:=fprem; inc(poc);
end;
  • neskôr uvidíme aj ďalšie možnosti stavových premenných - vlastností

Deštruktor Destroy

  • je podobný konštruktoru - automaticky sa zavolá pri metóde Free, t.j. keď rušíme objekt, môžeme na záver vykonať nejaké "upratovacie" akcie (napr. uvolniť bitmapu, skryť korytnačku a pod.)
  • volanie metódy obj.Free nejakého objektu obj si môžeme zjednodušene predstaviť ako
       if obj<>nil then obj.Destroy;
  • takýto Free funguje len s deštruktorom Destroy, hoci vo všeobecnosti by sme mohli mať viac deštruktorov s rôznymi menami

Konštruktor a deštruktor korytnačiek

  • Jednotka KorUnit má zadefinované dve súkromné premenné (sú deklarované iba v implementation časti):

globálne premenné:

var
  kory:array of TKor;
  maxkor:integer = -1;
  • v ktorých sa zapamätávajú všetky vytvorené korytnačky - tento mechanizmus slúži len ako ukážka práce s dynamickým poľom a v tejto verzii programovej jednotky KorUnit nemá veľký význam
  • dynamické pole kory slúži na zapamätanie si všetkých doteraz vytvorených korytnačiek
  • premenná maxkor obsahuje index maximálne priradenej korytnačky v poli kory - pole kory nezväčšujeme pri každej novej korytnačke o 1, ale raz začas zväčšíme o 10 a maxkory je vtedy menšie ako High(kory)
  • toto pole kory sa aktualizuje v TKor.Create a v TKor.Destroy
  • premenná g:TCanvas slúži na kreslenie do grafickej plochy
  • všimnite si, ako konštruktor Create inicializuje stavové premenné:

konštruktor:

constructor TKor.Create(nx,ny,uhol:real);
var
  i:integer;
begin
  Inicializuj;
  FX:=nx; FY:=ny; FSmer:=uhol;
  FFP:=clBlack; FHP:=1;
  FDole:=true;
  i:=0; while (i<=maxkor) and (kory[i]<>nil) do inc(i);
  if i>high(kory) then SetLength(kory, Length(kory)+10);
  kory[i]:=self; if i>maxkor then maxkor:=i;
end;
  • všimnite si ako druhý variant konštruktora bez parametrov volá svoj prvý variant s parametrami

druhý variant konštruktora:

constructor TKor.Create;
begin
  Inicializuj;
  Create(sirka/2,vyska/2);
end;
  • aj deštruktor Destroy pracuje s poľom kory:

deštruktor:

destructor TKor.Destroy;
var
  i:integer;
begin
  i:=0; while (i<=maxkor) and (kory[i]<>self) do inc(i);
  if i<=maxkor then kory[i]:=nil;
  inherited;
end;
  • inherited na záver metódy je ukážkou toho, že netreba zabudnúť spustiť "upratovacie" akcie aj pre triedu predka - v našom prípade je to zbytočné, lebo predok triedy TKor má prázdny deštruktor (trieda TKor je potomkom triedy TObject)

V jednotke KorUnit sú okrem triedy TKor v interface časti definované aj nejaké konštanty, premenné a procedúry (a teda sú viditeľné pre programy, ktoré majú uses KorUnit;):

  • Zmaz - má jeden predvolený parameter - farbu pozadia grafickej plochy - bez parametra zmaže grafickú plochu na bielo
  • Cakaj - pozdrží vykonávanie programu o zadaný počet milisekúnd - počas tohoto čakania sa vykonáva Application.ProcessMessages
  • premenné sirka a vyska obsahujú šírku a výšku korytnačej grafickej plochy

KorUnit nemá ani časť initialization ani finalization - lebo inicializácia premenných sa robí až pri prvom TKor.Create, resp. pri NastavPlochu

  • pozrime si inicializáciu, ktorá sa spustí pred prvým vyvolaním TKor.Create - jej najdôležitejšou úlohou je nájsť v našej aplikácii nejakú grafickú plochu (TImage) - nemá zmysel sa snažiť do detailov pochopiť, ako to funguje

inicializácia:

procedure Inicializuj;
var
  i,n:integer;
  f:TForm;
begin
  if g<>nil then exit;
  f:=Application.MainForm;
  if f=nil then begin
    n:=Application.ComponentCount; i:=0;
    while (i<n) and not (Application.Components[i] is TForm) do inc(i);
    if i=n then begin
      ShowMessage('vadná aplikácia - nenašiel som formulár');
      Halt;
    end;
    f:=TForm(Application.Components[i]);
  end;
  n:=f.ControlCount; i:=0;
  while (i<n) and not (f.Controls[i] is TImage) do inc(i);
  if i>=n then begin
    ShowMessage('vadná aplikácia - nenašiel som grafickú plochu');
    halt;
  end;
  NastavPlochu(TImage(f.Controls[i]));
end;
  • pomocná procedúra NastavPlochu si požadovanú grafickú plochu (TImage) zapamätá do premennej g, do premenných sirka a vyska si uloží jej rozmery a formuláru, v ktorom je grafická plocha nastaví DoubleBuffered na true

pomocná procedúra NastavPlochu:

procedure NastavPlochu(p:TImage);
begin
  if g<>nil then exit;
  if p.Owner is TForm then
    TForm(p.Owner).DoubleBuffered:=true;
  g:=p.Canvas;
  sirka:=p.Width; vyska:=p.Height;
end;

  • ak túto procedúru zavoláme s nejakou grafickou plochou pred prvým vytvorením korytnačky, tak táto sa stane plochou pre korytnačky - inak sa spúšťa mechanizmus automatického hľadania "prvej" grafickej plochy

príklad s vlastnou triedou mojKor

  • Malá ukážka potomka triedy TKor, ktorý dodefinuje metódu na kreslenie binárneho stromu:

odvodená trieda od TKor:

uses
  KorUnit;

type
  TMojaKor = class(TKor)
  public
    procedure strom(n:integer; s:real);
  end;

procedure TMojaKor.strom(n:integer; s:real);
begin
  dopredu(s);
  if n>1 then begin
    vlavo(45);
    strom(n-1,s*0.67);
    vpravo(90);
    strom(n-1,s*0.67);
    vlavo(45);
  end;
  dopredu(-s);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  k:TMojaKor;
begin
  k:=TMojaKor.Create(250,400);   // Create je zdedený
  k.strom(5,150);                // strom je metóda triedy TMojaKor
  k.Free;
end;
  • bolo by dobre, keby ste si zvykali dávať definície nových tried do samostatných programových jednotiek, napr. táto trieda by bola definovaná v unite mojaKorUnit a v unite s formulárom by sa iba používala:

mojaKorUnit.pas

unit mojaKorUnit;

interface

uses
  KorUnit;

type
  TMojaKor = class(TKor)
  public
    procedure strom(n:integer; s:real);
  end;

implementation

procedure TMojaKor.strom(n:integer; s:real);
begin
  dopredu(s);
  if n>1 then begin
    vlavo(45);
    strom(n-1,s*0.67);
    vpravo(90);
    strom(n-1,s*0.67);
    vlavo(45);
  end;
  dopredu(-s);
end;

end.

časť Unit1.pas

uses
  mojaKorUnit;

procedure TForm1.Button1Click(Sender: TObject);
var
  k:TMojaKor;
begin
  k:=TMojaKor.Create(250,400);   // Create je zdedený
  k.strom(5,150);                // strom je metóda triedy TMojaKor
  k.Free;
end;


© 2002 AB, KVI
blaho@fmph.uniba.sk