objektově orientované programování 1 xobo1hunka/vyuka/javaoop/xobo1_java.pdf1 objektově...

104
1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Huňka, CSc.

Upload: doduong

Post on 05-May-2019

222 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

1

Objektově orientované programování 1

XOBO1

Autor: Doc. Ing. František Huňka, CSc.

Page 2: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

2

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

Page 3: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

3

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

Page 4: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

4

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

Page 5: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

5

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.

konceptyspecifikujícíproblém

realizované koncepty (třídy)

jevy objekty (instance)

abstrakce abstrakce

modelování

referenční systém model systému

Programování jako proces modelování

konceptyspecifikujícíproblém

realizované koncepty (třídy)

jevy objekty (instance)

abstrakce abstrakce

modelování

referenční systém model systému

Programování jako proces modelování

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

Page 6: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

6

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 } }

Page 7: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

7

• 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,

Page 8: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

8

• 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

Page 9: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

9

• 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)

A c to r

U s e C a s e

• Úč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.

Page 10: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

10

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ší. Obr. 1.2 Základní grafická struktura třídy

Z á k a z n í k

S y s t é m o b j e d n á v e k p o š t o u

D o p r a v c e

D i s p e č e r

Z a d a t O b j e d n á v k u

S t o r n o v a t O b j e d n á v k u

D o d a t P r o d u k t

O v ě ř i t S t a v O b j e d n á v k y

O d e s l a t K a t a lo g

*

*

*

*

*

**

*

účastník

Komunikační relace

Název systému

Hranice systému

Případ užití

+vyhodnoceníKreditu()+tisk()

-jméno-adresa-seznamPovolání

ZákazníkJméno třídy

atributy

operace

Page 11: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

11

+vyhodnoceníKreditu()+tisk()

-jméno-adresa-seznamPovolání

Zákazník

+odeslání()+tisk()

-datumPřijetí-jeZaplacena-počet-cena

Objednávka

0..* 1

asociace

kardinalita

kvalifikace

+vyhodnoceníKreditu()+tisk()

-jméno-adresa-seznamPovolání

Zákazník

+odeslání()+tisk()

-datumPřijetí-jeZaplacena-počet-cena

Objednávka

0..* 1

asociace

kardinalita

kvalifikace

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

Object3: Class

Message1

Message2

Message3Message4

Obr. 1.4 Sekvenční diagram

Page 12: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

12

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.

pr ide j s tup en do ce lkem

pr ide j 1 do citac

o dpov ida pr ikazucelkem = ce lkem + s tupe n

odp ovid a prikazuc itac = c itac + 1

tisk "chyba" tisk "proslo"

[ stupen >= 60][ stupen < 60 ]

Page 13: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

13

A c t io n S ta te 1

A c t io n S t a te 2 A c t io n S ta te 3

A c t io n S ta te 4 A c t io n S ta te 5

A c t io n S ta te 6

F o r k

B r a n c h

M e r g e

J o in

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ů.

Page 14: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

14

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 mu sime 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(){

Page 15: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

15

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 idnti fikator musime // odlisit identifikato ry pomoci this this.pocet = pocet; nazev = n; // ruzne identifikatory - nemusim e 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();

Page 16: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

16

} }

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?

Page 17: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

17

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.

Page 18: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

18

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,

Page 19: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

19

– 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 //inicializac e 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.

Page 20: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

20

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.

zpráva

Datový atribut A Datový atribut B Datový atribut C

Metoda 1

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

Page 21: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

21

– 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

Page 22: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

22

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:

Page 23: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

23

Typ Velikost v bitech

Zobrazená hodnota

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 */

Page 24: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

24

public Bod() { // initialise instance variables x = 0; y=0; } public Bod(int c) // konstruktor s jedním param etrem { 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() z děděné od t řídy Object { String t = "\nX: "+ x +" Y: "+y; return t; } public void tisk() { System.out.println("Souradnice bodu "+this.to String()); } 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

Page 25: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

25

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.

Page 26: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

26

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.

Page 27: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

27

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ů.

Page 28: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

28

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(); } }

Page 29: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

29

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

Page 30: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

30

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;

Page 31: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

31

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.

Page 32: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

32

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 vari able x // y = a.y; can't assign a value to final vari able 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ší.

Page 33: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

