Objektové programování a výjimky 2. cvičení Y36PJV, X36PJV Úloha: navrhněte program pro řešení úloh na obvody ve stacionárním ustáleném stavu. Využijte maximálně výhod objektově orientovaného programování: zapoudření (encapsulation) – private, public, protected a friend přístup dědičnosti (inheritance) – „udělejte Kaipan z Favorita“ polymorfizmu (pozdní vazba) Definujte třídy reprezentující následující stavební prvky elektrických obvodů v SUS: Zkrat (spojený vodič) Rozpojený vodič Rezistor Sériová kombinace dílčích obvodů Paralelní kombinace dílčích obvodů Jak se v SUS chová kapacitor a induktor? Mgr. David Vít, K336, © 2007
Předpokládejme pro jednoduchost obvody, kde je pouze jeden zdroj a máme určit celkovou hodnotu odporu obvodu v části bez zdroje. Mgr. David Vít, K336, © 2007
Objektové řešení bude snadné a snadno rozšiřitelné Navrhneme program, který pro výše uvedené obvody po vyjmutí proudového či napěťového zdroje určí celkovou hodnotu rezistorové části obvodu s tím, že budeme moci některé části obvodu nahrazovat zkratem či rozpojeným obvodem. Celkovou vypočtenou hodnotu odporu rezistorové části obvodu pak budeme používat pro výpočty napětí, resp. proudu na zdroji v závislosti na proudu, resp. napětí zdroje, dle Ohmova zákona. Námi navržený program bude obecný, takže umožní definovat libovolné konečné zapojení sestávající se z výše uvedených obvodových prvků Objektové řešení bude snadné a snadno rozšiřitelné Mgr. David Vít, K336, © 2007
Ve stacionárním ustáleném stavu platí Ohmův zákon: Předpokládejme, že pro každý obvod v SUS je charakteristickým údajem hodnota stejnosměrného odporu. Jak to vypadá pro jednotlivé obvodové prvky? rezistor – má konečný odpor zkrat – má nulový odpor rozpojený vodič – má nekonečně velký odpor sériová kombinace (suma odporů) – platí vzorec vpravo: paralelní kombinace (suma vodivostí) – platí vzorec vlevo Mgr. David Vít, K336, © 2007
1. Identifikace společných částí Pro účely naší úlohy identifikujeme nejprve společné znaky všech odporových obvodů (bez zdroje) bez ohledu na to, jak složité jsou: Obvod má hodnotu celkového odporu Obvod můžeme nějak pojmenovat Pokud známe hodnotu proudu zdroje, můžeme spočítat napětí na něm pomocí Ohmova zákona Pokud známe hodnotu napětí zdroje, můžeme spočítat proud na něm pomocí Ohmova zákona Ohmův zákon platí stejně pro všechny obvody v SUS bez ohledu na jejich složitost. Mgr. David Vít, K336, © 2007
2. návrh společného základu Můžeme definovat abstraktní třídu s názvem AbstraktniObvod Nevíme, jak se u něj bude určovat hodnota odporu, takže metodu vratOdpor označíme jako abstraktní Pokud máme některou metodu abstraktní, tak musí být abstraktní třída celá, Java totiž neví, jak by se při zavolání abstraktní metoda provedla. Přestože neznáme přesnou implementaci abstraktní metody, víme, co má dělat a ostatní metody ji mohou používat. U metod vratssProud a vratssNapeti víme, co přesně mají dělat a jejich implementace je stejná pro všechny obvody v SUS. Takže je můžeme označit jako konečné, klíčovým slovem final. public final double vratssProud(double napeti) { return napeti / vratOdpor (); // Ohmuv zakon } Metoda informace má nějaké základní vlastnosti pro všechny obvody, ale nevylučujeme, že bychom ji mohli změnit pro konkrétní případ. Mgr. David Vít, K336, © 2007
3. Třída AbstraktniObvod public abstract class AbstraktniObvod { protected String oznaceni; // pristupne i v dedenych tridach public AbstraktniObvod(String oznaceni) { this.oznaceni = oznaceni ; } // tyto metody nemuze konkretni implementace zmenit public final double vratssProud(double napeti) { return napeti / vratOdpor (); // Ohmuv zakon public final double vratNapeti(double ssProud) { return ssProud * vratOdpor () ; // Ohmuv zakon // tuto metodu muze kazda konkretni implementace menit public String informace() { return oznaceni ; // tuto metodu musi definovat kazda konkretni implementace public abstract double vratOdpor(); Mgr. David Vít, K336, © 2007
4. Hierarchie obvodových prvků Nyní se musíme rozhodnout, jak budou vypadat reprezentace dalších obvodových prvků Jejich společným základem je abstraktní třída AbstraktniObvod. My nadefinujeme chování abstraktní metody vratOdpor podle vlastností jednotlivých obvodových prvků, případně upravíme metodu informace, pokud je nezbytné Ukážeme si to na třídě Rezistor Pozor, každá public třída musí mít svůj soubor stejného jména!!! Domácí úloha: obdobným způsobem udělejte třídu Zkrat a RozpojenyVodic. To, jak se mají chovat, jsme si popsali na začátku. Mgr. David Vít, K336, © 2007
5. Třída Rezistor Instanci třídy Rezistor již můžeme vytvořit pomocí new a použít, protože není abstraktní, využíváme dědičnost. public class Rezistor extends AbstraktniObvod { private double odpor = 1000.0 ; public Rezistor(String oznaceni, double odpor) { super (oznaceni) ; // tohle nemusime delat znovu this.odpor = odpor ; } // upravime si, chceme vice informaci(oznaceni dostupne) public String informace() { return "Rezistor " + oznaceni + "=" + odpor + " Ohmu"; // pro tuto tridu Java vi, jak se ma chovat tato metoda public double vratOdpor(){ return odpor ; Mgr. David Vít, K336, © 2007
6. Jak to vyzkoušíme? public class Main { public static void main(String[] args) { AbstraktniObvod r1 = new Rezistor ("R1", 1000.0) ; Rezistor r2 = new Rezistor ("R2", 2000.0) ; System.out.println(r1.informace() + " odpor " + r1.vratOdpor() + " Ohmu"); System.out.println(r2.informace() + " odpor " + r2.vratOdpor() + " Ohmu"); } Díky dědičnosti je Rezistor také AbstraktniObvod, proto lze takto volat. Přestože používáme referenci na základní typ AbstraktniObvod, tak díky mechanizmu polymorfizmu Java pozná verzi metody nejblíže typu, který byl užit v new. Říká se tomu „pozdní vazba“. Zkuste si: if (r1 instanceof Rezistor) { // …. } String str = r1.getClass().getName () ; // zjistá typ Jedná se o tzv. vlastnosti RTTI (můžete zjistit zda jde o instanci daného typu v hierarchii dědičnosti). Občas se může hodit. Mgr. David Vít, K336, © 2007
7. Struktura ostatních tříd návrhu Mgr. David Vít, K336, © 2007
8. Složené obvody K čemu a proč je dobrá abstraktní třída SlozeneZapojeni? Kromě jednoduchých obvodových prvků nám zbývají dva složené obvody – paralelní a sériové zapojení Stejně jako jednoduché obvodové prvky rozšiřují vlastnosti třídy AbstraktniObvod, nicméně se liší v implementaci výpočtu hodnoty odporu, tj. metody vratOdpor. Společná je ovšem podpora více obvodových prvků zapojených dohromady Nabízí se tedy možnost vytvoření zděděné abstraktní třídy, která přidá podporu více obvodových prvků, ale nebude řešit konkrétní výpočet odporu vratOdpor, který se pro paralelní a sériové zapojení liší Pro implementaci použijeme třeba ArrayList<typ> Mgr. David Vít, K336, © 2007
9. Abstraktní třída SlozeneZapojeni import java.util.* ; public abstract class SlozeneZapojeni extends AbstraktniObvod { // seznam prvku (novy rys Java 5 – urceni typu v kolekci) protected final ArrayList<AbstraktniObvod> kombinace = new ArrayList<AbstraktniObvod> () ; public SlozeneZapojeni(AbstraktniObvod [] prvky) { super("zapojeni") ; for (AbstraktniObvod ao : prvky) { // for each ukazka.. Java 5 kombinace.add(ao); } // rozsirujeme o vypis vsech prvku v zapojeni public String informace() { // pouzijeme zdedenou implementaci String inf = super.informace() + " ("; for (AbstraktniObvod ao : kombinace) { inf += "\n" + ao.informace() + " "; inf += "\n)" ; return inf ; Mgr. David Vít, K336, © 2007
10. Třída SerioveZapojeni Nyní uděláme jednoduchou implementaci metody vratOdpor a máme naši První Implementaci složeného zapojení. Instance této třídy již půjde vytvořit. public class SerioveZapojeni extends SlozeneZapojeni { public SerioveZapojeni(AbstraktniObvod [] prvky) { super (prvky) ; } // a nyni uz mame implementaci, pujde vytvorit instanci public double vratOdpor () { // odpor je souctem odporu jednotlivych prvku double odpor = 0.0 ; for (AbstraktniObvod ao : kombinace) { odpor += ao.vratOdpor(); // neni to elegantni? return odpor ; // jednoduchost objektoveho navrhu … public String informace() { return "seriove " + super.informace() ; Mgr. David Vít, K336, © 2007
11. Finální aplikace Domácí úloha: analogicky dokončete implementaci třídy ParalelniZapojeni, a zkuste sestavit 3 složitější obvody. public class Main { // nakreslete si tento obvod na papir public static void main(String[] args) { AbstraktniObvod r1 = new Rezistor ("R1", 1000.0) ; AbstraktniObvod r2 = new Rezistor ("R2", 2000.0) ; AbstraktniObvod r3 = new Rezistor ("R3", 3000.0) ; AbstraktniObvod r4 = new Rezistor ("R4", 500.0) ; AbstraktniObvod [] kombinace1 = { r1, r2 } ; AbstraktniObvod [] kombinace2 = { r2, r4, r1 } ; AbstraktniObvod obvod1 = new ParalelniZapojeni (kombinace1) ; AbstraktniObvod obvod2 = new SerioveZapojeni (kombinace2) ; AbstraktniObvod [] kombinace3 = { obvod1, obvod2, r3, new Zkrat () } ; AbstraktniObvod obvod = new ParalelniZapojeni (kombinace3); System.out.println(obvod.informace() + " odpor " + obvod.vratOdpor() + " Ohmu"); for (int napeti = -5 ; napeti <= 5 ; napeti++) { System.out.println("Proud pri napeti " + napeti + " V = " + String.format("%f", obvod.vratssProud(napeti)) + " A") ; } Mgr. David Vít, K336, © 2007
Zpracování výjimek I. Mechanizmus zpracování chybových stavů (ClassCastException, NumberFormatException, FileNotFoundException, ….) Při chybě se zavolá throw new NejakaVyjimka () Pokud metoda nechce zpracovat sama výjimku, tak musí deklarovat v hlavičce throws NejakaVyjimka, tím má odpovědnost za zpracování chyvového stavu volající funkce Výjimka se zpracovává pomocí bloku try-catch-finally. Alespoň jedno z catch, finally je povinné, pokud použiji try Blok catch chytne výjimku, zde ji mohu úplně zpracovat nebo po zpracování dále předat volající funkci (nutno užít throws a získanou instanci výjimky) Doporučená technika – píši složitější aplikaci, kde se mi objevuje více různých výjimek (RMI, TCP, UDP, JDBC, …), abych všechny výjimky nepředával dál, udělám try-catch blok a vyhodím nějakou vlastní definovanou výjimku, kterou pak zpracovávám ve své aplikaci Výjimka umožňuje signalizovat chybu v konstruktoru. Mgr. David Vít, K336, © 2007
Zpracování výjimek II. Uživatelská výjimka může nést libovolnou dodatečnou informaci, kterou chci předat volajícím funkcím. Zpravidla přenáší chybové zprávy „vnitřních“ výjimek, které převádím do nové uživatelské. public class MojeVyjimka extends java.lang.Exception { public MojeVyjimka(String msg) { super(msg); System.out.println("Moje vyjimka " + this.getClass().getName() + ": " + this.getMessage()) ; } Domácí úloha: vytvořte uživatelskou výjimku ZapornyOdporVyjimka, která je vyvolána v okamžiku, kdy je do konstruktoru třídy Rezistor zadána záporná hodnota. Blok finally { … } je velmi důležitý při práci se soubory, databázemi či síťové komunikaci. Je proveden při korektním opuštění bloku try i při vyvolání výjimky. Užívá se k zavírání systémových zdrojů, aby se vždy korektně uvolňovaly systémové prostředky. Mgr. David Vít, K336, © 2007
Tovární metody Velmi oblíbená technika v Javě, zpravidla se vytváří třída pouze se statickými metodami, která se nazývá „továrna“ Použití statické metody třídy namísto konstruktoru k jednoduššímu vytváření instancí tříd v knihovnách. public class TovarnaNaObvody { public static Rezistor novyRezistor(String nazev, double odpor) { return new Rezistor (nazev, odpor); } Domácí úloha: doplňte třídu TovarnaNaObvody a udělejte alternativní verzi metody main z Vaší úlohy pouze s použitím továrních metod. Nepovinná domácí úloha: doplňte třídy ProudovyZdroj a NapetovyZdroj a třídu Obvod, která bude obsahovat jeden ze zdrojů připojený k obvodu z naší úlohy a má metodu dopocti, která dle zapojení zdroje dopočte buď hodnotu proudu nebo napětí. Mgr. David Vít, K336, © 2007