Kapitola 6 Delphi - objektovˇ e orientovan´ e programov´ an´ı Objektovˇ e orientovan´ e programov´ an´ı (zkracov´ano na OOP, z anglick´eho Object oriented programming) je metodika v´ yvoje softwaru, zaloˇzen´a na tˇechto myˇslenk´ach, koncepci:
6.1
Koncepce
Objekty – jednotliv´e prvky modelovan´e reality (jak data, tak souvisej´ıc´ı funkˇcnost) jsou v programu seskupeny do entit, naz´ yvan´ ych objekty. Objekty si pamatuj´ı sv˚ uj stav a navenek poskytuj´ı operace (pˇr´ıstupn´e jako metody pro vol´an´ı). Abstrakce – program´ator, potaˇzmo program, kter´ y vytv´aˇr´ı, m˚ uˇze abstrahovat od nˇekter´ ych detail˚ u pr´ace jednotliv´ ych objekt˚ u. Kaˇzd´ y objekt pracuje jako ˇcern´a skˇr´ıˇ nka, kter´a dok´aˇze prov´adˇet urˇcen´e ˇcinnosti a komunikovat s okol´ım, aniˇz by vyˇzadovala znalost zp˚ usobu, kter´ ym vnitˇrnˇe pracuje. Zapouzdˇ ren´ı – zaruˇcuje, ˇze objekt nem˚ uˇze pˇr´ımo pˇristupovat k vnitˇrn´ım parametr˚ um jin´ ych objekt˚ u, coˇz by mohlo v´est k nekonzistenci. Kaˇzd´ y objekt navenek zpˇr´ıstupˇ nuje rozhran´ı, pomoc´ı kter´eho (a nijak jinak) se s objektem pracuje. Skl´ ad´ an´ı – objekt m˚ uˇze vyuˇz´ıvat sluˇzeb jin´ ych objekt˚ u tak, ˇze je poˇz´ad´a o proveden´ı operace. Dˇ ediˇ cnost – objekty jsou organizov´any stromov´ ym zp˚ usobem, kdy objekty nˇejak´eho druhu mohou dˇedit z jin´eho druhu objekt˚ u, ˇc´ımˇz pˇreb´ıraj´ı jejich schopnosti, ke kter´ ym pouze pˇrid´avaj´ı svoje vlastn´ı rozˇs´ıˇren´ı. Tato myˇslenka se obvykle implementuje pomoc´ı rozdˇelen´ı objekt˚ u do tˇr´ıd, pˇriˇcemˇz kaˇzd´ y objekt je instanc´ı nˇejak´e tˇr´ıdy. Kaˇzd´a tˇr´ıda pak m˚ uˇze dˇedit od jin´e tˇr´ıdy (v nˇekter´ ych programovac´ıch jazyc´ıch i z nˇekolika jin´ ych tˇr´ıd). 1
ˇ ORIENTOVANE ´ PROGRAMOVAN ´ ´I 2 KAPITOLA 6. DELPHI - OBJEKTOVE Polymorfismus – odkazovan´ y objekt se chov´a podle toho, jak´ y je jeho skuteˇcn´ y typ. Pokud nˇekolik objekt˚ u poskytuje stejn´e rozhran´ı, pracuje se s nimi stejn´ ym zp˚ usobem, ale jejich konkr´etn´ı chov´an´ı se liˇs´ı. V praxi se tato vlastnost projevuje napˇr. tak, ˇze na m´ısto, kde je oˇcek´av´ana instance nˇejak´e tˇr´ıdy, m˚ uˇzeme dosadit i instanci libovoln´e jej´ı podtˇr´ıdy (tˇr´ıdy, kter´a pˇr´ımo ˇci nepˇr´ımo z t´eto tˇr´ıdy dˇed´ı), kter´a se m˚ uˇze chovat jinak, neˇz by se chovala instance rodiˇcovsk´e tˇr´ıdy, ovˇsem v r´amci mantinel˚ u, dan´ ych popisem rozhran´ı. Pokud pˇredchoz´ı koncepci jiˇz rozum´ıme, bude pro n´as pochopen´ı objektovˇe orientovan´eho programov´an´ı jednoduch´e. Pokud vˇsak nerozum´ıme, coˇz se pˇredpokl´ad´a, nevad´ı. K dalˇs´ımu vysvˇetlen´ı n´am vˇrele pom˚ uˇze uk´azkov´ y pˇr´ıklad.
6.2
Uk´ azkov´ y pˇ r´ıklad
Nejprve si pˇredstavme, ˇze cel´ y svˇet se skl´ad´a z objekt˚ u. Objektem m˚ uˇzeme rozumˇet mˇesta, ulice, domy, stromy, auta, silnice, atd. Aby se n´am tyto objekty nepletli, rozdˇel´ıme si je na r˚ uzn´e druhy, resp. r˚ uzn´e tˇ r´ıdy. V programov´an´ı b´ yv´a nejˇcastˇejˇs´ım uk´azkov´ ym pˇr´ıkladem tˇr´ıd tˇr´ıda aut. Pˇredstavme si auto (objekt). Kaˇzd´e auto, pokud neuvaˇzujeme nˇekter´e speci´aln´ı pˇr´ıpady, je pˇresnˇe urˇceno st´atn´ı pozn´avac´ı znaˇckou, respektive p´ısmeny a ˇc´ıslicemi na SPZ. M˚ uˇzeme tedy ˇr´ıci, ˇze SPZ je charakteristick´a vlastnost auta, neboli vlastnost objektu. Pochopitelnˇe m´a kaˇzd´e auto mnohem v´ıce vlastnost´ı, jako je napˇr´ıklad znaˇcka, barva, typ, objem motoru, atd.
6.3
Definice tˇ r´ıdy
Nadefinujme si tedy tˇr´ıdu aut, tak aby obsahovala tyto vlastnosti: spz, typ, objem Motoru. Tˇr´ıdu aut, jak je v Delphi zvykem, nazveme TAuto. V t´eto tˇr´ıdˇe budeme uvaˇzovat spz a typ jako ˇretˇezce a objemMotoru jako celoˇc´ıselnou hodnotu. Syntaxe t´eto tˇr´ıdy je n´asleduj´ıc´ı: type TAuto = class spz, typ: String; objemMotoru: Integer; end; Tuto definici nov´e tˇr´ıdy TAuto um´ıst´ıme v Delphi do oblasti, kde definujeme nov´e typy, tedy za pˇr´ıkaz type. Kaˇzd´a nov´a tˇr´ıda se zad´av´a stejn´ ym zp˚ usobem: jmenotridy = class, kde class n´am ud´av´a, ˇze se jedn´a o tˇr´ıdu. Po v´ yˇctu vlastnost´ı tˇr´ıdy, ukonˇc´ıme definici pˇr´ıkazem end;, jak je vidˇet v´ yˇse.
ˇ ´I OBJEKTU 6.4. VYTVOREN
6.4
3
Vytvoˇ ren´ı objektu
Nyn´ı m´ame nadefinovanou tˇr´ıdu TAuto, ale zat´ım nem´ame ˇz´adn´ y objekt. Pˇredstavme si, ˇze vlastn´ıme dva automobily (objekty): sluˇzebn´ı a soukrom´ y. Aby jsme mohli tyto objekty vytvoˇrit, mus´ıme si je nejdˇr´ıve deklarovat. Objekty budeme deklarovat stejnˇe jako promˇenn´e v ˇc´asti programu urˇcen´e k deklaraci (tedy za kl´ıˇcov´ ym slovem var) a tyto objekty budou typu TAuto. var sluzebniAuto, soukromeAuto: TAuto; Abychom mohli s objekty pracovat, je zapotˇreb´ı je nejdˇr´ıve vytvoˇrit. Vytvoˇren´ı se provede pomoc´ı konstruktoru (konstruktor bude vysvˇetlen pozdˇeji) t´ımto zp˚ usobem: sluzebniAuto := TAuto.Create; soukromeAuto := TAuto.Create; Takto byli vytvoˇreny dva objekty sluzebniAuto a soukromeAuto. Pˇresnˇeji ˇreˇceno, jsme si uvolnily v pamˇeti poˇc´ıtaˇce m´ısto pro tyto objekty a nyn´ı s nimi m˚ uˇzeme pracovat. V objektovˇe orientovan´em programov´an´ı je zapotˇreb´ı st´ale pamatovat na to, ˇze objekty mus´ıme nejdˇr´ıve vytvoˇrit a aˇz pak s nimi m˚ uˇzeme pracovat. Jestliˇze uˇz objekt k pr´aci nepotˇrebujeme, je zapotˇreb´ı objekt zruˇsit, pˇresnˇeji uvolnit zabran´e m´ısto v pamˇeti poˇc´ıtaˇce. To se provede pˇr´ıkazem Free n´asledovnˇe: sluzebniAuto.Free; soukromeAuto.Free; Po tomto pˇr´ıkazu jiˇz naˇse objekty neexistuj´ı, nem˚ uˇzeme tedy s nimi pracovat.
6.5
Vlastnosti objektu
Vrat’me se nyn´ı trochu zpˇet a pˇredpokl´adejme, ˇze m´ame vytvoˇreny objekty sluzebni Auto a soukromeAuto. Tyto objekty jsou typu TAuto a tud´ıˇz obsahuj´ı vlastnosti spz, typ a objemMotoru. Pokud chceme pˇristupovat k vlastnostem objektu, budeme k nim pˇristupovat pomoc´ı teˇcky. Pokud tedy chceme zadat vlastnosti naˇsich aut, udˇel´ame to takto: sluzebniAuto.spz := ’1B0 25-69’; sluzebniAuto.typ := ’ˇ Skoda’; sluzebniAuto.objemMotoru := 1600; soukromeAuto.spz := ’2B3 85-31’; soukromeAuto.typ := ’Porshe’; soukromeAuto.objemMotoru := 3200;
ˇ ORIENTOVANE ´ PROGRAMOVAN ´ ´I 4 KAPITOLA 6. DELPHI - OBJEKTOVE Naopak, pokud budeme cht´ıt vypsat do dialogov´eho okna typ naˇseho sluˇzebn´ıho auta, nap´ıˇseme tento pˇr´ıkaz: MessageDlg(’Typ m´ eho soukrom´ eho auta je: ’ + soukromeAuto.typ, mtInformation, [mbOK], 0);
6.6
Metody objektu
Dosud jsme si vysvˇetlili, ˇze kaˇzd´ y objekt m˚ uˇze obsahovat r˚ uzn´e vlastnosti a my k nim pak jednoduˇse pomoc´ı teˇcky pˇristupujeme. Nab´ız´ı se ot´azka: ”K ˇcemu je to dobr´e?”, kdyˇz stejnˇe tak funguje v Delphi z´aznam, neboli record. Vysvˇetlen´ı je jednoduch´e. Objekty n´am nab´ızej´ı mnohem v´ıce moˇznost´ı a jednou z nich jsou metody, neboli funkce ˇci procedury uvnitˇr objektu. Vrat’me se zpˇet k naˇsim aut˚ um a chtˇejme sledovat stav benz´ınu v n´adrˇzi pomoc´ı vlastnosti palivo. D´ale budeme pˇredpokl´adat, ˇze mnoˇzstv´ı paliva ub´ yv´a, pokud ujedeme nˇejakou vzd´alenost v z´avislosti na spotˇrebˇe (spotreba) a pˇrib´ yv´a, pokud tankujeme na ˇcerpac´ı stanici. Tyto dva poznatky n´am zaruˇc´ı procedury Ujed a Tankuj. Deklarace tˇr´ıdy TAuto bude vypadat n´asledovnˇe: type TAuto = class spz, typ: String; objemMotoru: Integer; palivo, spotreba: Double; procedure Ujed(vzdalenost: Double); procedure Tankuj(mnozstviPaliva: Double); end; D´ale je zapotˇreb´ı vytvoˇrit tˇela naˇsich nov´ ych dvou procedur. To provedeme kdekoliv v ˇc´asti zdrojov´eho textu, tedy za pˇr´ıkazem implementation, jak jsme zvykl´ı. Procedury budou vypadat n´asledovnˇe: procedure TAuto.Ujed(vzdalenost: Double); begin palivo := palivo - vzdalenost * (spotreba / 100); end; procedure TAuto.Tankuj(mnozstviPaliva: Double); begin palivo := palivo + mnozstviPaliva; end;
6.7. KONSTRUKTOR A DESTRUKTOR OBJEKTU
5
Nyn´ı si vˇsimnˇeme, ˇze pˇri definici nov´ ych procedur jsme jako n´azev uvedli TAuto. Ujed, resp. TAuto.Tankuj m´ısto pouh´eho Ujed, resp. Tankuj. D˚ uvod je velmi jednoduch´ y. Pokud bychom v programu mˇeli v´ıce tˇr´ıd, kter´e by obsahovaly proceduru Ujed a pˇritom v kaˇzd´e tˇr´ıdˇe by procedura mˇela jinou definici, nastal by pˇri pˇrekladu zdrojov´eho textu zmatek. Dalˇs´ı d˚ uleˇzit´ y poznatek je v promˇenn´e palivo. Vˇsimnˇeme si, ˇze neuv´ad´ıme napˇr. soukromeAuto.palivo, ale pouze palivo. Jak tedy program pozn´a jak´e palivo m´a zmˇenit? Jednoduˇse. Zmˇen´ı palivo toho objektu, na kter´em byla dan´a procedura vol´ana. Tedy pokud vol´ame nad objektem soukrom´eho auta (soukromeAuto. Ujed(50)), odeˇcte poˇzadovanou hodnotu z paliva tohoto objektu, tedy z soukromeAuto. palivo.
6.7
Konstruktor a destruktor objektu
Uˇz dˇr´ıve jsme se zm´ınili o konstruktoru. Pouˇz´ıv´a se k tomu, aby n´am vytvoˇril objekt, ˇ resp. aby alokoval m´ısto v pamˇeti pro tento n´aˇs objekt. Casto je vˇsak velmi vhodn´e, kdyˇz uˇz pˇri vytv´aˇren´ı objektu specifikujeme nˇekter´e vlastnosti, v naˇsem pˇr´ıpadˇe spz, typ, objemMotoru, palivo a spotreba. Doc´ıl´ıme toho tak, ˇze pˇredefinujeme klasick´ y konstruktor Delphi. Do definice tˇr´ıdy TAuto pˇrid´ame zm´ınku o tom, ˇze budeme konstruktor vytv´aˇret vlastn´ı. Definice tˇr´ıdy pak bude vypadat: type TAuto = class spz, typ: String; objemMotoru: Integer; palivo, spotreba: Double; constructor Create(sspz, ttyp: String; oobjemMotoru: Integer; sspotreba: Double); procedure Ujed(vzdalenost: Double); procedure Tankuj(mnozstviPaliva: Double); end; Konstruktor je uveden kl´ıˇcov´ ym slovem constructor a n´azev m˚ uˇze b´ yt jak´ ykoliv, avˇsak je vhodn´e n´azev ponechat tak, jak je zvykem, tedy Create. Definice samotn´eho konstruktoru se opˇet prov´ad´ı v ˇc´asti implementace a m˚ uˇze vypadat takto: constructor TAuto.Create(sspz, ttyp: String; oobjemMotoru: Integer; sspotreba: Double); begin inherited Create;
ˇ ORIENTOVANE ´ PROGRAMOVAN ´ ´I 6 KAPITOLA 6. DELPHI - OBJEKTOVE spz := sspz; typ := ttyp; objemMotoru := oobjemMotoru; palivo := 0; spotreba := sspotreba; end; Pˇr´ıkaz inheroted Create n´am slouˇz´ı k vlastn´ımu vytvoˇren´ı objektu, dalˇs´ı pˇr´ıkazy jsou snad zˇrejm´e a jednoduch´e. Pokud tedy budeme cht´ıt vytvoˇrit opˇet sluˇzebn´ı a soukrom´ y v˚ uz, budeme postupovat takto: sluzebniAuto := TAuto.Create(’1B0 25-69’, ’ˇ Skoda’, 1600, 7.8); soukromeAuto := TAuto.Create(’2B3 85-31’, ’Porshe’, 3200, 11.4); Nˇekdy je zapotˇreb´ı nˇeco prov´est, neˇz se objekt zruˇs´ı. Tˇreba uloˇzit d˚ uleˇzit´a data do souboru. K tomu n´am hravˇe poslouˇz´ı pˇredefinov´an´ı destruktoru. Je to stejn´e jako v pˇr´ıpadˇe konstruktoru, pouze kl´ıˇcov´e slovo je destructor a je bˇeˇzn´e pouˇz´ıv´an´ı n´azvu Destroy. Destruktor pak m˚ uˇze vypadat takto: destructor TAuto.Destroy; begin UlozeniDat; inherited Destroy; end; Pozorn´ y ˇcten´aˇr si vˇsimne, ˇze v´ yˇse jsme k zruˇsen´ı objektu pouˇzili pˇr´ıkazu Free, nikoliv Destroy. Bylo to z d˚ uvodu, ˇze procedura Free nejdˇr´ıve zjist´ı zda dan´ y objekt existuje a pokud existuje, tak pak teprve vol´a destruktor tˇr´ıdy Destroy.
6.8
Dˇ ediˇ cnost
Dˇediˇcnost je dalˇs´ı d˚ uleˇzitou vlastnost´ı pˇri objektovˇe orientovan´em programov´an´ı. Pˇredstavme si, ˇze m´ame jiˇz vytvoˇrenou tˇr´ıdu TAuto, kter´e splˇ nuje poˇzadavky pro veˇsker´a auta. Nyn´ı vˇsak budeme cht´ıt vytvoˇrit tˇr´ıdy pro auta osobn´ı a n´akladn´ı, kter´e budou m´ıt stejn´e vlastnosti jako tˇr´ıda TAuto. Nav´ıc u tˇr´ıdy TOsobniAuto budeme poˇzadovat vlastnost pocetMist a u tˇr´ıdy TNakladniAuto budeme poˇzadovat vlastnost nosnost. D´ıky dˇediˇcnosti v OOP je definice tˇechto tˇr´ıd velmi jednoduch´a: type TOsobniAuto = class(TAuto) pocetMist: Integer; end; TNakladniAuto = class(TAuto) nosnost: Integer; end;
ˇ CNOST ˇ 6.8. DEDI
7
Jak je vidˇet, syntaxe nov´ ych tˇr´ıd je velmi jednoduch´a, staˇc´ı pouze do z´avorky za pˇr´ıkaz class napsat jm´eno tˇr´ıdy, po kter´e bude nov´a tˇr´ıda dˇedit vlastnosti a metody. Znamen´a to tedy, ˇze napˇr. nov´a tˇr´ıda TOsobniAuto obsahuje vlastnosti spz, typ, objemMotoru, palivo, spotreba a pocetMist a metody Ujed a Tankuj. Pokud chceme aby vlastnost pocetMist byla zad´av´ana jiˇz pˇri vytv´aˇren´ı objektu osobn´ıho auta, mus´ıme zmˇenit konstruktor tˇr´ıdy TOsobniAuto. To udˇel´ame obdobnˇe, jako jsme to dˇelali ve tˇr´ıdˇe TAuto.