33

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){

Page 34: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

34

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().

Page 35: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

35

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 %4 d", "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.

Page 36: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

36

– překladač totiž nemá žádnou informaci, která instance by se za klíčovým slovem this mohla v daném okamžiku skrývat.

Výhodou těchto třídních metod je to, že při jejich aplikaci zadáme pouze název třídy, odpovídající třídní zprávu následovanou eventuálními argumenty. Praktické využití je například v použití knihovny Math, kdy většina metod této knihovny je deklarovaná jako třídní. Aby uživatel nemohl vytvořit instance od dané třídy (není to v žádném případě žádoucí), je bezparametrický konstruktor uveden jako soukromý (private). 3.4 Metoda main Za povšimnutí jistě stojí, že každá hlavní metoda main (metoda, která provádí požadované akce v aplikaci) je také deklarovaná jako třídní metoda (static). Deklarací metody main jako static umožňuje JVM (Java Virtual Machine) vyvolat metodu main bez vytváření instance této třídy. Hlavička metody main je následující:

public static void main(String[] args) Pro spuštění aplikace v dosovském okně zvoláme interpret Javy následovaný názvem třídy, tedy souboru, kde je uložena hlavní metoda a soubor je s příponou .class.

java JmenoClassName arg1 arg2 …// běh aplikace JVM nahrává specifikovanou třídu (ClassName) a vyvolává metodu main této třídy. Proto je metoda main třídní.

Příklady: V následujícím příkladu jsou uvedeny třídy Kruh a KruhTest, které ukazují na možnosti použití třídních (statických) metod. Ve třídě Kruh jsou deklarovány dvě třídní metody a to metoda obsah( ) a metoda obvod( ), které počítají uvedené charakteristiky kruhu. Třída Kruh má kompozici se třídou Bod, jejíž objekt představuje střed kruhu. Třída Bod ve výpisu uvedena není. Knihovna (třída) Math je v balíčku java.lang. public class Kruh { private Bod stred; private double polomer; //konstruktor public Kruh(Bod b, double r){ stred = b; polomer = r; } public double getPolomer(){

Page 37: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

37

return polomer; } public void posun(int dx, int dy){ stred.posun(dx, dy); } public String toString(){ return String.format("\n%s %s %s %7.2f","Kruh s tred:", stred.toString(),"polomer", polomer) ; } public void tisk(){ System.out.println(this.toString()); } public static double obvod(double polomer){ double vysledek, r; r = polomer; vysledek = 2 * Math.PI * r; return vysledek; } public static double obsah(double polomer){ double r = polomer; return Math.PI * Math.pow(r, 2); // math.PI * polomer * polomer; } }

public class KruhTest { public static void main(String[] args) { Kruh k1 = new Kruh(new Bod(20, -45), 48.71); Kruh k2 = new Kruh(new Bod(125, 88), 275.687); k1.tisk(); k2.posun(100, 200); k2.tisk(); System.out.printf("\n%s : %9.3f %7s: %12.2f","k1 o bvod" ,Kruh.obvod(k1.getPolomer()), "k1 obsah",Kruh.obsah(k1.getPolomer())); double v1 = k2.obvod(); } }

Jak vydíte, pokud třídní metody používáme mimo třídu, ve které byly deklarovány, musíme použít celý násev, tedy název třídy a metody (Kruh.obsah( double r); Formát %12.2f – 12 míst celkem včetně desetinné tečky, 2 místa za desetinnou tečkou. Formát %s – obecný formát pro tisk textů.

Klíčové slovo this a jeho použití. Klíčové slovo final se používá jak pro datové atributy, tak i pro metody. Třídní datové atributy jsou přístupné (buď přímo mají-li modifikátor public, nebo prostřednictvím třídní přístupové metody) a jsou přístupné všem objektům dané třídy. U třídních metod je příjemcem zprávy vždy odpovídající třída.

Page 38: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

38

V této kapitole jsme si detailněji přiblížili další možnosti tříd. Je třeba si uvědomit význam pseudoproměnné this (logicky se nesmí se používat v třídních metodách). Dále pak porozumět možnostem třídních metod, které jsou ve velké míře používány v knihovnách jazyka Java.

Proč je deklarovaná metoda main jako statická?

V čem jsou výhody použití klíčového slova final pro datové atributy?

3.1 V čem jsou výhody třídních atributů? 3.2 V čem jsou výhody třídních metod?.

3.1 Třídní atributy jsou přístupné všem objektům dané třídy buď přímo, nebo prostřednictvím třídních přístupových metod. Do těchto atributů je možné ukládat hodnoty, které vyžadují všechny objekty dané třídy. Např. třída Teplota může mít tři různé pohledy na teplotu a to prostřednictvím stupňů Celsia, Kelvina a Fahrenheita. Příslušná stupnice může být právě uložena v třídním atributu. 3.2 Výhodou třídních metod je to, že mohou být volány před vytvořením instancí dané třídy (jejich příjemcem je daná třída). Další výhodou je to, že se dají aplikovat na všechny instance dané třídy. Viz např. třída Math a její třídní metody. Vytváří zástupnou obálku ve které se mohou také deklarovat obecně potřebné metody.

Page 39: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

39

Princip třídních metod a atributů patří k základním vědomostem. Některé objektově orientované jazyky podobně jako Java mají možnost jiné možnost třídních atributů a metod nemají a řeší to jiným způsobem.

Page 40: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

40

4 Skládání objektů – kompozice & agregace. Přetěžované konstruktory. Klasifikace a kompozice (skládání) patří mezi základní prostředky, které nám pomáhají se vyrovnat se složitostí reálného světa. Protože naší snahou je vytvářet modely (modelovat) reálného světa v počítači (vytváříme tzv. referenční systém – viz. kapitola 1), využíváme i zde prostředky klasifikace a kompozice. Klasifikace představuje prostředek, pomocí kterého vytváříme a rozlišujeme mezi různými třídami jevů. To znamená, že vytváříme koncepty. Klasifikace umožňuje vytvářet hierarchie tříd a také umožňuje dědičnost mezi třídami. Detailněji se uvedené problematice budeme věnovat v kapitole o dědičnosti. Kompozice – skládání je prostředek, který umožňuje, že jev může být chápán jako složenina (kompozice) dalších jevů. Je zde jasné rozlišení mezi jevem, který reprezentuje celek a jevy, reprezentující jeho části. Jak jsme se již zmínili, klasifikace a kompozice jsou prostředky, pomocí kterých organizujeme složitost v pojmech hierarchií. Oba prostředky (klasifikace a kompozice) jsou používány v odlišným, ale navzájem doplňujícím se způsobem. V této kapitole se budeme věnovat pouze skládání objektů, protože je relativně snadnější, dále pak v objektově orientovaném přístupu by skládání mělo mít přednost před dědičností. dědičnost tříd (ke které se ještě dostaneme je krásný a silný prostředek, ale není všespasitelný. Použije-li se místo dědičnosti ve třídě atribut, který bude podle potřeby odkazovat na instance různých tříd, dosáhne se tím v řadě případů větší flexibility, než při použití dědičnosti. Existují dva základní typy skládání a to agregace a kompozice. Agregace (někdy označovaná jako referenční kompozice) je volnou vazbou mezi objekty – vyskytuje se např. mezi počítačem a jeho periferními zařízeními, rezervace a odkazy na hotel, pokoj a hosta. Kompozice je typem velmi těsné vazby mezi objekty – vyskytuje se např. mezi stromem a jeho listy, mezi autem a jeho jednotlivými součástmi (kola, karoserie, volant, motor ... ), dřevěný panáček (hlava, krk, ruce, trup, nohy). Pro skládání se používají následující termíny:

– celek nebo složený objekt

– části (složeného objektu).

Pro implementace těchto dvou základních typů skládání se v programovacích jazycích využívají dynamické resp. statické proměnné. Pro agregaci dynamické, referenční proměnné – umožňují vytvořit právě požadovanou volnou vazbu mezi celkem a částmi. Pro kompozici se využívají statické proměnné, které jsou charakteristické svou „neměnností“. V jazyce Java (podobně jako v jazyce Smalltalk) však existují pouze dynamické proměnné. To do jisté míry zjednodušuje situaci, avšak neumožňuje explicitně využívat pro kompozici

Page 41: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

41

statické proměnné. Nebude tedy rozdíl mezi kompozicí a agregací tak zřetelný jako v jiných programovacích jazycích. Rozlišení mezi kompozicí a agregací probíhá na úrovni konstruktorů. Pro kompozici platí, že vazba mezi celkem a jeho částmi se vytváří v konstruktoru. Naopak pro agregaci platí, že vazba mezi celkem a jeho částmi se vytváří následně kdekoli v programu, resp. může být pak následně kdekoli v programu změněna, což umožňuje flexibilita. Obecná struktura třídy pro skládání public class Celek { private CastA castA; private CastB castB; private CastC castC; public Celek() { // konstruktor; } public kvalifikátor metoda1() { castA.metodaA_1(); . . . castB.metodaB_4(); } public kvalifikátor metoda2() { . . . castC.metodaC_7(); } }

Uvedený výpis obsahuje deklaraci třídy Celek. Části tohoto celku jsou deklarovány pomocí datových atributů patřičných tříd. Např. datový atribut castA je instancí třídy CastA adt. Celek využívá své části ve svých metodách. Např. uvnitř metody metoda1 jsou vyvolány metody metodaA_1 pro objekt castA a metodaB_4 pro objekt castB. Kvalifikátor v popisu metod představuje patřičný návratový typ příslušné metody, nebo void pro metodu která nic nevrací. 4.1 Sémantika agregace Agregace je relací typu celek / část (skládající se z mnoha dílčích částí). V tomto typu relace používá jeden objekt (celek) služby dalšího objektu (části).

• Celek: – bývá dominantní v relaci, – řídí chod relace.

• Části:

– poskytují služby, – reagují na požadavky celku (jsou pasivní).

Page 42: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

42

Obr. 4.1 Znázornění agregace pomocí diagramu tříd ULM Na obr. 4.1 je znázorněna agregace mezi objektem Pocitac (reprezentuje celek) a objektem Tiskarna (reprezentuje část. Celek má u sebe značku agregace, což je prázdný kosočtverec. Z diagramu je také vidět, že celek může být jeden, nebo žádný a části mohou být žádné až nekonečně mnoho (znázorněno symbolem hvězdičky). Slovní vyjádření obrázku 4.1:

• k Počítači může být připojeno více tiskáren, ale i žádná

• Tiskárna může být připojena max. k jednomu počítači, nebo žádnému počítači

• danou Tiskárnu může postupně používat více Počítačů

• Tiskárna je v podstatě na Počítači nezávislá Shrnutí agregace

• Celek (agregát) bývá závislý na částech, ale může existovat nezávisle na nich (občas také existuje)

• Části mohou existovat nezávisle na celku

• Chybí-li některé části, je celek v jistém smyslu neúplný.

• Části mohou být sdíleny více celky. 4.2 Sémantika kompozice

• Kompozice je silnější formou relace celek / část. • V kompozici nemohou části existovat mimo celek. • V kompozici každá část patří pouze jednomu celku.

P o c ita c T is k a rn a

0 ..1 0 ..*

celek nebo agregát agregace

část

Page 43: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

43

Obr. 4.2 Znázornění kompozice pomocí diagramu tříd UML Z obrázku 4.2 je patrné, že objekt Myš je složena z jednoho až čtyř tlačítek, které k objektu Myš patří a nemohou bez něho existovat. Celek má u sebe značku, kterou v případě kompozice tvoří plný kosočtverec. Shrnutí kompozice

• Části patří výhradně jen jednomu celku (kompozici).

• Celek nese výhradní odpovědnost za použití všech svých částí – znamená to zodpovědnost za jejich tvorbu a zničení.

• Za předpokladu, že odpovědnost za části přejde na jiný objekt, může celek tyto části uvolnit.

• Je-li celek zničen, musí zničit rovněž všechny svoje součásti, nebo převést odpovědnost za ně na nějaký další objekt.

• Vzhledem k tomu, že v kompozici má celek výhradní odpovědnost nejen za životní cyklus, ale i za uspořádání všech svých částí, vytvoří složený objekt při svém vytvoření všechny své části automaticky.

• Podobně je tomu i při vymazání objektu. Složený objekt (celek) musí ještě před svým vymazáním vymazat (zničit) všechny své části, nebo je uspořádat tak, aby je mohl přijmout jiný složený objekt (celek).

Nyní uvedeme pár konkrétních příkladů. Následující příklad je ukázkou kompozice, kdy každý objekt třídy Obdélník je složen ze dvou objektů třídy Bod a sice z levyHorní reprezentující levý horní bod obdélníka a pravyDolni reprezentující pravý dolní bod obdélníka. Pro úplnost ještě uvedeme diagram tříd jazyka UML.

Obdelnik Bod-celek

1

-části

2 Obr. 4.3 Kompozice - příklad

Myš Tlačítka

1 1..4

celek částkompozice

Page 44: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

44

Šipka u třídy Bod znamená „navigabilitu“ – směrování či dostupnost. V daném případě to znamená, že objekty třídy Bod jsou dostupné ze třídy Obdélník. public class Obdelnik { private Bod levyHorni, pravyDolni; // konstruktory tridy Obdelnik public Obdelnik() { levyHorni = new Bod(); pravyDolni= new Bod(); } public Obdelnik(Bod A, Bod B) { levyHorni= new Bod(A); // použití kopírovacíh o konstruktoru pravyDolni= new Bod(B); } public void posun(int dx, int dy) { levyHorni.posun(dx,dy); pravyDolni.posun(dx,dy); } public String toString() { String tx= "\nObdelnik tisk levyHorni :" +levyHorni.toString()+ " pravyDolni: "+pravyDolni.toString(); return tx; } public void tisk() { System.out.println(this.toString()); } }

Z výpisu programu je patrné, že třída Obdélník má dva datové atributy, které odkazují na instance třídy bod. Třída Bod není uvedena, protože její struktura zůstává stejná jako v předchozích kapitolách. Jedná se o kompozici, protože objekty (instance) částí jsou k celku vždy připojeny v konstruktorech třídy Obdélník. Tato třída má bezparametrický konstruktor a konstruktor vyžadující dva parametry (dva objekty třídy Bod). Testování této třídy bude uvedeno v následujícím výpise. Dále dobré si všimnout konstrukce metod třídy Obdélník. Metoda posun posouvá oba body (levyHorni, pravyDolni) o zadanou hodnotu ve směru osy x (dx) a o zadanou hodnotu ve směru osy y (dy). Uvnitř této metody jsou vyvolány metody posun s parametry dx a dy pro oba objekty třídy Bod. (Nevhodné, nedoporučované a komplikované řešení by bylo, pokud bychom sami chtěli upravovat ještě souřadnice objektu třídy Bod např. následujícím způsobem: public void posun(int dx, int dy) { levyHorni.setX(dx + levyHorni.getX()); levyHorni.setY(dy + levyHorni.getY()); //levyHorni.posun(dx,dy); pravyDolni.setX(dx + pravyDolni.getX()); pravyDolni.setY(dy + pravyDolni.getY());

Page 45: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

45

//pravyDolni.posun(dx,dy); }

Naznačené řešení je funkční, avšak již nyní je vidět, že je komplikovanější. A což kdybychom například měli nějaký další objekt, který by se skládal z několika objektů třídy Obdélník? Platí zásada, že objekt je samostatná sebeidentifikovatelná entita a podle toho bychom také měli postupovat a svěřit vždy příslušnou „odpovědnost“ patřičnému objektu. Stane-li se, že objekt typu část námi požadované metody nemá, tak tyto metody doplníme. Obdobně jako v metodě posun je postupováno i v metodě toString(), kde při vykonání celé metody jsou dílčí úkoly delegovány na dva objekty třídy Bod. Názvy metod posun a toString() jsou záměrně stejné, protože se vždy jedná o jiné třídy (třídu Obdélník a třídu Bod). Pro úplnost si ještě uvedeme použití třídy Obdélník a sice třídu ObdelníkTest. public class ObdelnikTest { public static void main(String args[]) { Bod LH = new Bod(22,23); Bod PD= new Bod(100, 200); Obdelnik obA = new Obdelnik(LH, PD); ob.tisk(); LH.posun(-100,-100); LH.tisk(); obA.tisk(); Obdelnik obD = new Obdelnik(new Bod(9, 12), ne w Bod(-33,46)); obD.tisk(); obD.posun(-120, 240); obD.tisk(); } }

V uvedeném příkladu vytváříme dva objekty třídy Obdélník. Jeden objekt s názvem obA a druhý s názvem obD. V prvém případě (objekt obA) si nejdříve vytvoříme objekty LH a PD a ty pak doplníme do konstruktoru třídy Obdélník. Objekt obA je plně funkční, avšak na jeho vnitřní body se můžeme odkazovat z vně objektu. Je to vidět z příkladu, kdy když posuneme bod LH o zadané parametry, posun zaznamená i bod deklarovaný uvnitř objektu třídy Obdélník. Prvé řešené proto nese rysy agregace. Ve druhém případě vytvoříme objekt obD. Jeho vnitřní body nejsou z vnějšku přístupné, protože vytvořené instance odkazují pouze na „vnitřní“ body. Další trochu složitější příklad bude ukázka kompozice i agregace. Na diagramu tříd UML je zobrazena celá problematika příkladu.

Page 46: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

46

Osoba Adresa

Ucet

1 1

1

1 Obr. 4.4 Ukázka kompozice a agregace Objekty třídy Osoba jsou složeny ze dvou částí a sice z objektu třídy Adresa (skládání pomocí kompozice – pevná vazba daná již v konstruktoru metody) a objektu třídy Ucet (skládání pomocí agregace – volnější, pružnější vazba). Nejdříve uvedeme deklarace obou tříd, jejichž objekty tvoří části a pak teprve třídu Osoba tvořící celek. public class Adresa { private String ulice; private int cislo; private String mesto; // konstruktor deklarace public Adresa() { ulice = "nezadana"; cislo = 0; mesto = "neu vedene"; } public Adresa(String ulice, int cislo, String m esto){ this.ulice = ulice; this.cislo = cislo; thi s.mesto= mesto; } public Adresa(Adresa adresa) { // kopírovací k onstruktor this.ulice = adresa.getUlice(); this.cislo = adresa.getCislo(); this.mesto = adresa.getMesto(); } public String getUlice() { return ulice; } public int getCislo() { return cislo; } public String getMesto() { return mesto; } public String toString() { return "Ulice: "+ ulice+ " cislo: "+ cislo+ " mesto: "+ mesto; } public void tisk(){ System.out.println( this.toString()); } }

Pro úsporu místa nejsou v deklaraci třídy uvedeny přístupové a modifikační metody (get, set).

Page 47: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

47

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: " + s tav; return tx; } public void tisk() { System.out.println(this.toString()); } }

Nejdůležitějšími metodami jsou u třídy Ucet metody vlozeni a vyber. Bylo by samozřejmě potřebné také doplnit přístupové a modifikační metody. public class Osoba { private String jmeno; private int rokNarozeni; private Adresa adresa; //kompozice celek cast private Ucet ucet; // agregace - referencni k ompozice // deklarace konstruktoru public Osoba() { jmeno = "neuvedeno"; rokNarozeni = 0; adresa = new Adresa(), ucet = new Ucet(); } public Osoba(String jmeno, int rokNarozeni, Adr esa adresa, Ucet ucet) { this.jmeno= jmeno; this.rokNarozeni = rokNaro zeni; this.adresa = Adresa(adresa); this.ucet = ucet; } // pristupove a modifikacni metody public void setJmeno(String jmeno) { this.jmeno = jmeno; } public String getJmeno() { return jmeno;

Page 48: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

48

} public void setRokNarozeni(int rokNarozeni) { this.rokNarozeni = rokNarozeni; } public int getRokNarozeni() { return rokNarozeni; } public String toString() { String tx= "\nJmeno: " + jmeno + " rok narozeni: " + getRokNarozeni(); return tx; } public void tisk() { System.out.println(this.toString()); adresa.tisk(); ucet.tisk(); } public void setUcet(Ucet ucet) { this.ucet = ucet; } public Ucet getUcet() { return ucet; } public void vlozeni(int castka) { ucet.vlozeni(castka); } public void vyber(int castka) { ucet.vyber(castka); } }

U třídy osoba jsou již uvedeny přístupové a modifikační metody (get, set) pro jednoduché datové atributy deklarované v této třídě. Všimněte si také, jak se třída Osoba ve svých metodách obrací k objektům tříd Adresa a Ucet. Deklaruje na ně dílčí operace. A nyní ke kompozice a agregaci. Třída Osoba využívá kompozice pro skládání s objektem třídy Adresa. Vždy v obou deklarovaných konstruktorech je nutné zadat objekt třídy Adresa. Objekt třídy Ucet zadán není a proto zůstává jeho odkaz neobsazen, tedy ukazuje na null. Objekt třídy Ucet je do třídy Osoba dodán pomocí modifikační metody setUcet. Tak je také možno vyměnit stávající objekt třídy Ucet za jiný objekt třídy Ucet. Pro úplnost si ještě uvedeme výpis programu OsobaTest, který otestuje celou aplikaci. public class OsobaTest { public static void main(String args[]) { Adresa a1 = new Adresa("Na nabrezi",237,"Hav irov"); Adresa a2 = new Adresa("30. dubna",22,"Ostra va"); Ucet u2 = new Ucet(2, 300); Osoba o1 = new Osoba("Kamil",1988,a1, new Uc et()); Osoba o2 = new Osoba("Petr",1956,a2, u2); Ucet u1 = new Ucet(1,200); o1.setUcet(u1); o2.setUcet(u1); o1.tisk(); o2.tisk(); o1.vyber(300); o2.vlozeni(800);

Page 49: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

49

u1.tisk(); } }

Jedná se jen o krátký test funkčnosti agregace a kompozice. Tento příklad je možné dále rozvíjet, přidávat další objekty. Protože třída Osoba má deklarovaný buď bezparametrický konstruktor, anebo konstruktor se všemi parametry, v případě, že nemáme deklarovanou instanci třídy Ucet, doplníme new Ucet(), přímo do parametru konstruktoru. 4.3 Deklarace tříd s přetíženými konstruktory Jak jste již sami viděli, můžete si deklarovat své vlastní konstruktory. Důležité je ukázat, jak se dají konstruktory přetěžovat. Pro přetěžování se využívá pseudoproměnná this, která v dané třídě znamená, že odkazuje sama na sebe. Takže pokud je v následujícím příkladu uvedeno v těle konstruktoru this(0, 0, 0); znamená to, že se vyvolá konstruktor dané třídy, který očekává tři parametry, do nichž se dosadí v daním případě samé nuly. Je to přehlednější a lépe udržovatelné. Následující příklad je toho praktickou ukázkou. Kromě toho je také v příkladě vidět, jak může fungovat konstruktor, kterému předáváme celý objekt dané třídy. public class Time2 { private int hour; // 0 - 23 private int minute; // 0 - 59 private int second; // 0 - 59 public Time2() { this( 0, 0, 0 ); // invoke Time2 constructor with three arguments } public Time2( int h ) { this( h, 0, 0 ); // invoke Time2 constructor w ith three arguments } public Time2( int h, int m ) { this( h, m, 0 ); // invoke Time2 constructor with three arguments } public Time2( int h, int m, int s ) { setTime( h, m, s ); // invoke setTime to valid ate time } // Time2 constructor: another Time2 object suppl ied public Time2( Time2 time ) { // invoke Time2 three-argument constructor this( time.getHour(), time.getMinute(), time. getSecond() ); } public void setTime( int h, int m, int s ) { setHour( h ); setMinute( m ); setSecond( s ); }

Page 50: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

50

public void setHour( int h ) { hour = ( ( h >= 0 && h < 24 ) ? h : 0 ); } public void setMinute( int m ) { minute = ( ( m >= 0 && m < 60 ) ? m : 0 ); } public void setSecond( int s ) { second = ( ( s >= 0 && s < 60 ) ? s : 0 ); } public int getHour() { return hour; } public int getMinute() { return minute; } public int getSecond() { return second; } // convert to String in universal-time format (H H:MM:SS) public String toUniversalString() { return String.format( "%02d:%02d:%02d", getHour(), getMinute(), getSecond() ); } // end method toUniversalString // convert to String in standard-time format (H: MM:SS AM or PM) public String toString() { return String.format( "%d:%02d:%02d %s", ( (getHour() == 0 || getHour() == 12) ? 12 : getHour() % 12 ), getMinute(), getSecond(), ( getHour() < 12 ? "AM" : "PM" ) ); } // end method toString public void printU() { System.out.println(" "+this.toUniversalStrin g()); } public void printS() { System.out.println(" "+this.toString()); } public void tisk() { System.out.printf("%s %03d %2d %2d","Aktualni cas: ",getHour(),getMinute(), g etSecond()); } }

Testovací program třídy Time2Test. V něm jsme použili metodu printU( ) a printS( ) pro zkrácený výpis výsledků. V komentářích je uveden celý odpovídající kód. public class Time2Test { public static void main( String args[] ) { Time2 t1 = new Time2(); // 00:00:00 Time2 t2 = new Time2( 2 ); // 02:00:00 Time2 t3 = new Time2( 21, 34 ); // 21:34:00 Time2 t4 = new Time2( 12, 25, 42 ); // 12:25:42 Time2 t5 = new Time2( 27, 74, 99 ); // 00:00:00 Time2 t6 = new Time2( t4 ); // 12:25:42 System.out.println( "Constructed with:" ); System.out.println( "t1: all arguments defaulted " ); t1.printU(); // System.out.printf( " %s\n", t 1.toUniversalString() );

Page 51: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

51

t1.printS(); // System.out.printf( " %s\n", t 1.toString() ); System.out.println( "t2: hour specified; minute and second defaul ted" ); t2.printU(); //System.out.printf( " %s\n", t2 .toUniversalString() ); t2.printS(); //System.out.printf( " %s\n", t2 .toString() ); System.out.println( "t3: hour and minute specified; second defaul ted" ); t3.printU(); // System.out.printf( " %s\n", t 3.toUniversalString() ); t3.printS(); //System.out.printf( " %s\n", t3 .toString() ); System.out.println( "t4: hour, minute and second specified" ); t4.printU(); //System.out.printf( " %s\n", t4 .toUniversalString() ); t4.printS(); //System.out.printf( " %s\n", t4 .toString() ); System.out.println( "t5: all invalid values spec ified" ); t5.printU(); //System.out.printf( " %s\n", t5 .toUniversalString() ); t5.printS(); //System.out.printf( " %s\n", t 5.toString() ); System.out.println( "t6: Time2 object t4 specifi ed" ); t6.printU(); //System.out.printf( " %s\n", t6 .toUniversalString() ); t6.printS(); //System.out.printf( " %s\n", t6 .toString() ); } }

Příklady k této kapitole jsou uvedeny v textu. V analýze problému je třeba důsledně rozlišovat mezi skládáním a dědičností. Zatím co při skládání si můžeme pomoci otázkou „obsahuje něco, je v něčem obsažen“? při dědičnosti zase otázkou je součástí čeho? Při skládání objektů je velmi důležité si uvědomit, co je celek a co jsou jeho části. Dále se musíme zaměřit na vazby mezi celkem a částmi a z toho pak dostaneme výsledek, zda se bude jednat o kompozici objektů nebo agregaci. Jak se deklarují přetížené konstruktory, k čemu se používá klíčové slovo this?

Page 52: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

52

4.1 V jaké formě skládání objektů může existovat část nezávisle na celku? 4.2 V jaké formě skládání objektů může existovat část, která je obsažena ve více celcích? 4.1 V agregaci. 4.2 V agregaci. Kompozice a agregace hrají velmi důležitou úlohu při analýze a návrhu programových systémů. Protože Java nepoužívá „statické“ proměnné, je mezi oběma formami skládání objektů menší rozdíl, než v jiných jazycích.

Page 53: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

53

5. Návrhové vzory (Design Patterns) Návrhové vzory jsou důležitou součástí vytváření programového řešení hned z několika důvodů a proto s nimi začínáme již zde a teď. Návrhové vzory jsou doporučené postupy řešení často se vyskytujících úloh. Mohli bychom je přirovnat k vzorečkům z matematiky nebo fyziky. Například řešení kvadratické rovnice. Když známe vzoreček, řešení není žádným problémem. V návrhových vzorech však na rozdíl od matematických vzorců budeme pracovat s rozhraními, třídami a objekty. V návrhovém vzoru neexistují konkrétní třídy ani konkrétní objekty (případně jiné prvky modelů), ale vyskytují se v nich jejich proměnné (proměnné tříd, proměnné objektů atd.) Tyto proměnné jsou chápány jako role, za které lze dosadit konkrétní třídy, konkrétní objekty resp. jiné konkrétní prvky našeho řešeného problému, a tak dostáváme řešení požadované situace. První z výhod používání návrhových vzorů je snížení pravděpodobnosti výskytu chyb při použití návrhových vzorů, než při vlastním řešení. Navíc většinou návrhové vzory počítají s budoucím možným rozšířením, takže i zde usnadňují práci. Další nezanedbatelnou výhodou, jakýmsi vedlejším efektem (side-effect) je, že při studiu a používání návrhových vzorů lépe zvládneme zásady objektově orientovaného přístupu. Jednou z nich je využívání tzv. interfacového přístupu k řešení problému (přístup pomocí rozhraní). K problematice rozhraní se dostaneme až v dalších kapitolách. (Návrhové vzory uváděné v této kapitole jsou úvodní jednoduché vzory). Kromě přístupu přes rozhraní je v návrhových vzorech uplatněn také polymorfismus. Dá se dokonce říci, že nosným pilířem návrhových vzorů je využití polymorfismu v objektově orientovaném přístupu. Obecně lze vzor považovat za opětovně použitelný obecný návod k řešení problému, který se neustále opakuje v různých obdobách a v různých situacích. Na chápání a na použití návrhových vzorů je obtížná právě jejich abstrakce. Samotný vzor totiž nepopisuje nějakou konkrétní situaci k řešení, ale popisuje zobecnění všech takových situací. A teď již konkrétně k jednotlivým vzorům. 5.1 Přepravka Problém: Sloučení několika samostatných informací do jednoho celku. Například víme, že metody používané v deklaraci tříd vracejí pouze jedno hodnotu, nebo pouze jeden objekt. Podobají se takto funkcím. Co ale udělat, když bychom potřebovali, aby metoda vrátila více hodnot resp. více objektů? Kontext: Vrácení několika hodnot současně z dané metody. Řešení: Deklarace třídy typu Přepravka, jejíž datové atributy budou využity k uložení požadovaných samostatných informací. Návratovým objektem z metody pak může být právě objekt typu přepravka.

Page 54: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

54

Implementace: Datové atributy třídy Přepravka se deklarují jako veřejné (public), aby k nim byl snadnější přístup (nebylo nutné používat přístupových resp. modifikačních metod). Po přenesení hodnot z přepravky se objekt přepravky většinou „zahodí“, dále nepoužívá. N2kdy je vlastní třída Nejdříve několik úvodních vysvětlení. V následujícím příkladě máme v balíčku (package) messenger uvedeno několik tříd demonstrující návrhový vzor přepravka bez specifikace public. Pro takové třídy platí, že:

– jsou viditelné pouze v rámci svého balíčku, – nemusí mít svůj zdrojový kód uložen v souboru se stejným jménem, – mohou být uloženy v souboru s jinými třídami, – název souboru je podle třídy se specifikací public, v našem případě je tedy vše uloženo

v souboru „PrepravkaDemo.java“. V tomto návrhovém vzoru se často vyskytuje tzv. kopírovací konstruktor (copy constructor). Kopírovací konstruktor vytváří novou instanci jako kopii instance předané jako parametr. Datové atributy předávané instance se zkopírují do nové instance. Vlastní přepravku vytváří třída Point. Její datové atributy jsou deklarované jako public – nemusí se tedy při práci s nimi používat přístupové a modifikační metody (get a set). package messenger; class Point { //prepravka public int x, y, z; //prepravni atributy public Point(int x, int y, int z){ this.x = x; this.y = y; this.z = z; } public Point(Point p){// kopirovaci konstruktor this.x = p.x; this.y = p.y; this.z = p.z; } public String toString(){ return "x: "+x+" y: "+ y+" z: "+z; } public void print(){ System.out.println(this.toString()); } } class Vector{ public int magnitude; int direction; public Vector(int magnitude, int direction){ this.magnitude = magnitude; this.direction = direction; } } class Space{ public static Point translate(Point p, Vector v){ p = new Point(p);//nova instance pres kopirovaci konstruktor // nemodifikuje original p.x = p.x + 1; p.y = p.y + v.magnitude;

Page 55: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

55

p.z = p.z + v.direction; return p; } } public class PrepravkaDemo { public void test(){ Point p1 = new Point(1, 2, 3); Point p2 = Space.translate(p1, new Vector(11, 32)); String result = "p1: "+ p1 +" p2: "+ p2; System.out.println(result); } public static void main(String[] args){ PrepravkaDemo pd = new PrepravkaDemo(); pd.test(); } }

Vlastní využití návrhového vzoru přepravka je demonstrováno ve statické metodě translate třídy Space, která vrací právě tři jednoduché hodnoty typu int jako objekt třídy Point. Výraz: p = new Point(p); vytvoří novou instanci p třídy Point, do které se zkopírují původní datové atributy instance p předávané do této metody jako parametr. Stejné pojmenování objektů se používá z toho důvodu, abychom neupravili datové atributy původního objektu předávaného jako parametr. Výsledkem je tisk objektů p1 a p2, které se navzájem liší. 5.2 Singleton - jedináček Problém Zabezpečit, aby třída měla pouze jednu instanci (objekt) globálně viditelnou ze všech částí programu. Kontext V mnoha případech je třeba, aby ze třídy vznikla pouze jedna sdílená instance pro celý program. Řešení Implementace singletonu je celá řada. Mají však společné to, že definují konstruktor třídy, která má mít pouze jednu instanci jako soukromý (private). Tím je zabezpečeno, že se k tomuto konstruktoru dostaneme pouze prostřednictvím jiné další metody, která bude veřejná (public). Modifikátor final způsobí, že třída Singleton se již dále nemůže rozvíjet, nemůže mít další podtřídy. Je to z toho důvodu, aby eventuálně v podtřídě nemohl existovat konstruktor. Podstatné je, že deklarovaná třída má jeden konstruktor, který je deklarovaný jako private (soukromý). Není tedy přístupný přímo. Přímo v deklaraci třídy se vytvoří nová instance (objekt) s označením s, který je přístupný prostřednictvím metody getReference. Tato metoda je statická, což umožňuje vytvářet instance s využitím třídy: Singleton.getReference( );

Page 56: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

56

final class Singleton { private int i; private static Singleton s = new Singleton(47); private Singleton(int x) { i = x; } //konstruktor s jednim argumentem public static Singleton getReference() { return s; } //ziskani reference public int getValue() { return i; } public void setValue(int x) { i = x; } public String toString(){ return "Singleton i: "+this.getValue(); } public void print(){ System.out.println(this.toString()); } } public class SingletonTest { public void test() { Singleton s = Singleton.getReference(); //vytvoreni nove instance String result = "" + s.getValue(); System.out.println(result); Singleton s2 = Singleton.getReference(); s2.setValue(9); result = "" + s.getValue(); System.out.println(result); s.print(); s2.print(); if(s == s2)System.out.println("Rovnaji se"); else System.out.println("Nerovnaji se"); } public static void main(String[] args) { new SingletonTest().test(); } }

V uvedeném příkladu se vytvoří dvě instance s a s2, které obě ukazují na stejný objekt.

Příklady: Příklady popisovaných návrhových vzorů jsou uvedeny přímo v textu.

Návrhové vzory, jejich význam a použití.

Page 57: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

57

Návrhové vzory jsou doporučené postupy řešení často se vyskytujících úloh. U těchto úloh můžeme popsat problém, který se snažíme vyřešit (buď úplně, nebo alespoň částečně), kontext ve kterém daná situace nastává a navržené řešení.

Co je charakteristické pro návrhový vzor přepravka? Jaký problém řeší návrhový vzor singleton (jedináček)?

Co je charakteristické pro návrhové vzory?

4.1 Jak se zabezpečí, aby se k metodě třídy dostaly všechny objekty (instance) této třídy? 4.2 Co způsobí, když je konstruktor deklarovaný s modifikátorem private?

4.1 Metoda se označí klíčovým slovem static.

4.2 K tomuto konstruktoru není možné se dostat přímo, proto to řešíme prostřednictvím jiných metod, deklarovaných jako public – viz getInstance( ).

Návrhové vzory se používají k dokumentaci řešené problematiky, sníží se jimi pravděpodobnost chyb v řešení a pomáhají také k lepšímu zvládnutí principů objektově orientovaného přístupu.

Page 58: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

58

6 Balíčky, zapouzdření, samostatná aplikace JAR soubory Jedním z problémů větších (velkých) programových celků je kolize jmen použitých identifikátorů. Což v podstatě znamená, abychom my při vlastní tvorbě již nepoužili identifikátor použitý někde v knihovně, nebo identifikátor, který používáme sami, nebo používá spolupracující kolega. Java řeší kolizní problém jmen zavedením konceptu namespace (prostor jmen, nebo jmenný prostor), který se v Javě nazývá package česky paket nebo balíček. Znamená to, že skupiny příbuzných tříd jsou uloženy do jednoho balíčku. Balíček fyzicky představuje adresář na disku a bývá v různých vývojových prostředích realizován trošku odlišně, principiálně však stejně. Prostředí BlueJ je trochu omezenější při práci s balíčky než prostředí Elipse. Pro třídy v daném balíčku platí, že jejich identifikátory (názvy tříd) musí být jedinečné. K tomu, aby příslušná třída patřila do daného balíčku musí být příslušná třída uložena v patřičném adresáři (podadresáři) reprezentující daný balíček, ale také musí programátor explicitně potvrdit, že tam patří - zdrojový text musí začínat příkazem: package název_balíčku; V následujícím příkladě je uveden balíček směnárna. Pro název balíčku se používají malá písmena. Kromě toho se dají balíčky uspořádat hierarchicky, podobně jako adresáře souborů. Je to výhodné zejména pro ukládání jiných verzí, dále pak se do balíčků mohou ukládat různé typy souborů např. zdrojový, zkompilovaný, spakovaný atd. Názvy tříd Rozmístění tříd do balíčků ovlivní jejich názvy. Kdybychom jako doposud používali pouze názvy tříd, vystavovali bychom se nebezpečí stejných názvů tříd z různých balíčků. Proto se název třídy skládá z názvu balíčku (balíčků) a názvu třídy odděleného tečkami. Např. smenarna.SmenarnaBezPoplatku c8.inner.Dispatcher Třída Dispatcher je umístěna v balíčku ihned, který je součástí balíčku c8. 6.1 Balíčky a příkaz import Aby byl překladač ochoten pracovat s deklarovanými třídami v různých balíčcích, musíme použít příkaz import a deklarovat patřičnou cestu k balíčkům. Např.: import c8.inner.Dispatcher; je příkaz pro importování pouze třídy Dispatcher z uvedených balíčků. Naopak, pokud uvedeme pouze: import c8.inner.* budou se importovat všechny třídy uvedené v cestě balíčků. Jak jste si jistě všimli, podobným způsobem se importují i všechny požadované třídy z javovských knihoven.

Page 59: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

59

Zapouzdření Jednou z hlavních charakteristik, zmiňovanou již v první kapitole bylo zapouzdření. Zapouzdření znamená, že s datovými atributy objektu pracujeme výhradně pomocí metod a ne přímo. Výhoda tohoto způsobu je v tom, že implicitně při využití metod pracujeme s datovými atributy pouze v omezeních, které nám dovolují deklarované metody. Implementace tohoto způsobu spočívá v tom, že datové atributy jsou deklarovány s modifikátorem private a metody jsou deklarovány s modifikátorem public. V souvislosti se zapouzdřením se seznámíme se dvěma termíny: Rozhraní třídy je množina informací, které o sobě třída zveřejní. Mezi rozhraní patří např. vše, co je ve třídě označeno modifikátorem public. Rozhraní – anglicky interface. Implementace je vše, co má být před uživatelem ukryto. Je to způsob, jak je třída naprogramována, jak dělá to co umí. Java zavedla typ rozhraní explicitně. V rozhraní jsou totiž pouze metody deklarovány. Rozhraní totiž na rozdíl od tříd neříká vůbec nic o tom, jak budou jednotlivé metody implementovány. Pro snazší pochopení si uvedeme jednoduchý příklad směnárny, která bude realizována jako směnárna bez poplatku a směnárna s poplatkem. Ve výpisu bude nejdříve uvedeno rozhraní, dále pak třída směnárna bez poplatku, směnárna s poplatkem a třída směnárna test. package smenarna; public interface Smenarna { double vykup(double kolik); double prodej(double kolik); }

package smenarna; public class SmenarnaBezPoplatku implements Smenarna { private double kurz; public SmenarnaBezPoplatku(double kurz) { this.kurz = kurz; } public double getKurz() {return kurz; } public double vykup(double kolik) { return kolik / kurz; } public double prodej(double kolik) { return kolik * kurz; } }

package smenarna; public class SmenarnaSPoplatkem implements Smenarna { private double kurz; private double pMin; private double pProc;

Page 60: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

60

public SmenarnaSPoplatkem(double kurz, double pMin , double pProc) { this.kurz = kurz; this.pMin= pMin; this.pProc = pProc; } public double getKurz(){ return kurz; } public double getpMin() { return pMin; } public double getpProc() {return pProc; } public double vykup(double kolik) { double castkaBezPoplatku = kolik / getKurz(); double poplatek = castkaBezPoplatku * pProc / 10 0; if(poplatek < pMin) poplatek = pMin; return castkaBezPoplatku - poplatek; } public double prodej(double kolik) { double castkaBezPoplatku = kolik * getKurz(); double poplatek = castkaBezPoplatku * pProc / 10 0; if( poplatek < pMin) poplatek = pMin; return castkaBezPoplatku - poplatek; } }

package smenarna; public class SmenarnaTest { public static void main(String[] args){ SmenarnaBezPoplatku sbp = new SmenarnaBezPoplatku (1/28.3); SmenarnaSPoplatkem ssp = new SmenarnaSPoplatkem(1/ 28.3, 20, 7); double castkaKc = 3000; double castkaEu = 300; double v1 = sbp.vykup(castkaEu); System.out.printf("v1 vykup castka: Eu %7.2f castk a Kc: %7.2f\n" ,castkaEu, v1); double v2 = sbp.prodej(castkaKc); System.out.printf("v2 prodej castka: Kc %7.2f cast ka Eu: %7.2f\n" ,castkaKc, v2); double v3 = ssp.vykup(castkaEu); System.out.printf("v3 vykup castka: Eu %7.2f castk a Kc: %7.2f\n" ,castkaEu,v3); double v4 = ssp.prodej(castkaKc); System.out.printf("v4 prodej castka: Kc %7.2f cast ka Eu: %7.2f\n" ,castkaKc, v4); } }

Deklarace rozhraní obsahuje deklarace metod výkup() a prodej(), obě s jedním argumentem. Obě metody jsou implementovány různě ve třídách, které implementují rozhraní. Třída SmenarnaBezPoplatku implementuje obě metody odlišně než třída SmenarnaSPoplatkem. Formálně ale obě třídy musí dodržovat deklaraci uvedenou v rozhraní pro obě metody. Rozhraní dále tvoří souhrn informací, které se z hlaviček deklarovaných metod vyčíst nedají. Jedná se o podmínky, za kterých daná metoda pracuje, informace o tom, že daná instance je

Page 61: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

61

jedináček atd. Tento souhrn informací bývá označován jako kontrakt a měl by být uveden v dokumentačních komentářích třídy, jejich metod a atributů. Součástí dokumentace (a tím i obecně závazného kontraktu) by měly být následující informace:

– popis funkce metody, – význam jednotlivých parametrů a omezení na ně kladená, – další podmínky, které je třeba při volání metody splnit, – význam funkční hodnoty, – popis chybových situací k nimž může dojít.

Rozhraní jako takové nemůže samo o sobě vytvořit instanci. Proto pokud hovoříme o instanci rozhraní, máme tím na mysli instanci některé z tříd, která dané rozhraní implementovala. 6.2 Samostatná aplikace – JAR soubory Jedná se o spuštění přeložené aplikace bez nutnosti spouštět vývojové prostředí. Java umožňuje zapakovat celou aplikaci do speciálního souboru a tam pak spouštět obdobně, jako se spouští standardní spustitelné soubory. Jedinou podmínkou pro spuštění je instalovaná Java. Stačí pouze tzv. běhové prostředí JRE (Java RuntimeEnvironment). Zapakovaný soubor má příponu JAR (zkratka Java ARchive). Tento soubor je obyčejný ZIP soubor, který obsahuje kromě přeložených tříd dané aplikace ještě složku META-INF se souborem MANIFEST.MF, v němž jsou uloženy některé informace důležité pro spuštění souboru. V prostředí BlueJ je třeba zadat příkaz: Project -> Export a pak vybrat odpovídající třídu s metodou main a eventuálně zaškrtnout přidružení zdrojových souborů. Při této eventualitě se pak dá dále spakovaný soubor ještě následně upravovat úpravou zdrojových souborů a následným spakováním. Následně by pak spakovaný soubor měl fungovat jako normální aplikace. Nebude-li jej možno spustit poklepáním nebo jiným ze způsobů, není asi správně asociována přípona jar.

Page 62: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

62

Příklady k této kapitole jsou uvedeny přímo v textu, další možnosti deklarace rozhraní je uvedeno v dalších kapitolách. Pojem jmenného prostoru resp. balíčku, paketu. Hierarchie balíčků, příkaz import. JAR soubory. Balíčky řeší problém kolize názvů proměnných. V balíčku se sdružují třídy řešící podobný problém. Balíčky se dají vytvářet hierarchicky podobně jako soubory. JAR soubory jsou zazipované soubory pouze javovské aplikace, nebo ještě mohou obsahovat také zdrojové soubory, pro možnou další modifikaci. Hierarchicky členěné balíčky se oddělují tečkou nebo obyčejným lomítkem nebo obráceným lomítkem? Co představuje rozhraní a co představuje implementace? Jaké možnosti toto členění přináší a čím bylo vyvoláno? Java na rozdíl od jiných jazyků zavedla typ rozhraní explicitně. V rozhraní jsou totiž pouze metody deklarovány. Rozhraní totiž na rozdíl od tříd neříká vůbec nic o tom, jak budou jednotlivé metody implementovány.

Page 63: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

63

7 Dědičnost. Vztahy mezi nadtřídou a podtřídami. Konstruktory v podt řídách. Třída Object

Dědičnost v objektově orientovaném přístupu představuje mechanismus vytváření tříd a podtříd. Podtřída (subclass) dále specializuje svoji nadtřídu to formou dalších datových atributů, nebo dalších resp. specializovanějších metod. Další metody jednoduše představují metody, které nadtřída nedeklaruje. Specializovanější metody mají stejný název jak metody nadtřídy. V tomto případě dojde buď k úplnému „překrytí“ (lepší termín zastínění, anglický používaný termín – overriding) metody nadtřídy, nebo může být využit kód nadtřídy, který v podtřídě rozšíříme (specializujeme) s využitím pseudoproměnné super. Pseudoproměnná super odkazuje na nadtřídu. Obě uvedené možnosti si ukážeme na příkladech. Postup směrem od nadtřídy k podtřídě se nazývá specializace, opačný postup pak generalizace – zevšeobecnění. Vztahy podtřídy a nadtřídy spolu s dalšími pravidly, která při dědičnosti platí si vysvětlíme na příkladu rezervace. Rezervace je třída, která deklaruje základní datové atributy a operace pro vlakovou a leteckou rezervaci. Její datové atributy tvoří datum, reprezentované instanci třídy Calendar z balíčku java.util. Mezi objekty Rezervace a Calendar je spojení pomocí kompozice – tato asociace se realizuje v konstruktoru. Za zmínku stojí ještě to, že třída Calendar vytváří instance pomocí metody getInstance( ) (tovární metoda) a ne pomocí třídní metody new. Dalším datovým atributem deklarovaným ve třídě Rezervace je objekt třídy Osoba, reprezentujícího zákazníka. Mezi objekty tříd Rezervace a Osoba je spojení pomocí agregace. Tato asociace se realizuje pomocí metody setZakaznika() až v běhu programu. Podtřídami třídy Rezervace jsou třídy LeteckaRezervace a VlakovaRezervace. Vše je uvedeno v UML diagramu tříd.

+toString()+tisk()

Rezervace

+getLet()+getSedadlo()+toString()+tisk()

-let-sedadlo

LeteckaRezervace

+getVlak()+getVagon()+getSedadlo()+toString()+tisk()

-vlak-vagon-sedadlo

VlakovaRezervace

+getJmeno()+getPohlavi()+toString()+tisk()

-jmeno-pohlavi

Osoba

Calendar 11

1 1

Obr. 7.1 Diagram tříd UML

Page 64: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

64

Podstatou dědičnosti (vytváření hierarchii tříd) je, že objekty dědí od nadřazených objektů strukturu lokálních datových atributů (ne obsah) a operace které jsou nad nimi deklarovány. od jimi nadřazených objektů. Vzniká tak hierarchie objektů, přičemž nejobecnější objekty mají jen relativně málo společných vlastností (lokální data a operace nad nimi). Objekty, které jsou v hierarchii níže, mohou být postupně odlišovány od obecných. 7.1 Ukrývání informací v hierarchii tříd Na ukrývání informací používá jazyk Java modifikátory private, public a protected. Nejdříve probereme modifikátory public a private. Doposud jsme většinou popisovali datové atributy modifikátorem private (s výjimkou návrhového vzoru přepravka) a metody většinou modifikátorem public. V rámci jedné třídy jsme si mohli i dovolit nepoužívat přístupových a modifikačních metod a upravovat datové atributy přímo (bez těchto metod). Pokus se však z podtřídy odkazujeme na datové atributy nadtřídy, jinak než s využitím přístupových a modifikačních metod to nejde. Je to z toho důvodu, že právě datové atributy využívají modifikátor private a metody modifikátor public. Byla by zde ještě jedna alternativa, využívat pro datové atributy modifikátor protected, který zabezpečuje „přímý přístup“ (bez použití přístupových a modifikačních metod) k datovým atributům z podtřídy do nadtřídy. Jeho nevýhodou je však to, že datové atributy s modifikátorem protected jsou však „přímo“ přístupné také všem třídám v daném balíčku. Z jiného pohledu je však výhodné používat pouze metod k přístupu k datovým atributům, protože takto je zabezpečen pouze metodami definovaný přístup – což patří k základním principům objektově orientovaného přístupu.

Nyní uvedeme výpisy deklarovaných tříd.

import java.util.Calendar; class Osoba { private String jmeno; private String pohlavi; public Osoba() { super(); jmeno="neuvedeno"; pohlavi="neuvedeno"; } public Osoba(String jmeno, String pohlavi) { this.jmeno = jmeno; this.pohlavi= pohlavi; } public String toString() { return String.format(" %s: %s %s: %s \n", "jmeno",jmeno,"pohlavi",pohlavi); } public void tisk() { System.out.println(this.getClass().getName()+ this.toString()); } public String getJmeno() { return jmeno; } public String getPohlavi() { return pohlavi; } }// end Osoba

Page 65: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

65

public class Rezervace { public Rezervace(Calendar d){ datum = d; } Calendar datum; Osoba zakaznik; public void setZakaznik(Osoba o){ zakaznik = o; } public Osoba getZakaznik(){ return zakaznik; } public String toString(){ return String.format("Zakaznik: %sDatum: %tF", zakaznik.toString(), datum); } public void tisk(){ System.out.print(this.toString()); } }// end Rezervace

import java.util.Calendar; public class LeteckaRezervace extends Rezervace { public LeteckaRezervace(Calendar d, String l, Stri ng s){ super(d); let = l; sedadlo = s; } String let; String sedadlo; public String getLet(){ return let; } public String getSedadlo(){ return sedadlo; } public String toString(){ return String.format("%s \nLet: %s sedadlo: %s\n" , super.toString(),getLet(),getSedadlo()); } public void tisk(){ System.out.println(this.toString()); } }//end LeteckaRezervace

Jak je vidět z výpisu pro vyjádření vztahu podtřída & nadtřída se používá klíčové slovo extends (rozšiřuje).

import java.util.Calendar; public class VlakovaRezervace extends Rezervace { String vlak; int vagon; int sedadlo; public VlakovaRezervace(Calendar d, String v, int vg, int s){ super(d); vlak = v; vagon = vg; sedadlo = s;

Page 66: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

66

} public String getVlak(){ return vlak; } public int getVagon(){ return vagon; } public int getSedadlo(){ return sedadlo; } public String toString(){ return String.format("%s\nVlak: %s vagon: %4d, se dadlo: %3d\n" ,super.toString(),vlak, vagon, sedadlo); } public void tisk(){ System.out.println(this.toString()); } }//end VlakovaRezervace

Následující zdrojový výpis je třída RezervaceTest na které si ukážeme praktické využití dědičnosti.

import java.util.Calendar; public class RezervaceTest { public static void main(String[] args) { 1 VlakovaRezervace v = new VlakovaRezervace(Calenda r.getInstance(), 2 "Pendolino",506,28); 3 4 Osoba o = new Osoba("Adam","muz"); 5 v.setZakaznik(o); 6 7 LeteckaRezervace f = new LeteckaRezervace(Calenda r.getInstance(), 8 "Ok 225","14A"); 9 o = new Osoba("Alena","zena"); 10 f.setZakaznik(o); 11 // tisk vlakove rezervace 12 v.tisk(); 13 // tisk letecke rezervace 14 f.tisk(); 15 16 System.out.println("Let: "+f.let); 17 18 //chybny pristup - informace z nadtridy pouze pr ostrednictvim 19 // pristupovych metod 20 // System.out.println("Jmeno zakaznika: "+f.zakaznik.jmeno); 21 //spravne 22 System.out.println("Jmeno klienta: 23 "+f.getZakaznik().getJmeno()+"\n "); 24 // kvalifikace promenne r 25 Rezervace r; 26 r = v; // pretypovani na predka 27 r.tisk(); 28 r = f; // pretypovani na predka 28 r.tisk(); 30 System.out.println("Jmeno: "+r.getZakaznik().get Jmeno()+ 31 " pohlavi: "+r.getZakaznik().getPohlavi()); 32 //the method getLet() is undefined for the type Reservace; 33 // System.out.println("Let: "+r.getLet());

Page 67: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

67

34 35 LeteckaRezervace fr; 36 fr = (LeteckaRezervace) r; 37 System.out.println("Let: "+fr.getLet()); } }

Pro snadnější pochopení a vysvětlení jsou řádky výpisu očíslovány. Nyní ještě uvedeme výpis programu, to co se vypíše na konzolu.

Zakaznik: jmeno: Adam pohlavi: muz

Datum: 2006-09-06

Vlak: Pendolino vagon: 506, sedadlo: 28

Zakaznik: jmeno: Alena pohlavi: zena

Datum: 2006-09-06

Let: Ok 225 sedadlo: 14A

Let: Ok 225

Jmeno klienta: Alena

Zakaznik: jmeno: Adam pohlavi: muz

Datum: 2006-09-06

Vlak: Pendolino vagon: 506, sedadlo: 28

Zakaznik: jmeno: Alena pohlavi: zena

Datum: 2006-09-06

Let: Ok 225 sedadlo: 14A

Jmeno: Alena pohlavi: zena

Let: Ok 225

Poznámky k programu RezervaceTest: 1.-2. řádek - vytvoření objektu v třídy VlakovaRezervace. 4. řádek – vytvoření objektu třídy Osoba 5. řádek – přiřazení odkazu objektu třídy Osoba do objektu třídy VlakovaRezervace 13. resp. 15. řádek tisk objektů třídy VlakováRezervace resp. LeteckaRezervace 16. řádek – tisk datového atributu let objektu f třídy LeteckáRezervace. Nepoužíváme přístupovou metodu getLet(), protože jak objekt f je kvalifikovaný ve třídě LeteckaRezervace a datový atribut let patří do stejné třídy (nic však nebrání tomu využívat přístupovou metodu getLet()). 20. řádek – chyba je třeba použít přístupové metody jak pro zákazníka, tak i pro jeho jméno, vše je správně uvedeno v řádku 22-23 25. řádek – kvalifikace objektu r třídy Rezervace Protože třída Rezervace je všeobecnější, než LeteckaRezervace resp. VlakovaRezervace, je možné, aby odkazovala na objekty svých podtříd. Viz řádek 26, kdy odkazuje na objekt třídy VlakovaRezervace a řádek 28, kdy odkazuje na objekt třídy LeteckaRezervace. Obráceně to nejde (kvalifikujeme proměnnou jako objekt např. třídy VlakovaRezervace a snažili bychom do tohoto objektu přiřadit objekt třídy Rezervace – v uvedeném příkladě nemáme vytvořený objekt třídy Rezervace, proto můžete vyzkoušet sami).

Page 68: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

68

27. a 29. řádek demonstrují princip pozdní vazby. Objekt r kvalifikovaný jako Rezervace za běhu programu ukazuje na objekty svých podtříd a metoda tisk ukazuje, že se vytisknou požadované atributy objektů tříd, na které byla proměnná r „přesměrována“. Brzká vazba říká, že kód metody je znám již v době překladu programu. Naproti tomu pozdní vazba – náš nynější případ říká, že kód metody je určen až za běhu programu. Je to dáno tím, že proměnná kvalifikovaná jako proměnná dané nadtřídy může během běhu programu ukazovat na objekty svých podtříd. V případě, že dostane zprávu něco provést, se nejdříve musí zjistit, na který objekt daná proměnná aktuálně ukazuje a teprve podle toho se vybere metoda patřící ke třídě daného objektu. Další důležité pravidlo, které patří k dědičnosti je uvedeno v řádcích 30 a 33. V řádku 30 program vypisuje u proměnné r (kvalifikována jako Rezervace, ukazuje na objekt třídy LetovaRezervace) datové atributy jméno a pohlaví. vše proběhne bez problému. Když ale chceme na řádku 33 vypsat stejným způsobem číslo letu, překladač ohlásí chybu – metoda getLet() není deklarovaná pro typ Rezervace. Z toho plyne závěr, že proměnná má přístup prostřednictvím přístupových a modifikačních metod pouze k těm datovým atributům, které jsou deklarovány ve třídě, pro kterou byla daná proměnná kvalifikována. Konstruktory a d ědičnost Jak je vidět z výpisu programů, prvním příkazem v konstruktoru podtřídy je volání konstruktoru bezprostřední nadtřídy pomocí pseudoproměnné super (podobně jako když se v rámci jedné třídy odkazujeme na jiné konstruktory s využitím pseudoproměnné this). Samozřejmě rozlišujeme, zda je konstruktor nadtřídy bezparametrický – super(), nebo vyžaduje zaslání nějakých parametrů – super(p1, p2, p3 .. ). Za zmínku stojí, že v konstruktoru podtřídy musíme zadat všechny parametry, tedy i parametry všech nadtříd. Např. v konstruktoru VlakovéRezervace musíme kromě vlaku, vagonu a sedadla zadat samozřejmě první parametr datum. Pořadí je dané pořadím deklarace. 7.2 Překrývání (zastiňování) metod – další specializace metod. Metoda tisk(), která se vyskytuje v jednoduché hierarchii tříd je metodou, která je vždy překryta (zastíněna) v jakékoli další podtřídě. To znamená, že podle typu proměnné se vybere patřičná metoda tisk() a ta se provede. Naproti tomu metoda toString() je metoda, která dále specializuje funkčnost podtřídy. Když se blíže podíváme na její konstrukci tak zjistíme, že používá konstrukci super.toString(). super je odkaz na bezprostřední nadtřídu a toString() je metoda. To znamená, že metoda toString() je vytvořena tak, že nejdříve zajde do nejvyšší třídy v hierarchii tříd a do řetězcové proměnné uloží datové atributy této třídy. Pak sestoupí o úroveň níže a celý postup opakuje, až se dostane do aktuální třídy, kde přidá do řetězcové proměnné datové atributy aktuální třídy. 7.3 Třída Object hierarchie tříd v javovských balíčcích, abstraktní třídy a metody Třída Object představuje nejvyšší třídu v hierarchii tříd. Protože je umístěna v balíčku java.lang (implicitně importován) je celý název java.lang.Object. Všechny námi doposud

Page 69: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

69

definované definované třídy jsou implicitně podtřídami této třídy. Třídu Object můžeme uvést i explicitně. Např. public class Auto extends Object { ... } Třída Object poskytuje svým podtřídám pouze metody, je to vlastně abstraktní třída (viz dále). V následující tabulce jsou uvedeny metody třídy Object a jejich stručný význam. Doposud jsme se setkali s metodami toString() a getClass(). Metoda Popis clone metoda protected, bez argumentů, vytváří kopii objektu, na který je zavolána; v

dané třídě se předpokládá redefinování této metody jako public, která by měla implementovat rozhraní Clonable (balíček java.lang). Implicitní implementace této metody provádí tzv. shallow copy

equals metoda porovnává dva objekty a vrací true v případě rovnosti a false při nerovnosti; object1.equals(object2); metoda předpokládá redefinici ve třídách použití

finalize metoda protected, je volána garbage collectorem ke konečnému uklízení, nemá parametry, vrací void, zřídka kdy používaná

getClass metoda vrací informace o dané třídě, jméno třídy dostaneme pomocí metody getName()

hashCode hashovací tabulka je datová struktura, která přiřazuje objekt klíč k objektu hodnota, hashCode je hodnota vrácená metodou hashCode, která se používá ke stanovení místa, kde bude uložena odpovídající hodnota

notify notifyAll wait

metody využívané pro multithreading (aktivní objekty)

toString

tato metoda vrací textovou (řetězcovou) reprezentaci objektu

Tabulka 7.1 Metody třídy Object

Page 70: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

70

Příklady: Příklady k této kapitole jsou uvedeny v textu kapitoly. Další jsou uvedeny na webu.

Dědičnost, hierarchie tříd, přetypování předka na následníka.

Objekty mohou dědit strukturu lokálních dat (ne obsah) a operace s nimi, od nadřazených objektů. Abstraktní třída slouží pouze k deklaraci, neslouží k vytváření instancí. Třída Object je nejobecnější abstraktní třída v jazyce Java.

Jak se objekt podtřídy dostane k datovému atributu nadtřídy, který má modifikátor private? Jak probíhá překrývání (zastiňování) metod? K čemu a jak se používá pseudoproměnná super?

Při vytváření objektu podtřídy je nutné zadat: a) pouze hodnoty datových atributů dané podtřídy? b) hodnoty datových atrubutů jak dané podtřídy, tak i všech nadtříd?

7.1 Proměnná kvalifikovaná v nadtřídě a přetypovaná na objekt podtřídy se může dostat ke kterým datovým atributům? 7.2 Jak by bylo možné doplnit do příkladu Rezervace ještě rezervaci na autobusy?

Page 71: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

71

7.1 Dostane se pouze k datovým atributům třídy, pro niž byla kvalifikována.. 7.2. Do hierarchie tříd by se doplnila třída AutobusovaRezervace na úroveň letecké a vlakové rezervace.

Hierarchie tříd (dědičnost) patří k základním charakteristikám objektově orientovaného programování. Důležité je správně navrhnout hierarchii tříd a příslušnost jednotlivých datových atributů k odpovídajícím třídám.

Page 72: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

72

8 Polymorfismus. Abstraktní třídy a metody. Rozhraní tvorba a použití Polymorfismus znamená mnohotvárnost a je třetí elementární funkční vlastností každého objektově orientovaného programovacího jazyka. Předchozí dvě jsou datová abstrakce a dědičnost. Polymorfní operace jsou operace s mnoha implementacemi. Polymorfismus úzce souvisí s tzv. pozdní vazbou, se kterou jsme se seznámili již v předchozí kapitole. Tam jsme v názorném příkladě také provedli konverzi na obecnější datový typ. (řádek 26 a 28 ve výpisu RezervaceTest). Ukažme si vše na příkladě hierarchie hudebních nástrojů.

+hrat()+co()+sladit()

Nastroj

+hrat()+co()+sladit()

Dechy

+hrat()+co()+sladit()

Bici

+hrat()+co()+sladit()

Smycce

+hrat()+co()

Dreveny

+hrat()+sladit()

Zestovy

Obr. 8.1 Hierarchie tříd hudebních nástrojů Následuje programové zpracování. package muzika.dedicnost; class Nastroj { public void hrat(){ System.out.println("Nastroj.hrat()"); } public String co(){ return "Nastroj"; } public void sladit(){}; }// end Nastroj class Dechy extends Nastroj { public void hrat() { System.out.println("Dechy.hrat()"); }

Page 73: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

73

public String co() { return "Dechy"; } public void sladit() { } }// end Dechy class Bici extends Nastroj { public void hrat() { System.out.println("Bici.hrat"); } public String co() { return "Bici"; } public void sladit() { } }// end Bici class Smycce extends Nastroj { public void hrat() { System.out.println("Smycce.hrat()"); } public String co() { return "Smycce"; } public void sladit() { } }// end Smycce class Zestovy extends Dechy { public void hrat(){ System.out.println("Zestovy.hrat()"); } public void sladit(){ System.out.println("Zestovy.sladit"); } }// end Zestovy class Dreveny extends Dechy { public void hrat(){ System.out.println("Dechy.hrat()"); } public String co(){ return "Dreveny"; } }// end Dreveny public class MuzikaD{ public static void tisk(String s){ System.out.println(s); } public static void main(String[] args){ Nastroj n = new Nastroj(); n.hrat(); tisk(n.co());

Page 74: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

74

n.sladit(); Bici b = new Bici(); b.hrat(); tisk(b.co()); b.sladit(); Zestovy z = new Zestovy(); z.hrat(); MuzikaD.tisk(z.co()); z.sladit(); n = b; //pretyppovani na predka n.hrat(); tisk(n.co()); n.sladit(); Bici bc; bc = (Bici) n; // pretypovani na naslednika // obdobne Dechy dc; dc = z; //pretypovani na predka dc.hrat(); dc.sladit(); } }

V uvedeném příkladě reprezentovala proměnná n polymorfní (vicetvarý) objekt, který může ukazovat na libovolný objekt svých podtříd. Stejně tak proměnná bc může ukazovat na objekt vlastní třídy a svých podtříd, tedy objekty tříd Dreveny a Zestovy. Zasláním zpráv hrat a ladit pak tento polymorfní objekt reaguje různě podle toho, na který objekt (dané třídy) právě ukazuje. Další praktické využití této vlastnosti si ukážeme v následující kapitole. 8.1 Abstraktní třída a abstraktní metody. Třída Nastroj a její metody jsou vlastně pouze formální. Nástroj obecně nepředstavuje žádný konkrétní hudební nástroj, ale pouze zobecnění. Důvodem pro vytvoření této třídy je deklarace společných datových atributů a společného rámce deklarování metod (hlavičky metod – ne implementace), protože každá podtřída si překryje metodu vlastním kódem. Z toho důvodu bývá výhodné deklarovat takovou třídu jako abstraktní. Abstraktní třída se označuje klíčovým slovem abstract a nedají se od ní vytvářet objekty (instance) a obsahuje alespoň jednu abstraktní metodu. Další z výhod abstraktní třídy je, že objekt kvalifikovaný v této třídě může odkazovat na libovolný objekt podtříd dané abstraktní třídy. Uvedeme nyní kód abstraktní třídu Nastroj. abstract class Nastroj { public abstract void hrat(); //nema telo { ... } String co(){ return "Nastroj"; } public abstract void sladit(); // nema telo } end Nastroj

Page 75: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

75

Abstraktní třída Nastroj obsahuje dvě abstraktní metody, které jsou také označeny klíčovým slovem abstract. Abstraktní metoda je metoda neúplná, obsahuje totiž pouze deklarace, ale její tělo je prázdné (nesmí být uvedeny složené závorky). Pro implementaci rozhraní se používá klíčové slovo implements. 8.2 Rozhraní - tvorba, použití Rozhraní je v podstatě další zobecnění abstraktní třídy. Rozdíly mezi rozhraním a abstraktní třídou jsou následující: – Z formálního hlediska se místo klíčového slova abstract používá klíčové slovo interface,

– Třída může implementovat libovolný počet rozhraní, ale může být podtřídou pouze jedné abstraktní třídy (nebo jedné konkrétní, samozřejmě)

– Abstraktní třída může mít i neabstraktní metody, rozhraní má pouze „abstraktní“ metody

– Abstraktní třída může deklarovat datové atributy, rozhraní může deklarovat pouze konstanty typu static final (třídní a neměnné)

– Abstraktní třída může pro své metody použít modifikátory private, protected, public, nebo nic – přístup pouze v rámci balíčku, všechny metody rozhraní jsou implicitně public (i když nemají žádný modifikátor)

– Abstraktní třída může definovat konstruktory, rozhraní ne. To že třída může implementovat libovolný počet rozhraní umožňuje řešit problémy vícenásobné dědičnosti. Jak jste si všimli, Java umožňuje pouze jednoduchou dědičnost, každá třída má pouze jednu nadtřídu. Pro konkrétní představu uvedeme dva příklady. Nejdříve předchozí příklad upravený tak, že třída Nastroj bude deklarovaná jako rozhraní. interface Nastroj { void hrat(); String co(); void sladit(); } class Dechy implements Nastroj { public void hrat() { System.out.println("Dechy.hrat()"); } public String co() { return "Dechy"; } public void sladit() { } } class Bici implements Nastroj { public void hrat() { System.out.println("Bici.hrat"); } public String co() { return "Bici";

Page 76: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

76

} public void sladit() { } } class Smycce implements Nastroj { public void hrat() { System.out.println("Smycce.hrat()"); } public String co() { return "Smycce"; } public void sladit() { } } class Zestovy extends Dechy { public void hrat(){ System.out.println("Zestovy.hrat()"); } public void sladit(){ System.out.println("Zestovy.sladit"); } } class Dreveny extends Dechy { public void hrat(){ System.out.println("Dechy.hrat()"); } public String co(){ return "Dreveny"; } } public class MuzikaR{ public static void tisk(String s){ System.out.println(s); } public static void main(String[] args){ Bici b = new Bici(); b.hrat(); tisk(b.co()); b.sladit(); Zestovy z = new Zestovy(); z.hrat(); MuzikaR.tisk(z.co()); z.sladit(); } }

Hlavní důvod pro zavedení rozhraní je, schopnost zobecnit objekt na více možných typů. Druhý důvod je stejný jako u rodičovských abstraktních tříd – jednak bráníme programátorovi ve vytvoření objektu této třídy, a jednak poskytujeme informaci, že tato třída slouží jako rozhraní. Je-li možné vytvořit bázovou třídu bez jakékoli definice metod, nebo datových atributů, je dobré dát přednost rozhraní před abstraktní třídou. Máte-li představu co bude bázovou třídou, měli byste se nejdříve pokusit o vytvoření rozhraní. K volbě abstraktní třídy

Page 77: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

77

byste se měli uchýlit teprve až budete moci rozšířit rozhraní o definici metod, nebo datových atributů (členských proměnných). Poslední možností je vytvoření abstraktní třídy. Klíčové slovo interface vytváří naprosto abstraktní třídu, takovou, která neposkytuje dědicům žádnou implementaci. Klíčové slovo interface je více než pouhé dovedení abstraktní třídy ad absurdum. Interface nabízí jistou obměnu vícenásobné dědičnosti; vytvořit třídu, kterou lze přetypovat na více než jeden rodičovský typ. Rozhraní se podobá dědičnosti. Jakmile rozhraní jednou implementujeme, stane se tato implementace běžnou třídou, kterou lze rozšiřovat klasickým způsobem. (Zde ji lze použít jako předka jiných tříd). Následuje další příklad s více rozhraními.

+bojovat()

«rozhraní»UmiBojovat

+plavat()

«rozhraní»UmiPlavat

+letat()

«rozhraní»UmiLetat

+bojovat()

CharakterovyZnak

+plavat()+letat()

Hrdina

Obr. 8.2 Diagram tříd v jazyce UML interface UmiBojovat { void bojovat(); } interface UmiPlavat { void plavat(); } interface UmiLetat { void letat(); } class CharakterovyZnak { public void bojovat() { System.out.println(this.getClass().getName()+" b ojovat"); }

Page 78: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

78

} class Hrdina extends CharakterovyZnak implements UmiBojovat, UmiLetat, UmiPlavat { public void letat() { System.out.println(this.getClass().getName()+" l etat"); } public void plavat() { System.out.println(this.getClass().getName()+" pl avat"); } } public class Dobrodruh { public static void main(String[] args) { UmiBojovat x = new Hrdina(); x.bojovat(); CharakterovyZnak y = new CharakterovyZnak(); y.bojovat(); UmiLetat k = new Hrdina(); k.letat(); UmiPlavat p = new Hrdina(); p.plavat(); } }

Příklady k této kapitole jsou uvedeny v textu kapitoly resp. na webu. Polymorfismus patří k základním charakteristikám objektově orientovaného přístupu. V této souvislosti je třeba také pochopit význam tzv. polymorfního (vícetvarého) objektu. Je to objekt kvalifikovaný v nadtřídě, který ukazuje na objekty svých podtříd. Abstraktní třída a rozhraní jsou dva důležité pojmy v objektově orientovaném programování. Rozhraní je v podstatě „čistá“ abstraktní třída obsahující pouze „hlavičky“ metod. Vyjmenujte rozdíly mezi abstraktní třídou a rozhraním? Jak je rozhraní implementováno?

Page 79: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

79

Jak je implementováno rozhraní? Poněkud divná otázka. 8.1 Může být rozhraní organizováno v hierarchii? 8.2 Proč se objekt kvalifikuje většinou na typ rozhraní? 8.1 Ano, rozhraní mohou být organizována v hierarchii. 8.2 Tím že je objekt kvalifikován jako rozhraní mu umožňuje, aby mohl ukazovat na všechny objekty, které to rozhraní implementují. V této kapitole jsme si ukázali na praktických příkladech, jak se dá využívat mechanismus abstraktní třídy a mechanismus rozhraní. Použití rozhraní vépe ještě vynikne při spojejí s nějakým seznamem, do kterého můžeme například ukládat všechny konkrétní objekty, které implementují dané rozhraní.

Page 80: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

80

9 Využití polí pro ukládání objektů. Realizace zásobníku a fronty s využitím polí Pole jako takové se probírají v algoritmech a datových strukturách. Proto se v této kapitole soustředíme na to, jak využít strukturu pole v jednoduchém seznamu, kterému můžeme říkat register (nebo česky – registr). Hlavní nevýhodou při standardní práci s poli je, že většinou pracujeme přímo s polem a nevyužíváme metody, které bychom si mohli deklarovat, pokud by uvedené pole sloužilo jako datový atribut již zmíněné struktury register. Navíc v příkladech pro práci s poli se implicitně předpokládá, že všechny prvky pole budou obsazeny a takto vždy pracujeme jen s funkcí length, která vrací kapacitní velikost pole. Ta může být v mnoha směrech podobná zásobníku (přidáváme prvky na konec seznamu a odebíráme prvky z konce seznamu), ale navíc můžeme zpřístupnit prvek podle indexu, přidat základní metodu hledání, tisku atd. Základní operace (metody), které bychom od takto deklarované třídy požadovali by byli následující: - vložení prvku (na konec seznamu) - odebrání prvku (z konce seznamu) - vytvoření textové reprezentace všech prvků pole - vytištění textové reprezentace všech prvků pole - vrácení prvku pole na pozici zadané indexem - kapacita pole - obsazenost pole - zda je seznam prázdný - zda je seznam plný atd.

Nejdříve si ukážeme strukturu číselného seznamu, pak přejdeme k seznamu, do kterého se dají ukládat odkazy na obecné objekty. Další záležitosti jako generický seznam, využití iterátoru pro průchod seznamem a další pak budou náplní kurzu XOBO2. Na rozcvičení se dáme práci s polem a základní operace: import java.util.Scanner; public class Pole { public static void main(String args[]) { int[] a, b; int[] c = new int [6]; String[] s = {"raz","dva","tri"}; double[] d = {1.3, -15.36, 23.568, -487.2, 4 4.0}; b= new int[4]; System.out.printf("%s:%9s\n","Index","Hodnota "); for (int i=0; i< s.length; ++i) System.out.printf("%5s:%9s\n",i,s[i]); // zadavani ciselnych hodnot Scanner vstup = new Scanner(System.in); System.out.println("Zadejte hodnoty pole:\n") ; for (int i= 0; i< b.length; ++i) { System.out.printf("%4d: ",i+1); b[i] = vstup.nextInt(); } // tisk pole

Page 81: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

81

System.out.printf("%s\n","Hodnota"); for (int i : b) System.out.printf("%7d\n",i); int suma = 0; for (int j: b) suma += j; // suma = suma + j double prumer = suma / b.length; System.out.printf("%20s::%.3f\n","Prumer pole b = ",prumer); prumer = (double) suma / b.length; System.out.printf("%20s::%.3f\n","Prumer pole b = ",prumer); // prirazeni a = b; System.out.printf("%s:%9s\n","Index","Hodnota "); for (int i=0; i< a.length; ++i) System.out.printf("%5s:%9d\n",i,a[i]); if (a == b) System.out.println("ANO"); else S ystem.out.println("NE"); a[3] = -200; System.out.printf("%s:%9s\n","Index","Hodnota "); for (int i=0; i< b.length; i++) System.out.printf("%5s:%9d\n",i,b[i]); System.out.printf("%s:%9s\n","Index","Hodnota "); for (int i= b.length - 1; i >= 0; i--) System.out.printf("%5s:%9d\n",i,b[i]); System.out.printf("%s:%9s\n","Index","Hodnota "); for (int i= b.length - 1; i >= 0; i -= 2) // i=i-2 System.out.printf("%5s:%9d\n",i,b[i]); } }

Výpis uvedeného programu je následující: Index: Hodnota 0: raz 1: dva 2: tri Zadejte hodnoty pole: 1: -12 2: 43 3: 88 4: 148 Hodnota -12 43 88 148 Prumer pole b = ::66,000 Prumer pole b = ::66,750 Index: Hodnota 0: -12 1: 43 2: 88 3: 148 ANO Index: Hodnota 0: -12 1: 43 2: 88 3: -200 Index: Hodnota

Page 82: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

82

3: -200 2: 88 1: 43 0: -12 Index: Hodnota 3: -200 1: 43

Register pro celá čísla: import java.util.Random; public class Register { private int pole[]; private int top; // konstruktor public Register(int pocet) { pole = new int[pocet]; top = -1; } public void vlozit(int prvek) { if ((top + 1) < pole.length) { top += 1; pole[top] = prvek; } else System.out.println("Registr je obsazeny "); } public String toString() { String t = String.format("%5s %9s\n","Index"," Hodnota"); for (int i=0; i<=top; ++i) t = t+ String.format("%5d %9d\n",i, pole[i]) ; return t; } public void tisk() { System.out.println(this.toString()); } public int getPrvek(int i) { int prvek=0; if (i>=0 && i<= top ) // top ukazuje skutecnou velikost prvek = pole[i]; else System.out.printf("%s %d %s\n","Index",i," mimo rozsah"); return prvek; } public int vyhledat(int prvek) { int vysl = -1; for (int i=0; i<= top; i++) { System.out.println("Index: "+i); //kontrolni radek prubehu vypoctu if (pole[i]== prvek) { vysl = i; break; }} return vysl; } public void odstranit(int prvek) { int v = vyhledat(prvek); if (v > -1 ) {for (int i=v; i< top; i++) pole[i] = pole[i+1]; top -= 1;

Page 83: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

83

System.out.println("Prvek odstranen"); } else System.out.printf("%s %d %s\n","Prvek",p rvek,"neni v registru"); } public int minimum() { int min = pole[0]; for (int cislo: pole) if (cislo < min) min = cislo; return min; } public void naplnitNahodne() { Random nc = new Random(); for (int i=0; i<pole.length; i++) vlozit(nc.nextInt()); } public void nahodneNaplnitLimit(int limit) { Random nc = new Random(); int prvek; for (int i=0; i<pole.length; i++) { prvek= 1+ nc.nextInt(limit); this.vlozit(prvek); } } public void vymazatVse() { top = -1; } } Pro třídy Register platí: - pole je vnitřní datový atribut třídy Register

- top – ukazatel na místo, kam se má vložit nový prvek

- do Registru (pole) můžeme vkládat libovolné primitivní a objektové typy

- třída Register obsahuje všechny potřebné metody, další nutné metody můžeme doplnit

- velikost datového atributu pole se určí až za běhu programu prostřednictvím konstruktoru

- pozor na problém indexů: • číslování 0 – (n-1) můžeme ponechat, • anebo zvolit indexování 1 - n

Metody uplatněné ve třídě Register: - getPrvek(i) – na zadaný (0 – top) index vrací prvek registru

- vyhledat(int prvek) – vrátí index na kterém se nachází zadaný prvek, nebo -1 (prvek není v registru)

- odstranit(int prvek) – odstraní z registru zadaný prvek - pokud v registru existuje, jinak se nic neprovede (ostatní prvky se posunou o jedno místo směrem k počátku – indexu nula)

- minimum – vrátí minimální prvek uložený do registru Význam dalších uváděných metod je zřejmý z kódu programu.

Page 84: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

84

Dále si ukážeme, jak napsát metodu, která vytvoří nový objekt od třídy Register a do něho uloží např. prvky původního objektu, které jsou např. pouze záporné resp. větší, či menší než zadané číslo. Je to podobný princip, který jste již viděli ve třetí kapitole při práci s objekty třídy Bod, kdy metoda rozdíl vytvořila nový objekt třídy Bod do jehož souřadnic uložila hodnoty rozdílu bodu, který zprávu přijal a bodu zaslaného jako argument do uvedené metody. // vytvari novy Register public class Register { private int pole[]; private int top; // dalsi metody // misto pole[i] pouzijeme this.getPrvek(i) 1 public Register novyReg(int cislo) 2 { Register nReg = new Register(pole.length); 3 for (int i=0; i<=top; i++) 4 // požadovaná podmínka 5 if(this.getPrvek(i)> cislo) nReg.vlozit(t his.getPrvek(i)); 6 return nReg; } }

Zde uvádíme pouze hlavičku třídy, datové atributy a patřičnou metodu. Ostatní metody jsou uvedeny v celkovém výpisu na webu. Název metody je novyReg() a vrací referenci na objekt třídy Register. V hlavičce metody je také uveden celočíselný argument cislo, který se používá pro testování prvků pole. Pokud bychom vybírali pouze kladné, nebo záporné prvky, je deklarace tohoto argumentu zbytečná. Pro lepší vysvětlení jsou řádky číslované. Ve 2. řádku deklarujeme objekt nReg třídy Register a nastavujeme velikost jeho datového atributu pole na velikost původního objektu třídy Register. V 5. řádku provádíme test a pokud je podmínka splněna, uložíme patřičný prvek do nového objektu třídy Register s využitím metody vlozit(). (Tato metoda zabezpečí potřebné nastavení indexů a další kontroly). Pro pořádek ještě uvedeme použití uvedené metody. public class RegisterTest { public static void main(String args[]) { Register a = new Register(5); a.vlozit(10); a.vlozit(15); a.vlozit(-44); a.tisk(); a.odstranit(15); a.tisk(); System.out.println("Minimalni prvek registru: " + a.minimum()); int b= a.getPrvek(15); RegisterA b = new RegisterA(5);

Page 85: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

85

b.vlozit(15); b.vlozit(25); b.vlozit(33); Register c = b.novyReg(20); // Register c; c = b.novyReg(20); // prikaz uvedeny v levo – mozno zapsat // pomoci dvou prikazu uvedenych vpravo c.tisk(); } }

Nyní si ukážeme, jak by se uvedený kód třídy Register jen málo změnil, pokud bychom do něho ukládali např. objekty třídy Osoba. Uvedeme proto nejdříve kód třídy Osoba a pak upravenou třídu Register RegisterOsoba. public class Osoba { private String jmeno; private int rokNarozeni; private int vaha; // deklarace konstruktoru public Osoba() { jmeno = "neuvedeno"; rokNarozeni = 0; vaha = 0; } public Osoba(String jmeno, int rokNarozeni, int vaha) { this.jmeno= jmeno; this.rokNarozeni = rokNaro zeni; this.vaha = vaha; } // pristupove a modifikacni metody public void setJmeno(String jmeno) { this.jmeno = jmeno; } public String getJmeno() { return jmeno; } public void setRokNarozeni(int rokNarozeni) { this.rokNarozeni = rokNarozeni; } public int getRokNarozeni() { return rokNarozeni; } public int getVek(int rok) { return rok - getRokNarozeni(); } public void setVaha(int vaha) { this.vaha = vaha; } public int getVaha() { return vaha; } public String toString() { return String.format("%5s %s %15s %4d %5s %d \n", "Jmeno",getJmeno(),"rok narozeni:",getRok Narozeni(), "vaha:",getVaha()); } public void tisk() { System.out.println(this.toString()); } }

Page 86: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

86

public class RegisterOsoba { private Osoba pole[]; private int top; // konstruktor public RegisterOsoba(int pocet){ top = -1; pole = new Osoba[pocet]; } public void vlozit(Osoba prvek) { if ((top + 1) < pole.length) { top += 1; pole[top] = prvek; } else System.out.println("Registr je obsazeny" ); } public Osoba getPrvek(int i) { Osoba prvek=null; if (i>=0 && i< pole.length) prvek = pole[i]; else System.out.printf("%s %d %s\n","Index",i, "mimo rozsah"); return prvek; } public int vyhledat(String jmeno) { int vysl = -1; for (int i=0; i<= top; i++) { System.out.println(" i, jmeno: "+i+" "+pol e[i].getJmeno()); if (pole[i].getJmeno()== jmeno) {vysl = i; break; }} return vysl; } public int vyhledat(Osoba prvek) { int vysl = -1; for (int i=0; i<= top; i++) { System.out.println("Index: "+i); if (pole[i]== prvek) { vysl = i; break; }} return vysl; } public void odstranit(Osoba prvek) { int v = vyhledat(prvek); if (v == -1) System.out.printf("%s %d %s\n","P rvek",prvek,"neni v registru"); else this.odstranit(v); } public void odstranit(String jmeno) { int v = vyhledat(jmeno); if (v == -1) System.out.printf("%s %s %s","Jmeno :",jmeno,"neni v registru"); else this.odstranit(v); } public void odstranit(int v) { for (int i=0; i< top - v; i++) pole[v+i] = pole[v+i+1]; top -= 1;

Page 87: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

87

System.out.println("Prvek odstranen"); } public String toString() { String t = String.format("%5s %16s\n","Index","H odnota"); for (int i=0; i<=top; ++i) t = t+ String.format("%5d ",i)+pole[i].toStr ing(); return t; } public void tisk() { System.out.println(this.toString()); } // pozor na pouziti zjednoduseneho for public Osoba minVaha() { Osoba min = pole[0]; for (Osoba zaznam: pole) { if (zaznam != null) if (zaznam.getVaha() < min.getVaha()) min = zaznam; } return min; } public Osoba maxVek() { Osoba max = pole[0]; for (int i=0; i<= top; i++) if (pole[i].getVek(2005)> max.getVek(2005 )) max = pole[i]; return max; } }

Ve třídě RegisterOsoba jsou např. uvedeny metody minVaha() (vyhledá osobu s minimální vahou) a maxVek(). Uvnitř metody minVaha() používáme zkrácený cyklus for, který má pro nás tu nepříjemnou vlastnost, že vždy prochází celým polem (až po pole.length). My však celé pole nemusíme mít obsazené, od toho máme datový atribut top, který ukazuje na poslední obsazenou pozici v poli. Proto musíme otestovat, zda načtený prvek se nerovná null. Pokud bychom to neošetřili, vyvolalo by to výjimku a konec programu způsobenou tím, že příjemce zprávy je null a ten nereaguje na námi deklarované zprávy. Závěrem si ještě uvedeme třídu Fronta, která je zajímavější než zásobník, protože vytváří „kruhový seznam“ z pole. public class Fronta { private int a[]; private int pocet, zapisUk, cteniUk; public Fronta(int n) { a = new int[n]; zapisUk = 0; pocet = 0; cteniUk = -1; } public void zapis(int prvek) { if (pocet >= a.length) System.out.println("Fronta je plna - zapis neproveden"); else { pocet++; a[zapisUk] = prvek; zapisUk = ((zapisUk+1) % a.length); } }

Page 88: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

88

public int cteni() { int prvek = 0; if (pocet <= 0) System.out.println("Fronta je prazdna"); else { pocet--; cteniUk = (cteniUk + 1) % a.le ngth; prvek = a[cteniUk]; } return prvek; } public int velikost() { return pocet; } public boolean prazdna() { return (pocet == 0); } public boolean plna() { return (pocet == a.length ); } public int kapacita() { return a.length; } public String toString() { int index = cteniUk; String t=""; t = String.format("%s\n%s%13s\n","Fronta vy pis","Index","Hodnota"); for (int i=0; i< pocet; i++) { index = (index +1) % a.length; t += String.format("%5d%13d\n",index,+ a[index]); } return t; } public void tisk() { System.out.println(this.toString()); } }

Metody tisk() a toString() jsou navíc pro naši kontrolu. Jak vidíte z uvedeného kódu, třída má tři pracovní datové atributy pro správu fronty a to ukazatel na zápis, ukazatel na čtení a počet uložených prvků ve frontě. V metodách čtení a zápisu jsou ukazatele modifikovány s využitím operace zbytek po celočíselném dělení. To právě umožňuje vytvořit „kruhový seznam. Příklady k této kapitole jsou uvedeny uvnitř kapitoly eventuálně na web. Je dobré si pamatovat rozdíly mezi abstraktní třídou a rozhraním. Jazyk Java deklaruje rozhraní jako samostatný typ. To je významný posun oproti jiným objektově orientovaným jazykům. Praxe jednoznačně potvrdila, výhodnost tohoto samostatného typu, který umožňuje tvorbu programových komponent.

Page 89: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

89

Vyjmenujte základní zabudované operace pro datovou strukturu pole. Co umožňuje třídě když implementuje několik rozhraní? 9.1 Jak se liší deklarace třídy Register pro ukládání např. objektů třídy Osoba od ukládání např. celočíselných hodnot. 9.2 Jakým jiným způsobem by se dala realizovat datová struktura fronta. (Pokud bychom nepoužily tzv. kruhový buffer). 9.1 Pouze v deklaraci datové struktury a pak ve všech deklaracích prvků pole se musí objevit odpovídající typ. Generické typy umožňují deklaraci seznamů ještě zefektivnit viz. další semestr. 9.2 Datová struktura fronta by se dala také realizovat tak, že po odebrání prvku ze začátku fronty bychom všechny prvky posunuly o jeden směrem k nultému indexu (začátku pole) a následně pak upravili pomocné proměnné. Podobným způsobem pracuje metoda pro odstranění prvku z registru. Deklarace vlastního seznamu formou třídy Register se může jevit zbytečná když existuje efektivní knihovna kontejner, která obsahuje všechny základní datové struktury typu seznam. Výhodou třídy Register je v tom, že názorně ukazuje, jak lze objektově využít datovou strukturu pole a jak lze aplikovat základní metody. To se dá pak snadno využívat např., v metodě, která vytváří nový register obsahující pouze vyprané prvky původního seznamu.

Page 90: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

90

10 Návrhové vzory – pokračování 10.1 Jednoduchá tovární metoda – Simple Factory Method Někdy se také používá termín statická tovární metoda (její metoda je deklarována skutečně jako statická). Jedná se o zjednodušenou verzi obecnějšího návrhového vzoru Tovární metoda. Problém: Potřeba získání odkazu na objekt, ale přímé použití konstruktoru není z nejrůznějších příčin optimální. Často je potřeba vytváření nových instancí nějakým způsobem hlídat, nebo provést nějaké akce, které konstruktor nedovolí. Kontext: Nejdříve si řekneme,e o nevýhodách konstruktoru. - prvním příkazem složitějšího konstruktoru je volání přetíženého konstruktoru nebo

rodičovského konstruktoru, - konstruktor nesmí používat žádné překryvné metody - konstruktor vždy vytvoří novou instanci (objekt).

Někdy ale potřebujeme k další práci získat nejdříve odkaz na objekt, s nímž budeme dále pracovat, ale nezajímá nás, zda to bude nový objekt, či již existující. Dalším problémem, se kterým si konstruktor neporadí, je mít možnost vrátit instanci deklarované třídy, nebo instanci podtříd této třídy. Řešeni: Řešením je definovat tzv. tovární metodu, která bude vracet požadovanou instanci. Většinou se uvedená metoda nazývá factory(), nebo getInstance() či valueOf() – to v případě, kdy se převádí nějaká hodnota na instanci dané třídy. Někdy se také využívá název getNazevTridy např. Tvar.getTvar(). V uvedených příkladech používáme název factory. Nejdříve uvedeme diagram tříd UML:

+factory()+draw()+erase()

Shape

+draw()+erase()

Square

+draw()+erase()

Circle

Page 91: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

91

Shape je abstraktní třída, která deklaruje třídní (statickou) metodu factory, která vytváří nové objekty buď třídy Square (čtverec) nebo třídy Circle (kruh). Dále deklaruje abstraktní metody draw() (kresli) a erase() (vymaž). V první (jednodušší variantě) programu používáme v metodě factory příkaz switch, který podle indexu rozhoduje, jaký objekt metoda vrátí. abstract class Shape { private static int index = 0; public abstract void draw(); public abstract void erase(); public static Shape factory() { Shape sh = null;; switch(index++ % 4) { case 0: sh = new Circle(); break; case 1: sh = new Square(); break; case 2: sh = new Square(); break; case 3: sh = new Circle(); break; default: System.out.println("Spatne definovane m aximum"); } return sh; } } class Circle extends Shape { Circle() {} // Package-access constructor public void draw() { System.out.println("Circle.draw"); } public void erase() { System.out.println("Circle.erase"); } } class Square extends Shape { Square() {} // Package-access constructor public void draw() { System.out.println("Square.draw"); } public void erase() { System.out.println("Square.erase"); } } public class ShapeFactoryS { public static void main(String[] args) { for(int i=0; i<=4; i++){ Shape s = Shape.factory(); System.out.println("New shape "+ s.getClass().get SimpleName()); s.draw(); s.erase(); } } }

Ve druhé variantě si ukážeme použití objektu třídy Registr, do kterého jsou ukládány nově vytvořené objekty a pak jsou tisknuty. Třída Register je deklarovaná podobně jako třída RegisterOsoba, proto její výpis zde není uveden.

Page 92: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

92

abstract class Shape { public abstract void draw(); public abstract void erase(); public static Shape factory(String type) { if(type.equals("Circle")) return new Circle(); if(type.equals("Square")) return new Square(); throw new RuntimeException( "Bad shape creation: " + type); } } class Circle extends Shape { Circle() {} // Package-access constructor public void draw() { System.out.println("Circle.draw"); } public void erase() { System.out.println("Circle.erase"); } } class Square extends Shape { Square() {} // Package-access constructor public void draw() { System.out.println("Square.draw"); } public void erase() { System.out.println("Square.erase"); } } public class ShapeFactory1 { public static void main(String args[]) { RegisterS r = new RegisterS(7); r.vlozit(Shape.factory("Circle")); r.vlozit(Shape.factory("Square")); r.vlozit(Shape.factory("Circle")); r.vlozit(Shape.factory("Square")); r.vlozit(Shape.factory("Circle")); r.vlozit(Shape.factory("Square")); for(int i=0; i<r.getTop(); i++){ Shape s = r.getPrvek(i); s.draw(); s.erase(); } } }

Výraz Shape.factory(„Circle“) vrátí objekt třídy Circle, která je podtřídou třídy Shape. V tomto případě textově zadáváme, jaký objekt má metoda factory vrátit (konstruktory tříd Circle a Square jsou ve třídní metodě factory. 10.2 Návrhový vzor State (stav) Problém: Stav objektu je kombinací aktuálních hodnot jeho atributů. Při zaslání zprávy set objektu, se přiřadí nějaká hodnota příslušnému atributu objektu. Tím se změnil jeho stav. Objekty obyčejně mění své vlastní stavy, jak se vykonávají jejich metody.

Page 93: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

93

V některých případech však jednoduchá implementace tohoto principu pomocí if nebo switch příkazů může vést k velmi nepřehledným konstrukcím a jejich obtížné údržbě. Kontext: Představme si, že v daném objektu existuje datový atribut atrState, který může nabývat tří hodnot 1, 2, 3. Na základě tohoto stavu se pak objekt rozhoduje v metodě OperaceA() pro volání jiných metod např. takto: public operaceA() { . . . switch (atrState) { 1 : ob.a1( ); 2 : ob.a2( ); 3 : ob.a3( ); }

Pobobně probíhá i operaceB( ). public operaceB() { . . . switch (atrState) { 1 : ob.b1( ); 2 : ob.b2( ); 3 : ob.b3( ); }

Ob reprezentuje odpovídajíví objekt, kterému je zaslaná zpráva. Řešení: Větvení, tak jak je uvedeno ve schematickém příkladu se provádět nebude. Rozdělení kódu se provede tak, že se k sobě dají operace pro stejný stav (mají samozřejmě různou implementaci) a to: a1 a b1 a a2 a b2 atd. Toto rozdělení se provede pomocí nových tříd. Zavede se nové rozhraní, které můžeme nazvat State. Toto rozhraní bude deklarovat polymorfní operace (operace mající stejný název, ale různou implementaci) operaceA( ) i operaceB( ). V dalším kroku vytvoříme tolik tříd implementujících rozhraní State, kolik je stavů. Pokud máme tři stavy, budou to tři třídy, které budou mít různou implementaci metod (operaceA( ) operaceB( )). Stručně bychom mohli říct, že záměrem tohoto vzoru je distribuovat stavově specifickou logiku do tříd, které reprezentují stavy objektu.

Page 94: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

94

+operA()+operB()

PuvodniTrida

+operaceA()+operaceB()

«rozhraní»State

+operaceA()+operaceB()

State 1

+operaceA()+operaceB()

State 2

+operaceA()+operaceB()

State 3

1 1

Metody operA( ) a operB( ) v PuvodniTrida vyvolávají uvnitř svých těl metody state.operaceA( ) resp. state.operaceB( ). Nejdříve si uvedeme kód programu, který je inspirovaný pohádkou o princi zakletém jako žába (frog) a princezně jejímž polibkem se opět stává princem. V tomto programu není uplatněn návrhový vzor State. class Creature { private boolean isFrog = true; public void greet() { if(isFrog) System.out.println("Ribbet!"); else System.out.println("Darling!"); } public void kiss() { isFrog = false; } } public class KissingPrincess { public static void main(String args[]) { Creature creature = new Creature(); creature.greet(); creature.kiss(); creature.greet(); } }

Nyní následuje diagram tříd UML pro stejný příklad ve kterénm se využívá návrhový vzor State.

Page 95: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

95

+changeState()+greet()

Creature

+response()

«rozhraní»State

+response()

Frog

+response()

Prince

1 1

interface State{ String response(); } class Creature { private State state; Creature(State state){ this.state = state; } // change state public void changeState(State newState) { state = newState; } public void greet() { System.out.println(state.response()); } } class Frog implements State { public String response() { return "Ribbet!"; } } class Prince implements State { public String response() { return "Darling!"; } } /* public class KissingPrincess { * Creature creature = new Creature(new Frog()); * public void test() { * creature.greet(); * creature.changeState(new Prince()); * creature.greet(); *} * public static void main(String args[]) { * new KissingPrincess().test(); * } */ public class KissingPrincess{ public static void main(String args[]){ Creature creature = new Creature(new Frog()); creature.greet(); creature.changeState(new Prince());

Page 96: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

96

creature.greet(); creature.greet(); creature.changeState(new Frog()); creature.greet(); } }

Část, která je uvedena jako komentář pracuje stejně, ale metody jsou vyvolány pomocí metody test třídy KissingPrincess. Příklad jsem se snažil zjednodušit, aby se dal snadno pochopit. Dále uvedeme obecnější příklad na využití tohoto návrhového vzoru.

+operation1()+operation2()+operation3()

«rozhraní»State

+changeState()+service1()+service2()+service3()

ServiceProvider

+operation1()+operation2()+operation3()

Implementation1

+operation1()+operation2()+operation3()

Implementation2

1 1

interface State { void operation1(); void operation2(); void operation3(); } class ServiceProvider { private State state; public ServiceProvider(State state) { this.state = state; } public void changeState(State newState) { state = newState; } // Pass method calls to the implementation: public void service1() { // ... state.operation1(); // ... state.operation3(); } public void service2() { // ... state.operation1(); // ...

Page 97: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

97

state.operation2(); } public void service3() { // ... state.operation3(); // ... state.operation2(); } } class Implementation1 implements State { public void operation1() { System.out.println("Implementation1.operation1( )"); } public void operation2() { System.out.println("Implementation1.operation2( )"); } public void operation3() { System.out.println("Implementation1.operation3( )"); } } class Implementation2 implements State { public void operation1() { System.out.println("Implementation2.operation1( )"); } public void operation2() { System.out.println("Implementation2.operation2( )"); } public void operation3() { System.out.println("Implementation2.operation3( )"); } } public class StateDemo { static void run(ServiceProvider sp) { sp.service1(); sp.service2(); sp.service3(); } ServiceProvider sp = new ServiceProvider(new Implementation1()); public void test() { run(sp); System.out.println("Change implementation"); sp.changeState(new Implementation2()); run(sp); } public static void main(String args[]) { new StateDemo().test(); } }

Jak je vidět z diagramu tříd UML, jedná se o stejný princip, jako v předešném příkladě, jen zaměření je obecnější.

Page 98: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

98

10.3 Návrhový vzor Proxy Problém: Přímý přístup k danému objektu není vhodný ani např. z hlediska bezpečnosti žádoucí. Kontext: Standardní objekt poskytuje své služby (metody) v souladu se svým rozhraním. Během jeho „života“ však nastávají případy, kdy daný objekt nemůže splnit všechny inzerované služby. Např. dlouhá doba nahrávání objektu (v případě, že je perzistentní), nebo objekt běží na jiném počítači. V takových a podobných případech je výhodné zavést tzv. proxy objekt (zástupce), který na sebe bere zodpovědnost, že zaslané zprávy předá cílovému objektu. Řešení: Záměrem vzoru proxy je řídit přístup k objektu prostřednictvím zástupce.

+f()+g()+h()

«rozhraní»ProxyBase

+f()+g()+h()

Proxy

+f()+g()+h()

RealObject

1 1

RealObject představuje skutečný objekt se kterým jsme přímo komunikovali. Objekt Proxy je zástupce, který nám komunikaci zprostředkovává a rozhraní ProxyBase zabezpečí stejné metody v obou objektech. interface ProxyBase { void f(); void g(); void h(); } class Proxy implements ProxyBase { private ProxyBase implementation; public Proxy() { implementation = new RealObject(); } // Pass method calls to the implementation: public void f() { implementation.f(); } public void g() { implementation.g(); } public void h() { implementation.h(); } } class RealObject implements ProxyBase { public void f() {

Page 99: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

99

System.out.println("Operation.f()"); } public void g() { System.out.println("Operation.g()"); } public void h() { System.out.println("Operation.h()"); } } public class ProxyDemo { public static void main(String args[]) { Proxy p = new Proxy(); p.f(); p.g(); p.h(); } }

Třída Proxy má referenci na RealObject realizovanou pomocí proměnné implementation. Klient komunikuje pouze s objektem Proxy, který má stejné rozhraní jako RealObject. Vlastní metody jsou ale deklarované pouze v objektu RealObject. Při porovnání návrhových vzorů State a Proxy dojdeme na některé shodné rysy a naopak na některé rozdílné záležitosti.

• Oba návrhové vzory State a Proxy využívají zástupnou třídu (ServiceProvider, Proxy) • reálná třída, která „skutečně provádí metody“ je skryta za touto zástupnou třídou • princip obou vzorů – volají se metody třídy Nahrada, které vyvolají metody

implementačních tříd Vzory State a Proxy jsou si velice podobné, vzor Proxy je speciálním případem vzoru State.

• vzor Proxy má jen jednu implementaci • vzor State má více než jednu implementaci • vzor Proxy se používá k řízení přístupu k jedné implementace • vzor State dovoluje měnit implementace dynamicky.

10.4 Návrhový vzor Command Problém: Standardní cesta pro vykonání metody je její vyvolání. V některých situacích nejsme schopni řídit načasování (timing) kontextu, ve kterém by se měly metody volat. Dalším problémem je flexibilní zpracování návratové hodnoty metody. Kontext: Klient, který využívá služeb (metod) objektu volá operaci, která vrací návratovou hodnotu, podle které se rozhodne o dalším pokračování programu. Pro jednoduchost předpokládejme návrat celočíselné hodnoty např. 1 a 2 a podle jich se rozhodne jak dále. Samozřejmě je třeba ještě ošetřit jinou návratovou hodnotu, která způsobí chybu. Původní řešení je následující:

Page 100: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

100

switch (hodnota) { 1 : a.nejakaOperace( ); 2 : b.jinaOperace( ); }

Uvedené řešení je funkční, ale není flexibilní. Přidání další možné návratové hodnoty znamená přepsat (doplnit) uvedený kód. Řešení: Zaveďme rozhraní s operací execute( ). Pro každé zpracování návratové hodnoty zavedeme další třídu pod tímto rozhraním a v této třídě implementujeme metodu execute( ) . Ta pak provede patřičné operace např. a.nejakaOperace( ). Jinými slovy zapouzdříme požadavek (metodu) do podoby objektu (objektové proměnné), takže můžeme s požadavkem pracovat jako s každou jinou proměnnou, což vede k parametrizaci požadavků (dynamické tvorbě seznamu požadavků, dosazení požadavku za jiný požadavek apod.

Klient

+execute()

«rozhraní»Command

+execute()

Command1

+execute()

Command2

+nejakaOperace()

Trida A

+jinaOperace()

Trida B

1

1

1

1

1 1

Na diagramu tříd UML vidíme možné řešení. Každá z tříd Command1, Command2 má ještě vazbu na konkrétní třídu, jejíž operace se může následně vyvolat. V kódu demonstračního programu, který dále uvádíme je v těchto třídách uvedena pouze operace pro výpis na konzole. interface Command { void execute(); } class Hello implements Command {

Page 101: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

101

public void execute() { System.out.println("Hello "); } } class World implements Command { public void execute() { System.out.println("World! "); } } class IAm implements Command { public void execute() { System.out.println("I'm the command pattern!"); } } public class CommandPattern{ private RegisterC rc = new RegisterC(6); public void test(){ rc.vlozit(new Hello()); rc.vlozit(new World()); rc.vlozit(new IAm()); } public void run(){ for(int i=0; i<=rc.getTop(); i++) rc.getPrvek(i).execute(); } public static void main(String args[]) { CommandPattern cp = new CommandPattern(); cp.test(); cp.run(); new IAm().execute(); // jednoradkove reseni new World().execute(); } }

V programu využíváme třídu Register, která je upravena pro ukládání objektů třídy Command. Tyto úpravy se dají dělat genericky, ale to budeme probírat až v následujícím díle (semestru).

Příklady: Příklady návrhových vzorů jsou uvedeny v textu.

Návrhové vzory Simple faktory, State, Proxy, Command – jejich struktura a použití.

Page 102: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

102

Polymorfismus a rozhraní deklarované jako samostatný typ je využito v uvedených návrhových vzorech. Další obecnou myšlenkou, která je využita je „zabalení“ metody do objektu, který pak ve své podstatě může být polymorfní a umožňuje řešit různé možnosti programu.

Co mají společného návrhový vzor Proxy a návrhový vzor State?

Je vám již nyní zřejmé, jak byste použili prakticky návrhový vzor Commander?

10.1 Co má společného návrhový vzor Singleton s návrhovým vzorem Factory?

10.1 Návrhový vzor singleton vlastně používá jednoduchou metodu vzoru Faktory.

V této kapitole jsme uvedli další čtyři návrhové vzory. Většinou jsme je uvedli v té nejjednodušší podobě, kterou budeme později ještě dále rozšiřovat. Příkladem může být vzor Factory, který má další rozšíření. Dalším aspektem je fakt, že v praktických aplikacích návrhové vzory často ještě obsahují další návrhové vzory.

Pecinovský R.: Myslíme objektově v jazyku Java 5.0, Grada 2004

Arlow J., Neustadt I.: UML a unifikovaný proces vývoje aplikací. ComputerPress 2003

Page 103: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

103

Gamma E., Helm R., Johnson R., Vlissides J.: Návrh programů pomocí vzorů. Grada 2003

Page-Jones: Základy objektově orientovaného návrhu v UML. Grada 2001

Polák, Merunka: Objektově orientované programování, Objektově orientované pojmy. Softwarové noviny 1993 série článků.

htpp://java. sun.com

http://albert.osu.cz/hunka - zdrojové příklady a učební text

Příklady k samostatnému zpracování a odeslání emailem ke kontrole. 1. Vytvořte třídu Strom, která je složena z objektu třídy Kruh (reprezentuje korunu stromu) a

objektu třídy Obdélník (reprezentuje kmen stromu). Doplňte také třídu Kruh (složena z objektu třídy Bod – střed a celočíselné hodnoty představující poloměr) a třídu Obdélník, kterou představují dva objekty třídy Bod (protilehlé body). V každé třídě doplňte bezparametrické konstruktory a konstruktory s parametry a dále metody posun – s celočíselnými parametry pro posun ve směru osy x a y, metody toString( ) a metody tisk(). Vytvořte třídu StromTest ve které vytvoříte objekt Strom a aplikujete na něj všechny metody. Souřadnice bodů zadáte libovolně. Struktura jednotlivých tříd je uvedena v diagramu tříd UML.

+posun()+toString()+tisk()

Strom

+posun()+toString()+tisk()

Obdelnik

-polomer

Kruh

+posun()+toString()+tisk()

-x-y

Bod

11

1 1

1

2

1

1

2. Vytvořte třídu Vypocet, která bude mít třídní (static) metodu vzdálenost() pro výpočet délky úsečky zadané dvěma krajními body. (Body jsou objekty probírané třídy Bod, kterou si buď vytvoříte nebo zkopírujete do vašeho projektu). Metoda vzdálenost() má dva parametry typu Bod – krajní body úsečky. Metoda zjistí rozdíl v x souřadnicích, dále pak v y souřadnicích a pro vlastní výpočet použiej Pythagorovu větu. Pro umocnění a odmocnění použijte výraz z knihovny matematických funkcí: vysledek = Math.sqrt (x*x + y*y)

Page 104: Objektově orientované programování 1 XOBO1hunka/vyuka/javaOOP/XOBO1_java.pdf1 Objektově orientované programování 1 XOBO1 Autor: Doc. Ing. František Hu ňka, CSc

104

Souřadnice bodů deklarujte double. Dále vytvořte třídu VypocetTest, kde metodu prakticky ověříte na více bodech.

3. Vytvořte jednoduchou aplikaci pro zaměstnavatele, který platí jednak faktury a dále platí pracovníky měsíčním platem. Vytvořte proto rozhraní Placeni, které bude obsahovat metodu vyplacenaCastka. Toto rozhraní implementuje třída Pracovník, která má datové atributy jmeno, bydliště a plat a přístupové a modifikační metody k datovým atributům a metodu vyplacenaCastka, která pouze vrací datový atribut plat. Rozhraní Placeni implementuje také třída Faktura, která obsahuje datové atributy cislo, pocetJednotek a cenaZaJednotku. Dále třída obsahuje přístupové a modifikační metody a metodu vyplacenaCastka, která vynásobí pocetJednotek s cenouZaJednotku a tento výsledek vrátí v uvedené metodě. Objekty tříd Faktura a Pracovník budete ukládat do třídy Register, kterou si upravíte tak, aby byla schopna vkládat objekty typu Placeni (tedy rozhraní). Vytvořte třídu PlaceníTest, kde vytvoříte dva objekty třídy Pracovník a dva objekty třídy Faktura a uložte je do objektu třídy Register. Následně pak všechny objekty vytiskněte. Struktura tříd je uvedena v diagramu tříd UML.

+vyplacenaCastka()

«rozhraní»Placeni

+vyplacenaCastka()

-cislo-pocetJednotek-cenaZaJednotku

Faktura

-jmeno-bydliste-plat

Pracovnik

Register

1

*