Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Huňka, CSc. 1
Seznam kapitol 1 Základní pojmy...3 1.1 Úvod do tříd a objektů...3 1.2 Vývojové prostředí...7 1.3 Jednotný modelovací jazyk (Unified Modeling Language)...8 1.3.1 Diagram případů užití...9 1.3.2 Diagram tříd...10 1.3.3 Interakční diagramy...11 1.3.4 Stavový diagram...12 1.3.5 Diagram aktivit...12 1.3.6 Fyzické diagramy...13 2 Třída...18 2.1 Třída & instance (objekt)...18 2.2 Zprávy a metody mechanismus posílání zpráv...18 2.3 Primitivní a objektové (referenční) datové typy...22 2.4 Metody třídy...23 3 Třídy a objekty detailnější pohled...28 3.1 Klíčové slovo (pseudoproměnná) this...30 3.2 Klíčové slovo final a jeho použití...31 3.3 Statické atributy, statické metody třídní atributy (proměnné), třídní metody...32 3.4 Metoda main...36 4 Skládání objektů kompozice & agregace. Přetěžované konstruktory...40 4.1 Sémantika agregace...41 4.2 Sémantika kompozice...42 4.3 Deklarace tříd s přetíženými konstruktory...49 5 Návrhové vzory (Design Patterns)...53 5.1 Přepravka...53 5.2 Singleton jedináček...55 6 Balíčky, zapouzdření, samostatná aplikace JAR soubory...58 6.1 Balíčky a příkaz import...58 6.2 Samostatná aplikace JAR soubory...61 7 Dědičnost. Vztahy mezi nadtřídou a podtřídami. Konstruktory v podtřídách. Třída Object...63 7.1 Ukrývání informací v hierarchii tříd...64 7.2 Překrývání (zastiňování) metod další specializace metod...68 7.3 Třída Object hierarchie tříd v javovských balíčcích, abstraktní třídy a metody...68 8 Polymorfismus. Abstraktní třídy a metody. Rozhraní tvorba a použití...72 8.1 Abstraktní třída a abstraktní metody...74 8.2 Rozhraní - tvorba, použití...75 9 Využití polí pro ukládání objektů. Realizace zásobníku a fronty s využitím polí...80 10 Návrhové vzory pokračování...90 10.1 Jednoduchá tovární metoda Simple Factory Metod...90 10.2 Návrhový vzor State (stav)...92 10.3 Návrhový vzor Proxy...98 10.4 Návrhový vzor Command...99 Příklady k samostatnému zpracování a odeslání emailem ke kontrole...103 2
1 Základní pojmy 1.1 Úvod do tříd a objektů V OOP se na rozdíl od klasického pojetí operuje pouze s objekty, které popisují jak datovou, tak i procesní stránku modelované problematiky. Objekt je určitá jednotka, která modeluje nějakou část reálného světa a z funkčního pohledu víc odpovídá malému kompaktnímu programu, než jedné proměnné příslušného datového typu, i když z programátorského pohledu takovou proměnnou je. Objektový systém je potom souborem takových vzájemně integrujících malých programových celků. Datová povaha objektu je dána tím, že objekty se skládají z příslušných vnitřních dat složek (atributů), což jsou v rozumném případě opět jiné objekty. Funkční povaha každého objektu je dána tím, že každý objekt má jakoby okolo svých vnitřních dat obal či zeď, která je tvořena množinou samostatných částí kódu, jenž jsou nazývány metodami. Metody slouží k tomu, aby popisovaly, co daný objekt dokáže udělat se svými složkami. Se složkami (atributy) daného objektu lze manipulovat (číst, nastavovat, měnit) pouze pomocí kódu nějaké metody tohoto objektu. Každý objekt dovoluje provádět jen ty operace, které povoluje jeho množina metod. Proto se hovoří o zapouzdření dat uvnitř objektů. Množina povolených operací s objektem se nazývá protokol objektu, což je také z hlediska vnějšího systému jeho jediný a plně postačující popis (charakteristika). Popis vnitřní struktury objektu (data) je vzhledem ke svému zapouzdření a závislosti na metodách z hlediska vnějšího systému nedůležitý. V objektovém modelu výpočtu se pracuje pouze se dvěma možnými operacemi s objekty. První z nich je pojmenování nějakého objektu (přiřazení k proměnné). Druhou je tzv. poslání zprávy. Zpráva představuje žádost o provedení operace - metody nějakého objektu. Součásti zprávy mohou být parametry zprávy, což jsou vlastně data - objekty, které představují dopředný datový tok (ve směru šíření zprávy) směrem k objektu přijímajícímu danou zprávu. Poslání zprávy má za následek provedení kódu jedné z metod objektu, který zprávu přijal, tak tento zmíněný kód také většinou dává nějaký výsledek v podobě nějakých dat - objektů, které představují zpětný datový tok ve směru od objektu - příjemce zprávy k objektu - vysílači zprávy (tj. v opačném směru k šíření zprávy). Vzhledem k možnostem kódů metod se výsledky po poslaných zprávách neomezují pouze na hodnoty jednotlivých složek objektů, protože jsou dány libovolně složitým výrazem příslušné metody nad množinou všech složek objektu sjednocenou s množinou parametrů zprávy. Zpráva je žádost, aby objekt provedl jednu ze svých operací. Zpráva specifikuje o jakou operaci se jedná, ale ne jak by se operace měla provést. Objekt, jemuž je posílána zpráva, určuje, jak provést požadovanou operaci. Běžící objektově orientovaný program je tvořen soustavou mezi sebou navzájem komunikujících objektů, který je řízen především sledem vnějších událostí z rozhraní programu. Hlavní program v objektové aplikaci tvoří jen vytvoření příslušné instance (nebo instancí) a zaslání těmto instancím odpovídajících zpráv. Hlavní program bývá tedy velmi krátký za předpokladu, že využíváme knihovny tříd. Obecně se model posílání zpráv popisuje pomocí okamžiku určení kódu, kterým se provede určitá operace. Rozlišujeme tedy tzv. pozdní a brzkou vazbu kódu. Chápe se tím doba, kdy je 3
znám kód metody, kterou se provede činnost způsobena posláním zprávy. V objektových systémech se setkáváme především s pozdní vazbou - kód operace je určen až za běhu programu, v okamžiku, kdy objekt začne provádět vyžádanou činnost. Při statickém chápání volání podprogramu je naopak kód operace znám již v době překladu programu - jedná se o brzkou vazbu. Polymorfismus Koncept posílání zprávy a vykonání metody nahrazuje koncept volání funkce (podprogramu) v klasických výpočetních modelech. Na rozdíl od volání funkce je tu však v případě použití pozdní vazby od sebe odlišen požadavek (to je poslání zprávy) a jeho provedení (to je vykonání metody) objektem přijímajícím zprávu, což dovoluje posílat stejnou zprávu různým objektům s různým účinkem. Takové objekty jsou potom z pohledu těchto zpráv navzájem zaměnitelné. Pro příklad uveďme zprávu dej svoji velikost. Pošleme-li ji objektu, který reprezentuje nějakou osobu, tak sdělí její výšku. Pošleme-li ji objektu představujícímu množinu nějakých číselných hodnot, sdělí jejich počet, pošleme-li ji objektu představujícímu nějaký obrázek, sdělí jeho rozměr atd. Je tomu tak proto, že všechny uvedené objekty jsou polymorfní a zpráva dej velikost může být poslána kterémukoliv z nich. Důležitá je tu ta skutečnost, že o výběru odpovídající metody na poslanou zprávu rozhoduje přijímací objekt, což znamená, že vysílací objekt (nebo část programu) se o detaily zpracování nemusí starat. Polymorfismus tedy v objektovém programování znamená, že ta samá zpráva může být poslaná rozličným objektům bez toho, že by nás při jejím poslání zajímala implementace odpovídajících metod a datových struktur objektu - příjemce zprávy; každý objekt může reagovat na poslanou zprávu po svém svoji metodou. Programátor není nucen brát ohled u významově podobných operací na to, že jsou požadovány od různých objektů. Výhody objektově orientovaného přístupu Jako výhody objektově orientovaného přístupu je často uváděno: blízkost chápání reálného světa, stabilita návrhu, znuvupoužitelnost. Blízkost chápání reálného světa V objektově orientovaném přístupu se vyjadřujeme a pracujeme v pojmech reálného světa a to se neliší od způsobu chápání reálného světa. Objektově orientovaná analýza je založena na pojmech, které jsme se nejprve učili ve školce; objekty, atributy, třídy, celky a části. Programování je chápáno jako proces modelování viz obr. 1.1. Na levé straně obrázku je referenční systém, který tvoří doménu, kterou modelujeme a na pravé straně je počítačový systém počítačová reprezentace systému reálného světa. Systém reálného světa (referenční systém) je složen z jevů (jednotlivin) reálného světa. Tyto jevy, jednotliviny představují konkrétní věci reálného světa. Tato tužka, tamto stojící zelené auto, modré tričko, které mám na sobě atd. Pomocí jevů se vyjadřují ty nejmenší děti. Koncepty se získají zobecněním (abstrakcí) jevů (jednotlivin). Například když řekneme tužka, nemáme na mysli konkrétní tužku, ale objekt splňující dané vlastnosti. Stejně tak auto jako 4
koncept pro nás může představovat nějaké osobní auto (třeba i dané značky) reprezentující dané vlastnosti. Jev je věc, která má danou individuální existenci v reálném světě, nebo v mysli; cokoli reálného. Koncept je zevšeobecňující představa kolekce jevů, založena na znalostech společných vlastností jevů v kolekci. Programování jako proces modelování modelování koncepty specifikující problém realizované koncepty (třídy) abstrakce abstrakce jevy objekty (instance) referenční systém model systému Obr. 1.1 Programování jako proces modelováni Na úrovni modelu systému musí existovat odpovídající prvky, aby se reálný systém mohl realizovat v počítačovém modelu. Tyto prvky jsou představovány objekty (někdy se používá také označení instance). Jevy reálného světa jsou modelovány do objektů počítačového světa. Abstrakcí těchto objektů dostáváme tzv. realizované koncepty, kterým se v počítačové terminologii říká třídy. Třída reprezentuje množinu objektů stejného typu. Jak je z obrázku názorně vidět, koncepty reálného světa jsou modelovány do tříd modelu systému. Stabilita návrhu Místo zaměření se na funkcionalitu systému, je prvním krokem vytvoření fyzikálního modelu reálného světa odpovídající dané aplikaci. Tento model potom vytváří základ pro různé funkce, které systém může mít. Tyto funkce mohou být později měněny a mohou být 5
dodávány nové funkce beze změny základního modelu. Objektově orientované programování poskytuje přirozenou kostru pro modelování aplikační domény. Znovupoužitelnost Jedním z dobře známých problémů tvorby software je schopnost znova použitelnosti programových komponent, když se vytváří nové komponenty. Zde máme na mysli úroveň implementace. Funkcionalita existující komponenty je často velmi podobná té, kterou potřebujeme pro nový systém. Z toho důvodu je nová komponenta často implementována kopírováním a modifikací existujícího kódu. To ale znamená, že musí být znova testována. Více problémů navíc způsobuje to, že se ztrácí vztah mezi starou a novou komponentou; pokud se detekuje chyba v jedné, musí být opraveny obě. Jednou z výhod OOP je, že mají silné konstrukce pro inkrementální programovou modifikaci. Je možné definovat novou komponentu jako inkrementální rozšíření již existující komponenty a udržet tak vztah mezi oběma komponentami. Schopnost vytvářet programy inkrementálním rozšiřováním je považována za jednu z hlavních výhod OOP. Nevýhodou inkrementální modifikace je, že knihovny komponent odráží historický vývoj těchto komponent. Navíc vztahy mezi komponenty jsou především diktovány potřebou pro maximálním sdílení kódu, což je často v konfliktu s požadavky modelování. Celý objektově orientovaný přístup budeme ilustrovat s využitím objektově orientovaného jazyka Java. V tomto jazyce bývá zvykem, že nejdříve deklarujeme všechny třídy (většinou každou uložíme do samostatného souboru) a pak deklarujeme třídu s příponou test, která vytvoří objekty (instance) od deklarovaných tříd a zasílá objektům odpovídající zprávy, které vyvolávají příslušné metody a dochází k běhu programu. Pokud deklarujeme např. třídu Hello, bude mít pak třída, která aplikaci spustí název HelloTest. První příklad Jak jinak začít, než pozdravením: /** * Class Hello bude uložena v souboru Hello.java */ class Hello //Metoda, která dělá tu práci public void go() System.out.println("Hello, world"); /** * třída HelloTest uložena v souboru HelloTest.java * main method hlavní metoda pro spuštění celé aplikace */ class HelloTest public static void main(string[] args) Hello hi = new Hello(); // vytvoření objektu hi třídy Hello hi.go(); //vykonání metody go zaslání zprávy go objektu hi 6
třída Hello obsahuje pouze metodu go, jejíž jedinou operací je tisk textu Hello World, třída HelloTest deklaruje a vytváří objekt hi a pak zašle zprávu go objektu hi (tím se provede příslušná operace Význam jednotlivých částí definice třídy je následující: public Je klíčové slovo a označuje, že třída je veřejná a že s ní proto může pracovat kdokoli. Je sice možné definovat třídu i bez použití klíčového slova public, ale tuto možnost zatím využívat nebudeme. class Toto klíčové slovo označuje, že za ním bude následovat deklarace třídy. Hello Je název nebo identifikátor třídy, který musí splňovat pravidla pro tvorbu identifikátorů v jazyce Java. Dále název veřejné třídy musí být shodný s názvem souboru, v němž je uložen zdrojový kód deklarace veřejné třídy. Soubor se zdrojovým kódem musí mít příponu.java. V jednom souboru tedy může být nanejvýš zdrojový kód jedné veřejné třídy. Deklarace nejjednodušší třídy je následující: public class TridaA Celý předchozí příklad může být samozřejmě uveden v jedné třídě a pak kód vypadá následovně: class Hello //Method that does the work public void go() System.out.println("Hello, world"); /** * main method for testing outside BlueJ */ public static void main(string[] args) Hello hi = new Hello(); hi.go(); 1.2 Vývojové prostředí Protože nedílnou součástí tohoto kurzu je jazyk UML (viz dále), pro tvorbu aplikací používáme vývojové prostředí BlueJ. Toto prostředí je výhodné zejména pro výuku objektově orientovaného programování a je ke stažení na adrese http://www.bluej.org. Mezi jeho charakteristiky patří: jednoduchost, 7
názornost slučuje možnost klasického textového zápisu programu s možností definice jeho architektury v grafickém prostředí. Grafické prostředí splňuje požadavky diagramu tříd jazyka UML. Toto prostředí je schopno vytvořit na základě grafického návrhu kostru programu a průběžně zanášet změny v programu do jeho grafické podoby a naopak změny v grafickém návrhu zanášet do textové podoby. interaktivnost umožňuje přímou práci s objekty. Ve vývojových prostředích se pracuje s projekty, které od samostatných programů mohou obsahovat jeden, nebo více programů (podle požadavků programátora. 1.3 Jednotný modelovací jazyk (Unified Modeling Language) Unified Modeling Language (UML) je standardní jazyk pro specifikaci, zobrazení (vizualizaci) vytváření a dokumentaci programových systémů, stejně také pro business modeling a jiné neprogramové systémy. UML je nejrozšířenější schéma grafické reprezentace pro modelování objektově orientovaných systémů. UML reprezentuje kolekci nejlepších inženýrských zkušeností, jaké byly úspěšně prověřeny v modelování rozsáhlých složitých systémů. UML využívá hlavně grafickou notaci pro vyjádření návrhu programových projektů. Používání UML pomáhá projektovým týmům komunikovat, zkoumat potenciální návrhy a prověřovat návrh architektury programovým systémům. Cíle UML 1. Poskytnout uživatelům jednoduchý, expresivní vizuální modelovací jazyk, aby mohly vyvíjet a měnit smysluplné modely. 2. Poskytnout mechanismus na rozšíření a další specifikaci základních konceptů. 3. Být nezávislý na konkrétním programovacím jazyku a vytvářených procesech. 4. Poskytnout formální základ pro porozumění jazyku modelování. 5. Podpořit vysoce úrovňové rozvojové koncepty jako spolupráce, programové balíčky (frameworks), vzory (patterns) a komponenty. 6. Integrovat nejlepší zkušenosti. Typy diagramů UML Každý UML diagram je navržen, aby dovolil vývojářům a zákazníkům mít pohled na programový systém z různých perspektiv a z měnících se stupňů abstrakce. UML diagramy obecně vytváření vizuální modelovací prostředky, které zahrnují: Diagram případů užití use case diagram Diagram tříd class diagram Interakční diagramy Sekvenční diagram Diagram spolupráce Stavový diagram 8
Diagram aktivit Fyzické diagramy Diagram komponent Diagram rozmístění deployment diagram 1.3.1 Diagram případů užití Use case (případ užití) je specifikace posloupnosti činností (včetně měnících se a chybových posloupností, které systém může vykonávat prostřednictvím interakce (vzájemného působení) s vnějšími účastníky. Případ užití je něco, co účastník (aktor) od systému očekává. Je to případ užití systému specifickým účastníkem. Případy užití jsou vždy iniciovány účastníkem. Případy užití jsou vždy napsány z pohledu účastníka. Use case diagram (diagram případů užití) zobrazuje vztah mezi účastníky a případy užití (use cases). Use case diagram má dvě hlavní komponenty: Use cases (případy užití) Účastníky - aktory (actors) U s e C a s e A c t o r Účastník - aktor reprezentuje uživatele, nebo jiný systém, který bude v interakci (vzájemném působení) se systémem, který modelujete. Use case (případ užití) je externí pohled systému, který reprezentuje nějakou činnost, kterou uživatel může vykonávat, aby dokončil úlohu. 9
Název systému S y s t é m o b j e d n á v e k p o š t o u Hranice systému Komunikační relace * Z a d a t O b j e d n á v k u D o p r a v c e * * S t o r n o v a t O b j e d n á v k u O d e s l a t K a t a lo g * Z á k a z n í k * * * D o d a t P r o d u k t účastník * O věř i t S t a v O b j e d n á v k y Případ užití D i s p ečer Diagram případu užití může být snadno rozšířen o další činnosti, resp. aktualizovat stávající činnosti. 1.3.2 Diagram tříd Class diagram (diagram tříd) modeluje strukturu a obsah tříd k čemuž používá navržené prvky jako třídy, pakety a objekty. Také zobrazuje vztahy (relace) jako kompozice, dědičnost, asociaci a další. Jméno třídy atributy operace Zákazník -jméno -adresa -seznampovolání +vyhodnoceníkreditu() +tisk() Obr. 1.2 Základní grafická struktura třídy 10
Objednávka -datumpřijetí -jezaplacena -počet -cena +odeslání() +tisk() asociace 0..* 1 Zákazník -jméno -adresa -seznampovolání +vyhodnoceníkreditu() +tisk() kvalifikace kardinalita Obr. 1.3 Grafické označení vazeb mezi třídami 1.3.3 Interakční diagramy Sequance diagram (sekvenční diagram) zobrazuje časovou sekvenci objektů účastnící se interakce. Skládá se z vertikální dimenze (čas) a horizontální dimenze (různé objekty). Collaboration diagram (diagram spolupráce) zobrazuje interakce organizované mezi objekty a jejich spojení (vazby) mezi sebou (pořadí zasílaných zpráv mezi objekty). Object1: Class Object2: Class Message1 Message2 Object3: Class Message4 Message3 Obr. 1.4 Sekvenční diagram 11
1.3.4 Stavový diagram zobrazuje sekvence stavů, kterými prochází objekt během svého života v závislosti na obdrženém stimulu, spolu s jeho reakcemi a činnostmi. 1.3.5 Diagram aktivit Zobrazuje speciální stavový diagram, kde většina ze stavů jsou stavy činností a většina přechodů je spouštěna vykonáním akcí ve zdrojových stavech. Tento diagram se zaměřuje na toky řízené vnitřním zpracováním. Používá se k zachycení algoritmů v programovacích jazycích vývojový diagram. pridej stup en do celkem o dpovida prikazu celkem = ce lkem + stupe n pridej 1 do citac odp ovid a prikazu citac = citac + 1 tisk "chyba" [ stupen < 60 ] [ stupen >= 60] tisk "proslo" 12
A c tio n S ta te 1 F o r k A c tio n S t a te 2 A c tio n S ta te 3 B r a n c h A c tio n S ta te 4 A c tio n S ta te 5 M e r g e J o in A c tio n S ta te 6 Obr. 1.5 Diagramy aktivit 1.3.6 Fyzické diagramy Diagram komponent zobrazuje vysokou úroveň paketové struktury samotného kódu. Jsou zobrazeny závislosti mezi komponentami včetně zdrojového kódu komponent, binárního kódu komponent a spustitelné komponenty. Diagram rozmístění (deployment diagram) zobrazuje konfikuraci prvků běhového zpracování (run-time processing elements) a programových komponent, procesů a na nich žijících objektů. 13
Příklady: jsou uvedeny tři jednoduché příklady. Napřed text příkladů projděte bez použití počítače, pak je prověřte na počítači. Příklad 1: Třída Citac, bez metody main, vhodná pro testování v prostředí BlueJ. Explicitně není deklarovaný konstruktor, je použit implicitní konstruktor. public class Citac private int pocet; public int getpocet() return pocet; public void pricti() pocet = pocet + 1; public void odecti() pocet--; public void nuluj() pocet = 0; public String tostring() return "Citac stav: "+getpocet(); public void tisk() System.out.println(this.toString()); //this - odkaz na sebe sama Příklad 2: Stejný příklad doplněný o metodu main a explicitní konstruktor, který nastaví čítač na zadanou hodnotu (objekt c1 na -7, objekt c2 na 0). Pokud deklarujeme ve třídě metodu tostring( ), pak každý objekt této třídy se vypíše jen tím, že se uvede jako argument v metodě println, System.out.println( objekt ). public class Citac private int pocet; // explicitni konstruktor public Citac(int pocet) // stejny idntifikator musime odlisit this this.pocet = pocet; public int getpocet() return pocet; public void pricti() pocet = pocet + 1; public void odecti() pocet--; public void nuluj() pocet = 0; public String tostring() 14
return "Citac stav: "+getpocet(); public void tisk() System.out.println(this.toString()); //this - odkaz na sebe sama public static void main(string[] args) Citac c1 = new Citac(-7); Citac c2 = new Citac(0); c1.pricti(); c2.odecti(); c2.odecti(); c1.pricti(); System.out.println("C1 "+c1); System.out.println("C2 "); c2.tisk(); Příklad 3: V tomto příkladě přidáme další datový atribut do třídy Counter a tím bude název. Ten nám pomůže rozlišovat jednotlivé objekty dané třídy při výpisu. public class Citac private int pocet; private String nazev; // explicitni konstruktor public Citac(int pocet, String n) // stejny idntifikator musime // odlisit identifikatory pomoci this this.pocet = pocet; nazev = n; // ruzne identifikatory - nemusime pouzivat this public int getpocet() return pocet; public void pricti() pocet = pocet + 1; public void odecti() pocet--; public void nuluj() pocet = 0; public String tostring() return nazev + " stav: "+getpocet(); public void tisk() System.out.println(this.toString()); //this - odkaz na sebe sama public static void main(string[] args) Citac c1 = new Citac(-7, "Citac c1"); Citac c2 = new Citac(0, "C2"); c1.pricti(); c2.odecti(); c2.odecti(); c1.pricti(); System.out.println("C1 "+c1); System.out.println("C2 "); c2.tisk(); 15
Objekt, pojmenování objektu, posílání zprávy, brzká a pozdní vazba, polymorfismus, UML diagramy. Každý objekt obsahuje jak data (atributy), tak metody (funkce, procedury), které umožňují práci s datovými atributy. Liší se tedy od klasického recordu tím, že obsahuje navíc metody. Pojmenování objektu je jeho přiřazení k proměnné. Zpráva zaslaná objektu je žádost o provedení operace. Brzká vazba (early binding) je vazba, kdy kód operace je znám v době překladu. Pozdní vazba (late binding) je vazba, kdy kód operace je znám až za běhu programu. Polymorfismus (vícetvarost) způsobuje, že zaslání stejné zprávy různým objektům způsobí jejich odlišné reakce. UML diagramy prostřednictvím grafického vyjádření pomáhají ve vyjádření daných konkrétních aspektů modelované reality. Co představuje datovou povahu a co funkční povahu objektu? Čím se formálně liší deklarace recordu od deklarace objektu? Znamená polymorfismus to, že objekty reagují stejně na stejnou zprávu? Jaké diagramy v UML rozeznáváme a k čemu se používají? Můžeme v objektově orientovaném jazyce deklarovat strukturu record? 1.1 Specifikuje zpráva, jak se má operace provést? 1.2 Je při pozdní vazbě kód operace určen při překladu? 16
1.1 Ne, zpráva je pouze žádost o provedení operace. 1.2 Ne, kód operace pozdní vazby je určen až za běhu programu. V tomto modulu jsou vysvětleny základní pojmy objektově orientovaného programování. Dále jsou jazyk UML jeho funkce a jednotlivé typy diagramů. V následujících kapitolách budeme základní pojmy dále rozvíjet a praktické aplikace doplňovat o odpovídající diagramy jazyka UML. 17
2. Třída 2.1 Třída & instance (objekt) Třída popisuje implementaci množiny objektů, které všechny reprezentují stejný druh systémové komponenty (složky). Třídy se zavádějí především z důvodů datové abstrakce, znalostní abstrakce, efektivnímu sdílení kódu a zavedení taxonomie do popisu programové aplikace. V systémech se třídami jsou objekty chápány jako instance tříd. Třída OsobníAuto má pak např. instance Fabia, Seat, Audi. Třída popisuje formu soukromých pamětí a popisuje, jak se provádějí operace. Např. existuje systémová třída, která popisuje implementace objektů, jenž reprezentují obdélníkové plochy. Tato třída popisuje, jak si individuální instance pamatují umístění svých ploch a také jak provádějí operace pro obdélníkové plochy. Každý objekt je instancí třídy. Programování pak sestává z vytváření tříd a specifikuje sekvenci zpráv, které se vyměňují mezi objekty. Objekt (instance třídy) odpovídá v reálném světě jevu, třída má svůj protějšek v konceptu. Instance a třídy jsou si podobné jak ve veřejných vlastnostech, tak v soukromých. Veřejné vlastnosti objektu jsou zprávy které tvoří protokol. Všechny instance jedné třídy mají stejný protokol, protože reprezentují stejný druh komponenty. Individuální vlastnosti objektu tvoří množina atributů, která vytváří jejich soukromou paměť a množinu metod, které popisují, jak provádět operace. Instanční proměnné a metody nejsou přímo přístupné jiným objektům. Všechny instance dané třídy užívají stejnou množinu metod k popisu svých operací. Např. instance které reprezentují obdélníky všechny odpovídají stejné množině zpráv a ony všechny používají stejné metody k určení jak odpovídat. Objekty umí reagovat na zprávy provedením příslušných operací, které jsou popsány ve třídě a sdíleny všemi jejími instancemi. Takové sdílení vede k vyšší efektivnosti při implementaci objektových systémů. Vztah třída-objekt můžeme charakterizovat jako vztah popisu a realizace. Rozdělení objektů na třídy a instance však mění model výpočtu, protože instance obsahují jen data a metody jsou uloženy mimo ně v jejich třídě. Je-li tedy instanci poslána zpráva, tak instance musí požádat svoji třídu o vydání příslušné metody. Kód metody je poté pouze dočasně poskytnut instanci k provedení. Deklarace třídy je základním stavebním kamenem objektově orientovaného přístupu v jazyce Java. 2.2 Zprávy a metody mechanismus posílání zpráv Objekty reagují na zprávy (požadavky), které jsou jim v čase adresované. Reakce objektů na zprávu může být: Jednoduchá odpověď objektu (vrácení hodnoty, nebo objektu), Změna vnitřního stavu objektu, 18
Odeslání zprávy jinému objektu, Vytvoření nového objektu Kombinace uvedených možností. Formalizace zápisu, kdy zpráva zaslaná příjemci nezasílá žádnou odpověď (kvalifikátor void při popisu metody): Příjemce.zprava(eventuální parametry zprávy); Formalizace zápisu, kdy zpráva zaslaná příjemci vrací odpověď (která musí být patřičně kvalifikovaná): Odpověď = Příjemce.zpráva(eventuální parametry zprávy); kde: příjemce reprezentuje objekt (instanci dané třídy, zpráva je konkrétní zpráva deklarovaní pro danou třídu (eventuálně její nadtřídy), eventuální parametry zprávy představují skutečné parametry zprávy, které jsou vyžadovány. Mechanismus posílání zpráv, který je využit v objektově orientovaném, paradigmatu je založen na výpočetním modelu klient / server. Odpověď představuje klienta a server je reprezentován Příjemcem zprávy. Např. třída String je poskytovatelem řady standardních služeb pro zpracování řetězců. Třída String server, který poskytuje řetězcově orientované služby aplikacím klientům. Aplikace, která využívá třídu String (její objekty) je klient, který vyžaduje služby serveru vyvoláním odpovídajících metod. Vyvolání metody je prováděno prostřednictvím mechanismu posílání zpráv. String s1 = Libovolny textovy retezec ; // úvodní kvalifikace a //inicializace objektu s1 int n = s1.length(); // n odpověď obsahuje délku řetězce s1 s1 = s1.tolowercase(); // řetězec s1 bude obsahovat pouze malá // písmena Nové objekty nejběžněji vznikají podle popisu třídy (šablony třídy). Třída specifikuje pro své objekty proměnné pro uchování jejich vnitřních stavů a metody (funkce), k vykonávání zpráv. Třídy je možné vytvářet v hierarchii, viz kapitola o dědičnosti. Pro uklízení nepotřebných objektů se stará speciální program garbage collector. V některých OOP jazycích C++ není garbage collector. Běžící objektově orientovaný program je tvořen soustavou mezi sebou navzájem komunikujících objektů, který je řízen především sledem vnějších událostí z rozhraní programu. 19
Hlavní program může být tvořen např. pouze deklarací objektu a zasláním zprávy danému objektu. Objekt Základem objektových systémů je objekt. Je to nedělitelná sebeidentifikovatelná entita, obsahující datové atributy, jejich identifikaci a metody, které realizují příslušné operace na těchto datech. Příklady objektů: Čísla, řetězce znaků, Datové struktury: zásobník, fronta seznam, slovník, Grafické obrazce, adresáře souborů, soubory, Kompilátory, výpočetní procesy Grafické pohledy na informace, finanční historie atd. Datový atribut A Datový atribut B Datový atribut C Metoda 1 zpráva Metoda 2 Metoda 3 Metoda 4 Obr. 2.1 Struktura objektu Zapouzdřenost encapsulation Zapouzdřenost patří k základním charakteristikám objektově orientovaného přístupu. Její výhody spočívají zejména v: Programy mohou být testovány po menších částech Odstraní se častá chyba, která bývá skryta ve sdíleném přístupu ke společným datům Interní datová struktura může být změněna bez nutnosti změn okolí objektu (změna názvu datových atributů) Mohou být vytvářeny knihovny objektů, tedy abstrakcí datových typů, které je možné použít v jiných aplikacích 20
Je zabezpečena ochrana a identifikace se kterými program pracuje Zapouzdření napomáhá k oddělení rozhraní (interface) viditelná část objektu od implementace (skrytá část deklarace objektu). Z definice objektu vyplývá, že je pro něj typické spojení dat a operací v jeden nedělitelný celek s ochranou dat, tedy zapouzdřením. Data jsou spojena s operacemi tak těsně, že se k nim bez těchto operací nedostaneme. Sebeidentifikace objekt sám o sobě ví kdo je, takže paměť obsahující objekty obsahuje i informace o struktuře svého obsahu. Ukrývání informací Při externím ukrývání informací máme na mysli to, že objekty by neměly zpřístupňovat přímo svá lokální data a ani kódy jednotlivých operací. Objekt je tedy zvenku neprůhledná entita. Při interním ukrývání informací máme na mysli to, že při dědění nemusí mít následníci objektu přístup k lokálním datům předchůdců a také nemusí mít přístup ke kódu jednotlivých operací svých předchůdců. Objekty chápeme jako dynamické entity, které v průběhu výpočtu vznikají, vytvářejí nové objekty a zase zanikají. Zpráva Objekty komunikují s jinými objekty pomocí posílání zpráv. Množina zpráv na kterou objekt reaguje se nazývá protokol (protokol zpráv). Zpráva je žádost, aby objekt provedl jednu ze svých operací. Zpráva specifikuje o jakou operaci se jedná, ale nespecifikuje, jak by se operace měla provést. Objekt jemuž je poslána zpráva sám určuje, jak provést požadovanou operaci. Na provedení operace je nahlíženo jako na vnitřní schopnost objektu, která může být inicializovaná jedině zasláním zprávy. Objektům se posílají zprávy, které se typicky skládají z adresáta neboli příjemce zprávy, selektoru zprávy (název zprávy) a eventuálních parametrů zprávy: System.out. println ( Text k tištění +prom); kde: System.out - reprezentuje příjemce zprávy println reprezentuje zprávu Text k tištění + prom jsou parametry zprávy Konstruktory Konstruktor je speciální metoda, pro vytváření a inicializaci nových objektů (instancí). Název této metody je totožný s názvem třídy. Např. Bod b1 = new Bod(); // new klíčové slovo 21
nebo zápis zdlouhavější: Bod b1; // kvalifikace proměnné b1 b1 = new Bod( ); // vytvoření a inicializace nového objektu (instance) Konstruktor vytvoří požadovanou instanci a vrátí odkaz, prostřednictvím nějž se na objekt odkazujeme. Konstruktor musí mít každá třída. Pokud třída nemá deklarovaný žádný konstruktor, doplní překladač nejjednodušší konstruktor (bez parametrů) a ten se označuje jako implicitní. Existuje-li pro třídu alespoň jeden programátorem deklarovaný konstruktor (explicitní), překladač žádný implicitní konstruktor nepřidává. Konstruktory dané třídy se mohou lišit počtem (typem) parametrů. Definice několika verzí konstruktorů s různými sadami parametrů se označuje jako přetěžování (overloading) daného konstruktoru. Jednotlivé verze konstruktorů se pak nazývají přetížené. Objekt (instance) versus odkaz (reference) na objekt V Javě program nikdy neobdrží vytvořený objekt, ale pouze odkaz (referenci) na vytvořený objekt. Objekt (instance) je zřízena někde ve zvláštní paměti v haldě (heap). O haldu se stará správce paměti (garbage collector). Jeho funkce: přidělování paměti nově vznikajícím objektům rušení objektů, které nikdo nepotřebuje, (na které nejsou žádné odkazy) Na jeden objekt může být více odkazů. Zrušení objektu představuje zrušení všech odkazů na něj. 2.3 Primitivní a objektové (referenční) datové typy Typ údaje popisuje, co je daný údaj zač. V typově orientovaných jazycích mají veškerá data se kterými program pracuje svůj typ, tedy u každého údaje předem znám typ. Výhody: rychlejší práce kompletnější kontrola (robustnost) Java rozlišuje: primitivní datové typy objektové datové typy Primitivní datové typy Jsou to např. čísla zabudována hluboko v jazyku, chování pevně dané; na vytvoření není třeba konstruktor tedy posílání žádných zpráv: 22
Typ Velikost Zobrazená hodnota v bitech boolean true false - implementace závislá na JVM (Java Virtual Machina) char 16 \u0000 až \uffff (0 65 535) byte 8-127 + 128 short 16-32 767 +32 768 int 32-2 147 483 648 +2 147 483 647 long 64-9 223372 036 854 775 808 +9 223 372 036 854 775 807 float 32 záporné: -3,40.. E+38-1,40.. e-45 kladné: 1,40.. e-45 3,40.. E+38 double 64 záporné: -1,79.. E + 308-4,94.. e - 324 kladné: 4,94.. e 324 1,79.. E + 308 Referenční objektové typy Referenční objektové typy jsou objekty (instance) daných tříd. Jedná se o třídy knihoven a uživatelem definované třídy. Standardní knihovna obsahuje cca 1500 tříd Třída String definuje typ znakových řetězců, posloupnost znaků chápána jako objekt. Vrácení hodnot primitivních datových typů int getx() objekt vrací celé číslo double getbalance() objekt vrací reálné číslo Vrácení referencí objektových datových typů K převzetí odkazu musíme mít připravený odkaz (referenci) odpovídajícího typu, (která bude vytvořena v zásobníku odkazů) a do které bude požadovaný odkaz na objekt uložen (viz příklad Osoba Adresa Ucet). Výjimkou je objektový typ (třída) String, která se někdy chová i jako primitivní typ automaticky se o okně BlueJ zobrazí a zároveň předává odkaz 2.4 Metody třídy Deklarace třídy a tvorba instancí public class Bod // datové atributy, instanční proměnné private int x; private int y; /** * Bezparametrický konstruktor pro objekty třídy Bod */ 23
public Bod() // initialise instance variables x = 0; y=0; public Bod(int c) // konstruktor s jedním parametrem x = c; y = c; public Bod(int x, int y) // konstruktor se dvěma parametry this.x = x; this.y = y; // kopírovací konstruktor (copy konstruktor) public Bod(Bod bod) this.x = bod.getx(); sety(body()); public String tostring() // metoda tostring() zděděné od třídy Object String t = "\nx: "+ x +" Y: "+y; return t; public void tisk() System.out.println("Souradnice bodu "+this.tostring()); public int getx() return x; public void setx(int x) this.x = x; public class BodTest public static void main(string[] args) Bod a = new Bod(1, 1); Bod b; Bod c = new Bod(3, 3); a.tisk(); b = a; b.tisk(); b.posun(100,200); a.tisk(); // použití kopírovacího konstruktoru Bod d = new Bod(c); Klíčové slovo private, které uvozuje informaci o typy datových atributů třídy Bod oznamuje, ža datový atribut je soukromým majetkem objektu (instance), ke kterému nemá nikdo cizí přístup. Kopírovací konstruktor je speciálním typem konstruktoru, který má jako parametr deklarovaný objekt (instanci) stejné třídy viz třída Bod. Funkcí tohoto konstruktoru je, že 24
vytvoří novou instanci dané třídy, do které zkopíruje všechny datové atributy objektu, který je předaný jako parametr. Ve třídě BodTest proměnná d odkazuje na objekt třídy Bod s datovými atributy x = 3, y = 3. Datové atributy jsou stejné jako u proměnné c, ale obě proměnné c, d ukazují na jiné objekty. Kopírovací konstruktor bývá využíván při skládání objektů typu kompozice (pevná vazba mezi celkem a částmi). Příklady: Příklad 4: Je uvedena třída Ucet, bez hlavní metody main. Tu je třeba doplnit, pokud byste pracovali pouze s touto třídou mino prostředí BlueJ. public class Ucet private int cislo; private int stav; // Konstruktory tridy Ucet public Ucet() cislo = 0; stav = 0; public Ucet(int cislo, int stav) this.cislo = cislo; this.stav = stav; public void vlozeni (int castka) stav = stav + castka; public int vyber (int castka) stav = stav - castka; return stav; public String tostring() String tx = "Cislo uctu: " + cislo + " stav: " + stav; return tx; public void tisk() System.out.println(this.toString()); Třída, instance třídy (objekt), konstruktor. 25
Třída popisuje implementaci množiny objektů. Třída představuje deklaraci. Objekty, instance jsou vlastně herci na jevišti, kteří celou práci provedou, tak jak je to deklarované ve třídách a tak jak jsou jim posílány zprávy. Na jakýkoli běžící objektově orientovaný program se můžeme dívat jako na graf objektů. Uzly v grafu jsou objekty a spojnice mezi uzly jsou reference mezi objekty. Co je to třída? Jaký je rozdíl mezi instancí a objektem? Čím se liší deklarace složeného typu od deklarace objektu? Proč se zavádí pojem třídy? 2.1 Co jsou to přístupové metody, jak se označují a k čemu se používají? 2.2 Co jsou modifikační metody, jak se označují a k čemu se používají? 2.1 Přístupové metody jsou metody ve třídě, které se používají k zpřístupnění datových atributů dané třídy. Zatímco datové atributy mají u sebe většinou modifikátor private, metody u sebe většinou mají modifikátor public. Většinou se tyto metody označují slovíčkem get které je následované identifikátorem daného datového atributu. NApř. getdelka( ), - metoda vrací datový atribut delka (return delka; ). 2.2 Modifikační metody se používají k modifikaci datových atributů. Většinou se tyto metody označují slovíčkem set, následovaný identifikátorem datového atributu a v závorkách je typ a nová hodnota, kterou chceme přiřadir datovému atributu. Např. setdelka(int p); kde proměnná p obsahuje novou hodnotu. Přístupové a modifikační metody se deklarují proto, abychom s datovými atributy nepracovali přímo, ale pouze prostřednictvím metod, které deklarují požadovaný přístup. 26
Pojem třídy je základníém pojmem v tzv. třídně instančních objektově orientovaných systémech. Třída vlastně představuje továrnu na výrobu objektů. 27
3. Třídy a objekty detailnější pohled V předchozí kapitole jsme si ukázali, že návratovou hodnotou z metody může být hodnotový typ (např. datový atribut x třídy Bod). Nyní si ukážeme, že návratovou hodnotou může být také referenční objektový typ a tedy že dvě proměnné mohou ukazovat na stejný objekt (instanci). Do předchozího příkladu doplníme dvě další metody a to metody getbod() a metodu setbod(). Zkrácený zdrojový kód třídy je pak následující: public class Bod private int x; private int y; // deklarace přetížených konstruktorů a dalších metod... public int getx() return x; public int gety() return y; public void setx(int x) this.x = x; public void sety(int y) this.y = y; public void setbod(bod f) x = f.x; y = f.y; public Bod getbod() return this; Metoda SetBod nastavuje datové atributy příjemce této zprávy na hodnoty datových atributů argumentu a. Metoda getbod() vrací referenci (odkaz) na příjemce této zprávy. Aby vše bylo názornější ukážeme si použití těchto metod ve třídě BodTest: public class BodTest public static void main(string[] args) Bod a, b, c; String t; a = new Bod(); b = new Bod(-10, 122); // STOP 1 c = b.getbod(); c.tisk(); if (b==c) t = "ANO"; else t = "NE"; System.out.println("Vysledek: "+t); // STOP 2 c.setbod(a); a.tisk(); b.tisk(); c.tisk(); 28
V příkladu jsme použili dva komentáře (STOP 1 a STOP 2). Ty nám kód programu rozdělují na tři části, které si pro větší přehlednost znázorníme graficky. a b c x 0 y 0 x -10 y 122 null Obr. 3.1 Situace před komentářem STOP 1 V jazyce Java jsou možné pouze odkazy (reference) na vytvořené objekty (instance). Proměnné a, b ukazují na nové objekty, jejichž datové atributy jsou nastaveny na odpovídající hodnoty. Proměnná c je pouze kvalifikována na třídu Bod, proto zatím ukazuje na null. a b c x 0 y 0 x -10 y 122 null Obr. 3.2 Situace mezi komentáři STOP1 a STOP 2 Obrázek 3.2 zobrazuje situaci po provedení příkazu c = b.getbod(). Tím proměnná c získává odkaz na objekt na který odkazuje proměnná b. Objekt null zruší garbage collector. Obrázek 3.3 zobrazuje situaci po provedení příkazu c.setbod(a). Zpráva setbod(a) způsobí, že příjemci zprávy, objektu c, se nastaví jeho datové atributy na stejné hodnoty jako objektu na který ukazuje proměnná a. Pro úplnost ještě uvádíme výpis programu BodTest. Souradnice bodu X: -10 Y: 122 // bod c Vysledek: ANO Souradnice bodu X: 0 Y: 0 // bod a 29
Souradnice bodu X: 0 Y: 0 // bod b Souradnice bodu X: 0 Y: 0 // bod c a b c x 0 y 0 x 0 y 0 null Obr. 3.3 Situace za komentářem STOP 2 3.1 Klíčové slovo (pseudoproměnná) this Jak jste si jistě všimli, v metodách setx() a sety() se vyskytuje pseudoproměnná this. Používá se všude tam, kde chceme zdůraznit, že se obracíme na atribut či metodu té instance (objektu), jejíž metodu právě definujeme, kvalifikujeme ji klíčovým slovem this. Použití pseudoproměnné this je v obou metodách nutné, protože tím rozlišujeme argument (parametr) x, jehož hodnotu předáváme to metody od datového atributu x, který je použitý v deklaraci třídy Bod. V příkazu this.x = x; this.x - reprezentuje datový atribut třídy, x - reprezentuje argument metody. Pokud bychom použili různé identifikátory, nemusíme pseudoproměnnou this používat. (Pokud ji ale použijeme, nic se nestane, jen se zpřehlední kód programu). Viz následující ukázka metody: public void setx(int k) x = k; // this.x = k; Stejným způsobem můžeme provádět i další operace. Například bychom do třídy Bod potřebovali doplnit metodu rozdíl, která by vytvořila nový objekt třídy Bod, jehož datové atributy by vznikly rozdílem odpovídajících souřadnic daného bodu, s bodem, který metoda přijme jako argument. Metoda rozdíl vrací referenci na nový objekt vzniklý rozdílem odpovídajících souřadnic. Následuje zkrácený výpis třídy Bod rozšířený o metodu rozdíl. public class Bod private int x; private int y; 30
public int getx() return x; public int gety() return y; public void setx(int x) this.x = x; public void sety(int y) this.y = y; public Bod rozdil(bod g) Bod nbod = new Bod(); nbod.setx(getx() + g.getx()); // nbod.x = x + g.x; nbod.sety(this.gety() + g.gety()); return nbod; // deklarace dalších metod... Princip vytváření nových objektů v metodách je velmi důležitý a velmi často se používá. Jeho význam je v tom, že původní objekt, tedy příjemce zprávy nezmění své datové atributy. Naopak je vytvořen nový objekt, do kterého je zaznamenán patřičný výsledek. Uvedeme proto ještě příklad špatného postupu, kdy výsledek operace např. rozdíl ovlivňuje datové atributy příjemce odpovídající zprávy: public Bod rozdilspatne(bod g) // Bod nbod = new Bod(); this.setx(getx() + g.getx()); // nbod.x = x + g.x; this.sety(this.gety() + g.gety()); return this; 3.2 Klíčové slovo final a jeho použití Někdy bychom potřebovali zabezpečit neměnnost datových atributů daných objektů. Máme tím na mysli neměnnost datových atributů, které jsme vytvořili v konstruktoru. Datovému atributu můžeme přiřadit jednou hodnotu, kterou již nemůžeme změnit. Také bývá výhodné v programu místo literálů používat pojmenované konstanty. Ty by měly podobný význam jako již zmiňované datové atributy, tedy jednou přiřazená hodnota je dále neměnná. Pojmenované konstanty se definují jako atributy, - mezi modifikátory se použije klíčové slovo final. Např. private final int TYDEN = 7; private final int PRACTYDEN = 5; Z uvedených příkladů vyplývá, že pojmenovaná konstanta TYDEN bude mít vždy hodnotu 7 a pojmenovaná konstanta PRAC_TYDEN hodnotu 5. Podle konvence se pro identifikátory konstant používají velká písmena. 31
Jak bude vypadat situace ve třídě Bod, pokud bychom přidali klíčové slovo final k deklaraci datových atributů? public class BodFinal private final int x; private final int y; public BodFinal() // initialise instance variables x = 0; y=0; public BodFinal(int c) x = c; y = c; public BodFinal(int x, int y) this.x = x; this.y = y; public void setx(int x) //this.x = x; // can't assign a value to final variable x public void sety(int y) //this.y = y; // can't assign a value to final variable y public void setbod(bod a) // x = a.x; can't assign a value to final variable x // y = a.y; can't assign a value to final variable y Při použití klíčového slova final pro datové atributy třídy, překladač umožní pouze inicializaci datových atributů v konstruktoru, avšak další změny nejsou možné. Abychom se zbavili chyb překladače, uvedli jsme tyto metody jako komentář. 3.3 Statické atributy, statické metody třídní atributy (proměnné), třídní metody V nadpisu této části používáme přídavná jména statický resp. třídní. Je to z toho důvodu, že se v různých publikacích popisují různě. Nejdříve se podíváme na atributy: Doposud jsme při deklaraci třídy deklarovaly tzv. datové atributy, které se někdy také v jiných jazycích (Smalltalk) nazývají instanční proměnní. Význam těchto atributů je v tom, že v deklaraci třídy nadeklarujeme jejich typy a pak každá instance si naplní tyto datové atributy svými hodnotami. Naproti tomu statické nebo třídní atributy jsou atributy deklarované ve třídě, jejichž hodnoty jsou stejné pro všechny instance dané třídy. Proto si do nich můžeme uložit nějaké důležité hodnoty např. krok posunu, barvu, zvolenou stupnici pro měření teploty (Fahrenheit, Celsius, Kelvin) atd. Instanční proměnné jsou datové atributy objektu. Jejich hodnoty, reference se většinou liší. 32
Třídní proměnné jsou proměnné třídy hodnoty stejné pro všechny objekty (instance) dané třídy. Klíčové slovo static je uvedeno před datovým / objektovým typem Notace zápisu: private static int cislo; private static String nazev; private static boolean Q; Klíčové slovo static uvedené v deklaraci datového atributu určuje, že daný atribut bude třídním (statickým) atributem. Praktické využití si můžeme ukázat zase na příkladu třídy Bod, kde jako statický atribut uvedeme velikost kroku, o který se budou jednotliví složky bodu posunovat při aplikaci metody posuň (move). Této metodě tedy nebudeme muset zadávat žádný argument posunu a navíc posun bude pro všechny instance dané třídy stejný (bude mít stejnou hodnotu). Uvedeme si proto jen ty deklarace a metody, které jsou nezbytně nutné k pochopení dané problematiky. Aby byl tisk souřadnic jednotlivých bodů srozumitelnější, přidali jsme ještě datový atribut jméno, do kterého se zadá při inicializaci patřiční proměnná. Musíme samozřejmě ještě změnit metodu tisk, aby vytiskla i jméno bodu. public class BodStatic private int x; private int y; private String jmeno; private static int krokx = 25; // třídní (statický) atribut private static int kroky = -40; // třídní (statický) atribut public BodStatic(String j) x = 0; y=0; jmeno = j; public BodStatic(String j, int c) x = c; y = c; jmeno =j; public BodStatic(String j, int x, int y) this.x = x; this.y = y; jmeno = j; public String tostring() String t = String.format("\n%11s %4s %4s %4d %4s %4d", "Nazev bodu:",getjmeno(),"x:",getx(),"y:",gety()); return t; public void tisk() System.out.printf(this.toString()); public int getx() return x; public void setx(int x) this.x = x; public void setbod(bodstatic a) 33
setx(a.getx()); //x = a.x; sety(a.gety()); //y = a.y; public String getjmeno() return jmeno; public BodStatic getbodstatic() return this; public void posunx() x =+ krokx; public void posuny() y =+ kroky; public void posunxy() posunx(); // this.posunx(); posuny(); // this.posuny(); public class BodStaticTest public static void main(string args[]) BodStatic a, b, c; a = new BodStatic("a"); a.tisk(); b = new BodStatic("b",-10, 122); b.tisk(); c = b.getbodstatic(); c.tisk(); a.posunx(); a.tisk(); b.posuny(); b.tisk(); c.posunxy(); c.tisk(); Výpis programu: Nazev bodu: a X: 0 Y: 0 Nazev bodu: b X: -10 Y: 122 Nazev bodu: b X: -10 Y: 122 //jedná se o bod c, který //ukazuje na stejný bod jako b Nazev bodu: a X: 25 Y: 0 Nazev bodu: b X: -10 Y: -40 Nazev bodu: b X: 25 Y: -40 //jedná se o bod c, který //ukazuje na stejný bod jako b Jak je vidět z výpisu programu, metoda posunx(), posuny(), posunxy() funguje pro všechny instance třídy Bod stejně (modifikuje souřadnice bodů o stejné hodnoty). V programu jsme také využili metody set a get abychom se nemuseli přímo odkazovat na datové atributy viz např. metoda setbod(). 34
Pro tisk bodů jsme využili formátovaný příkaz pro tisk printf. Tento příkaz vyžaduje, aby se tisk skládal ze dvou částí. První část tvoří formát a je uzavřena v uvozovkách a druhou část pak tvoří argumenty tisku. Uvozovací symbol pro formáty je znak %, za kterým je uveden počet míst pro danou položku a následuje symbol typu tištěné proměnné s pro řetězec a d pro desítkové číslo. Zároveň se vytisknou i všechny znaky, které jsou uvedeny v první části příkazy (v našem případě se jedná pouze o mezery). String t = String.format("\n%11s %4s %4s %4d %4s %4d", "Nazev bodu:",getjmeno(),"x:",getx(),"y:",gety()); Význam zápisu: - tisk na nový řádek // \n - 11 znaků pro text Nazev bodu: zarovnáno od leva - 1 mezeta // mezera mezi %11s %4d - 4 znaky pro jméno bodu - 1 znak mezera - 4 znaky pro text X: - 1 znak mezera - 4 znaky pro hodnotu x souřadnici - 1 znak mezera - 4 znaky pro text Y: - 1 znak mezera - 4 znaky pro hodnotu y souřadnici Vyzkoušejte, co by se vytisklo, pokud bychom upravili uvedenou metodu následovně: String t = String.format("\n%11s@%4s+%4sq%4dkk%4s*&$%4d", "Nazev bodu:",getjmeno(),"x:",getx(),"y:",gety()); Statické metody (třídní metody) Doposud probírané metody byly tzv. instanční metody, to je metody, které se aplikují na objekty. Třídní metody (statické metody) se aplikují na danou třídu, zajišťují (nastavují) hodnoty třídních atributů, nebo se používají všude tam, kde nechceme uvádět příjemce jako objekt, ale místo příjemce uvádíme patřičnou třídu a k ní žádanou třídní metodu. Další charakteristiky jsou následující: definují se vložením klíčového slova static mezi identifikátory mají platnost pro všechny instance (objekty) dané třídy mohou být volány před vznikem instance dané třídy slouží k přípravě prostředí ve kterém vznikne objekt slouží k definici metod, které nejsou vázány na žádnou instanci (objekt) - takto jsou definovány matematické funkce. Metody třídy (Class methods) nezávisí na žádné instanci. V metodách třídy tedy nemůžeme: používat metody a atributy instancí přesněji atributy a metody instancí kvalifikované klíčovým slovem this. 35