SW_03 Implementace a testování
Implementace • Cílem implementace je doplnit navrženou architekturu (kostru) aplikace o programový kód a vytvořit tak kompletní systém. • Implementační model specifikuje jak jsou jednotlivé elementy (objekty a třídy) vytvořené v etapě návrhu implementovány ve smyslu softwarových komponent, kterými jsou zdrojové kódy, spustitelné kódy, data a podobně. 2
Implementace • Softwarová komponenta je definována jako fyzicky existující a zaměnitelná část systému vyhovující požadované množině rozhraní a poskytující jejich realizaci.
3
SW komponenty • Podle typu softwarových komponent hovoříme o zdrojovém kódu - částech systému zapsaném v programovacím jazyce, binárním kódu - (přeloženém do strojové kódu procesoru) a spustitelném kódu, ostatních částech reprezentovaných databázovými tabulkami, dokumenty apod.
4
SW komponenty • Jestliže jsme ve fázi analýzy a návrhu pracovali pouze s abstrakcemi dokumentovanými v podobě jednotlivých diagramů, pak v průběhu implementace dochází k jejich fyzické realizaci. • Implementační model se tedy také zaměřuje na specifikaci toho, jak budou tyto komponenty fyzicky organizovány podle implementačního prostředí a programovacího jazyka poskytujícího konkrétní mechanizmus strukturování a modularizace. 5
SW komponenty • Ke splnění těchto cílů nabízí jazyk UML prostředky, kterými jsou v tomto případě tyto dva následující diagramy: • Diagram komponent ilustrující organizaci a závislosti mezi softwarovými komponentami. • Diagram nasazení upřesněný nejen ve smyslu konfigurace technických prostředků, ale především z hlediska rozmístění implementovaných softwarových komponent na těchto prostředcích. 6
Mapování elementů logického modelu na komponenty • Důsledná a přesná specifikace objektů a jejich tříd v etapě návrhu umožňuje automatické generování zdrojových kódů dle následující tabulky. • Tabulka má dva sloupce, – první z nich odpovídá elementům jazyka UML, – druhý popisuje jejich zobrazení v programovacím jazyce (Java). 7
Tabulka mapování
8
Mapování elementů logického modelu na komponenty • Jediné co nelze přímo odvodit z diagramů UML jsou těla metod, kód který je proveden v odezvě na přijatou zprávu a také jaká je fyzická struktura vytvářených souborů reprezentujících softwarové komponenty. • Vše ostatní lze automaticky vygenerovat doslova bez účasti týmu programátorů. • Pro názornost, z následujícího diagramu tříd se pokusíme vytvořit požadované softwarové komponenty . 9
Diagram tříd
10
Zdrojové, binární a spustitelné komponenty • Ukázka vytvoření komponent se zahájí částí týkající se zdrojových komponent, tedy souborů vytvořených pomocí použitého programovacího jazyka. • První krok bude rozhodnout o fyzické organizaci těchto zdrojových souborů definovaných pomocí diagramu komponent.
11
Diagram komponent- zdrojový kód
12
Příklad • Z výsledného diagramu tříd specifikující jádro aplikace prodeje automobilu vytvořme diagram komponent. • Třídy Automobil, Objednávka, Sklad, Vybraný a Specifikovaný budou každá obsažena v samostatném souboru totožného názvu s koncovkou .java označující, že se jedná o kód zapsaný v programovacím jazyce Java. • Hierarchii tříd popisující část doplňků (s využitím návrhového vzoru Kompozit) umístíme do jediného souboru odpovídající svým názvem supertřídě Doplněk.
13
Příklad • Relace závislosti nazvané jako <
> označují použití komponent z jiných subsystémů (balíčků), zatímco relace nazvané jako <> označují, že dané komponenty spadají do společného balíčku. • V jazyce Java to znamená, že se nacházejí jejich soubory ve společném adresáři.
14
Příklad • Pokud máme k dispozici vývojové nástroje typu CASE (Computer Aided Software Engineering), pak lze nechat tyto zdrojové kódy takovým nástrojem vygenerovat. • Postup vytváření zdrojových kódů vychází z tabulky mapování elementů jazyka UML na zdrojový kód jazyka Java. • Jediné co je nutné doplnit, co nelze automaticky či jednoduše vytvořit, jsou těla metod reprezentované algoritmy operací. 15
Příklad • Tyto lze alespoň hrubým způsobem specifikovat jednak ze sekvenčních diagramů definujících operace, ale především pak ze stavových diagramů popisujících detailně chování objektů.
16
Zdrojový kód Objednávka
17
Implementace tříd • Třída Objednávka využívá tříd Automobil a Sklad umístěných v balíčcích auta a sklad. • Proto dle specifikace v diagramu komponent je nutné tyto třídy importovat. • Atributy zákazník a cena jsou implementovány v podobě instančních proměnných stejně jako asociace na instance jiných tříd pojmenované jako specifikovaný, vybraný (jména rolí instance třídy Automobil) a sklad. • Metody, jak vyplývá z diagramu tříd, jsou dvě vyplň a vlož. 18
19
Implementace tříd • Za pozornost v této části implementace stojí způsob, jakým jazyk Java realizuje rozhraní. • K tomuto účelu slouží klíčové slovo implements, které v definici třídy specifikuje, která rozhraní (v našem případě Vybraný s Specifikovaný) jsou v rámci dané třídy implementována. • Z hlediska samotné třídy platí, že musí definovat metody deklarované v rámci rozhraní, tedy specifikace z rozhraní Specifikovaný a jeRezervován a rezervuj z rozhraní Vybraný. • V případě chybějících implementací těchto metod překladač jazyka Java bude hlásit chybu. 20
21
Implementace tříd • Poslední z příkladů implementace zdrojových komponent dokumentuje způsob jakým je v jazyce Java řešena relace generalizace (dědičnost). • Jedná se použití klíčového slova extends, které uvozuje supertřídu dané třídy. • Tedy např. třída Výbava je podtřídou třídy Doplněk. 22
Komponenty • Binární komponenty (komponenty přeložené do strojového kódu daného procesoru) mají také svůj diagram komponent obvykle vyjadřující z jakých zdrojových souborů jsou požadované binární vytvořeny.
23
Binární komponenty 24
Diagram komponent: běh programu
25
Příklad • V našem příkladu ke spuštění třídy Databáze aut potřebujeme jednak virtuální stroj jazyka Java (java.exe) a jeho knihovny (rt.jar). • Konfigurační parametry fontů jsou uloženy v textovém souboru (font.properties). • Samotná aplikace využívá služeb databázového serveru (sql.exe) pracujícího s databází aut strukturovanou v relačních tabulkách (Automobily). 26
Implementace • Posledním úkolem, který činí etapu implementace kompletní je zpřesnění diagramů nasazení. • Vlastní zpřesnění spočívá v umístění spustitelných a datových komponent na jednotlivé technické prostředky reprezentované počítači.
27
28
Testování a nasazení SW produktu • Testování se provádí z pohledu tří základních dimenzí reprezentovaných kvalitou, funkcionalitou a výkonností systému. • Testování se týká všech vytvářených modelů a jejich diagramů.
29
Testování • Principy používané při testování jsou následující: – Testy se plánují pro každou iteraci a zahrnují integrační testy a testy systémové. – Integrační testy se provádí pro každý vytvořený produkt během iterace, zatímco systémový test se provádí pouze na konci iterace, kdy vzniká spustitelná verze vyvíjeného produktu.
30
Testování – Testy se navrhují a následně implementují v podobě testovacích úloh, které jednoznačně definují co se má ověřit. – Hovoříme o testovacích procedurách, které specifikují jak se má test provádět, nebo se vytváří spustitelné testovací komponenty umožňující automatizaci procesu ověřování.
31
Testování – Výsledky provedených testů jsou systematicky zpracovávány a vadné části jsou opakovaně testovány a případně zaslány zpět do toků činností jako je analýza, návrh nebo implementace s cílem nedostatky odstranit.
32
Cíle procesu verifikace a validace • Verifikace je proces testování hledající odpověď na otázku, zda-li softwarový produkt je vytvářen správně. Jinak řečeno, hledáme nedostatky v samotném softwarovém systému. • Validace je proces testování hledající odpověď na otázku, zda-li vytvářený software je správný. Jinými slovy, zda-li implementuje požadovanou funkcionalitu. 33
Verifikace a validace • Z uvedeného vyplývá, že může nastat situace, kde systém je perfektně verifikován, ale je k ničemu, neboť nesplňuje zadavatelem specifikovanou funkcionalitu. • Na druhou stranu systém splňuje všechny funkce, ale je nestabilní nebo pomalý.
34
Verifikace a validace • Kromě verifikace a validace také rozlišujeme mezi statickým a dynamickým ověřováním: – Statické techniky se zabývající testováním vytvořených modelů a jejich diagramů, čili umožňují pouze ověřit korespondenci mezi vytvořeným systémem a jeho specifikací.
35
Verifikace a validace – Dynamické techniky zkouší již implementovaný produkt cestou jeho spuštění a tím ověřují i jeho užitečnost a kvalitu.
36
Modely testování • Na rozdíl od předchozích modelů, které byly definovány různými typy diagramů, pro testování nemá jazyk UML k dispozici žádný diagram. Model testování je dán souborem: – testovacích úloh definujících co se má u systému testovat, – testovacích procedur specifikujících postupy pro provedení testovacích úloh, – testovacích komponent, které automatizují testovací procedury.
37
Testovací úloha • Testovací úloha specifikuje způsob testování systému zahrnující informace o tom, co se má testovat s jakými vstupy a za jakých podmínek.
38
Testovací úloha – funkční test • V podstatě existují dva typy nejběžněji používaných testovacích úloh: 1. Testovací úlohy určené k ověření případů užití nebo jejich specifických scénářů. – Takové úlohy ověřují výsledky interakce aktéra se systémem. Tento tzv. black-box test ověřuje z vnějšku pozorovatelné chování systému.
39
Testovací úloha – konstrukční test 2. Testovací úlohy specifikující jak ověřovat realizaci případů užití. – Tyto úlohy ověřují interakce mezi komponentami implementující případ užití. Tento tzv. white-box test je určen k ověření vnitřní interakcí daného systému.
40
Testovací procedura • Testovací procedura specifikuje jak provést jednu nebo více testovacích úloh nebo jejich částí. • Testovací procedury lze specifikovat pomocí: – instrukcí popisujícími jak ručně provést danou testovací úlohu, – popisu jak vytvořit spustitelnou testovací komponentu, – specifikace jak použít nástroj pro automatické testování. 41
Testovací komponenty • Testovací komponenty automatizují jednu nebo více testovacích procedur nebo jejich části. • Testovací komponenty jsou používány k ověření komponent tvořících implementační model cestou poskytnutí požadovaných vstupů, řízení a sledování běhu ověřované komponenty a vytvoření výsledné zprávy o průběhu testu. • K vytvoření testovacích komponent se mohou používat různé skriptovací jazyky nebo k tomuto účelu vytvořené systémy automatizované verifikace. 42
JUnit přiklad na použití
Zadání příkladu • Převod mezi římskými a arabskými číslicemi. • Platí následující pravidla: – hodnoty písmen se až na výjimky sčítají (I, II, VIII) – Písmena vyjadřující mocniny se mohou opakovat max. 3. – Písmena vyjadřující 5, 50, 500 se neopakují. – Písmena římského čísla se vyjadřují od největšího po nejmenší, záleží na pořadí.
Zadání příkladu – Pro každou hodnotu existuje pouze jeden správný zápis římskými číslicemi. – Pomocí římských číslic lze zapsat čísla v rozsahu 1 – 3999.
Vytvoření kostry třídy • Třída Roman bude obsahovat dvě statické metody pro převod toRoman(int) a fromRoman(String). – bude-li vstupní parametr toRoman(int) mimo rozsah, vyvolá se výjimka IllegalArgumentException. – bude-li argument metody fromRoman(String) obsahovat nepřípustný znak, vyvolá se výjimka NumberFormatException.
public class Roman { public static int fromRoman(String s) throws NumberFormatException { int vysl = 0; return vysl; } public static String toRoman(int i) throws IllegalArgumentException { String vysl= ""; return vysl; } }
47
public class RomanTest extends TestCase{ public RomanTest(String name) { super(name); }
// musí specifikovat konstruktor
protected void setUp() { // počáteční nastavení } protected void tearDown() { // úklid po testování } // testy postupně doplnované . . .
// obyčejně deklarovaná statická metoda suite // v jejímž rámci se vytvoří seznam testů public static Test suite() { return new TestSuite(RomanTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } }
48
// Pro vytvoření seznamu testů se většinou používá příkaz: return new TestSuite(RomanTest.class); //do seznamu testů se zahrnou všechny metody začínající řetězcem „test“ // preferovaný způsob
TestSuite seznamTestu = new TestSuite(); seznamTestu.addTest(new RomanTest(“testToRomanKnownValues”); seznamTestu.addTest(new RomanTest(“testFromRomanKnownValues”); ... return seznamTestu;
49
Testy správnosti • nadefinujeme dva testy: testToRomanKnownValues() a testFromRomanKnownValues()
class Dvojice { int arab; String roman; Dvojice(int arab, String roman) { this.arab = arab; this.roman = roman; } }; Dvojice knownValues[] = { new Dvojice(1,"I"), new Dvojice(2,"II"), new Dvojice(3,"III"), new Dvojice(4,"IV"), new Dvojice(5,"V"), new Dvojice(6,"VI"), new Dvojice(7,"VII"), new Dvojice(8,"VIII"), new Dvojice(9,"IX"), new Dvojice(10,"X"), new Dvojice(50,"L"), new Dvojice(100,"C"), new Dvojice(500,"D"), new Dvojice(1000,"M"), new Dvojice(31,"XXXI"), new Dvojice(148,"CXLVIII"), new Dvojice(3940,"MMMCMXL"), new Dvojice(3999,"MMMCMXCIX"), }; 51
public void testToRomanKnownValues() { for(int i=0; i
52
Testy výjimek • Testy výjimek otestují, zda se v případě chybných vstupních parametrů bude zahlášena chyba metody. • Odchytávání výjimek se provádí v kódu testu, v případě, že se výjimka neobjeví, ohlásí se chyba pomocí metody JUnit fail(String textChyby)
public void testToRomanException() { int badValues[] = {0, -1, 4000}; for(int i=0; i
54
public void testSanity() { for(int i= 1; i< 4000; i++){ int vysl = Roman.fromRoman(Roman.toRoman(i)); assertEquals(i, vysl); } }
55
Vlastní třída a testování • Do třídy Roman metody toRoman() - doplnit test intervalu hodnot public static String toRoman(int i) throws IllegalArgumentException { if((i>3999) || (i<= 0)) throw new IllegalArgumentException(); else { return vysl; } }
Vlastní třída a testování • Dále je třeba doplnit třídu Dvojice a tabulku pro převod a doplnit odpovídajícím způsobem metodu toRoman() • Výpis třídy Roman • Výpis třídy RomanTest
public class Roman { public static int fromRoman(String s) throws NumberFormatException { int vysl = 0; return vysl; } public static String toRoman(int i) throws IllegalArgumentException { if((i>3999) || (i<= 0)) throw new IllegalArgumentException(); else { int cislo =i; String vysl= ""; while(cislo >0) { for(int j=0; j< tabulka.length; j++){ if(cislo >= tabulka[j].arab) { vysl= vysl+ tabulka[j].roman; cislo= cislo- tabulka[j].arab; break; } } } return vysl; } } static class Dvojice { int arab; String roman; Dvojice(int arab, String roman){ this.arab = arab; this.roman = roman; } } 58
static Dvojice tabulka[] = { new Dvojice (1000, "M"), new Dvojice (900, "CM"), new Dvojice (500, "D"), new Dvojice (400, "CD"), new Dvojice (100, "C"), new Dvojice (90, "XC"), new Dvojice (50, "L"), new Dvojice (40, "XL"), new Dvojice (10, "X"), new Dvojice (9, "IX"), new Dvojice (5, "V"), new Dvojice (4, "IV"), new Dvojice (1, "I"), }; }
59
public class RomanTest extends TestCase{ class Dvojice { int arab; String roman; Dvojice(int arab, String roman) { this.arab = arab; this.roman = roman; } }; Dvojice knownValues[] = { new Dvojice(1,"I"), new Dvojice(2,"II"), new Dvojice(3,"III"), new Dvojice(4,"IV"), new Dvojice(5,"V"), new Dvojice(6,"VI"), new Dvojice(7,"VII"), new Dvojice(8,"VIII"), new Dvojice(9,"IX"), new Dvojice(10,"X"), new Dvojice(50,"L"), new Dvojice(100,"C"), new Dvojice(500,"D"), new Dvojice(1000,"M"), new Dvojice(31,"XXXI"), new Dvojice(148,"CXLVIII"), new Dvojice(3940,"MMMCMXL"), new Dvojice(3999,"MMMCMXCIX"), }; 60
public RomanTest(String name) { super(name); } protected void setUp() { } protected void tearDown() { } // testy postupne doplnovane public void testToRomanKnownValues() { for(int i=0; i
61
// test vyjimky public void testToRomanException() { int badValues[] = {0, -1, 4000}; for(int i=0; i
62
public void testTooManyRepeatedNumerals() { String badValues[] = {"MMMM", "VV", "LL", "CCCC", "DD", "IIII"}; for(int i=0; i< badValues.length; i++) { try { int vysl= Roman.fromRoman(badValues[i]); }catch (NumberFormatException e) { }fail("Expected IllegalArgument for: "+ badValues[i]); }; } public void testRepeatedPairs() { String badValues[] = {"CMCM", "CDCD", "IVIV", "IXIX", "XLXL"}; for(int i=0; i< badValues.length; i++){ try { int vysl= Roman.fromRoman(badValues[i]); }catch (NumberFormatException e){ continue; }fail("Expected IllegalArgumentException for: "+ badValues[i]); } }
63
public void testMalformedAntecedent() { String badValues[] = {"IIMMCC", "VX", "DCM", "CMM", "CMD", "IXIV", "MCMC", "XCX", "IVI", "LM", "LD", "LC"}; int vysl; for(int i= 0; i< badValues.length; i++ ){ try { vysl= Roman.fromRoman(badValues[i]); }catch(NumberFormatException e){ continue; }fail("Expected IllegalArgumentException for: "+ badValues[i]+ " (" + vysl); }; } public void testSanity() { for(int i= 0; i< 4000; i++){ int vysl = Roman.fromRoman(Roman.toRoman(i)); assertEquals(i, vysl); } }
64
// zaklad public static Test suite() { return new TestSuite(RomanTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } }
65
Typování • Abstrakce představuje nejdůležitější princip pro opětovnou použitelnost. • Jedná se o nahrazení konkrétní datové entity, abstraktnější datovou entitou. • Uvedená abstrakce nabývá většinou jedné ze dvou následujících forem: – generalizace – typování
Generalizace • Generalizací se rozumí zobecnění specializované datové entity do obecnější datové entity; mezi těmito entitami se stanoví relace generalizace. • Specifické vlastnosti relace generalizace: – existuje jen mezi datovými entitami (třídami), nepřenáší se na instance, – způsobuje dědění datových atributů a relací z obecnější do specializovanější datové entity, – každá instance od obecnější entity dědí pouze strukturu datových atributů, nikoli však obsah,
Generalizace – je to relace typu nebo – existuje-li k jedné obecnější datové entitě více specializovanějších entit, daná instance se vytvoří právě k jedné z nich.
Diagramy Osoba a Lokalita
• Osoba atributy: jméno, rok narození • Muž atributy: počet manželek (zdědí jméno, rok narození) • Žena atributy: počet narozených dětí (zdědí jméno, rok narození)
Typování • Při typování se místo obecnější datové entity definuje datová entita, která reprezentuje typ této datové entity. • Tím, že se při typování obecnější datová entita specializuje ne datovou entitou jako při generalizaci, ale instancemi typové datové entity, vytvořená instance dědí nejen strukturu datových atributů obecnější datové entity, ale zároveň i jejich obsah. • Uvedené datové entity jsou spojeny asociací. • První příklad – typování osob, druhý příklad – typování lokalit.
Typování osob a lokalit
• TypOsoby jsou instance Muž (jméno, rok narození, počet manželek) a Žena (jméno, rok narození, počet narozených dětí) a instance od třídy Osoba pak patří buď pod Muže nebo Ženu. • Hodnoty datových atributů zůstávají stejné pro všechny specializované instance.
Objednávka / Produkt (lepší příklad) • ProductType (Objednávka) obsahuje všechny plánované položky (plánovaná váha, plánované složení příměsí). • Instance od ProductType pak představují různé objednávky lišící se různou hodnotou plánovaných položek. • Instance od třídy Product jsou vždy svázány s konkrétní instancí třídy ProductType a liší se v hodnotách skutečných položek (skutečná váha, skutečné složení příměsí).
Typování & generalizace 1/2 • Typování je podobné generalizaci. V obou případech je využit princip obecnější datové entity. • Při generalizaci se tato obecnější datová entita specializuje pomocí konkrétnějších datových entit. • Při typování se obecnější datová entita specializuje instancemi typové datové entity. • Při typování tak instance typové datové entity odpovídají konkrétnějším datovým entitám u generalizace.
Typování & generalizace 2/2 • Typování se používá v případě, že seznam typů se mění a je malá důležitost dědění. • Generalizace se používá v případě, že seznam specializovanějších entit statický a je větší důležitost dědění. • Při generalizaci instance specializovanější datové entity dědí strukturu datových atributů od obecnější datové entity. • Při typování specializovanější instance dědí od obecnější instance nejen strukturu datových atributů, ale zároveň i jejich obsah.
Typování • Datové entity typ se vzájemnými relacemi patří do meta úrovně (směrnice, obecné zákony). • Standardní entity vyjadřují operační úroveň (každodenní skutečnost). • Mezi operativní úrovní a metaúrovní existuje vzájemný vztah – operativní úroveň zachycuje konkrétní případy, které musí (měly by být) splňovat pravidla předepsaná metaúrovní.
Stratifikované typování • Instance od třídy TypUniversity jsou např. VŠB-TU, SU nebo OU. Ty mají konkrétní různé hodnoty jmen rektorů. • V rámci instancí VŠB-TU se pak vytváří instance EkF, nebo FEI se svými děkany a společným rektorem. Pro SU a OU je to podobné. • V rámci instancí fakult se vytváří konkrétní instance kateder např. katedra matematických metod, informatiky v ekonomii pro EkF nebo informatiky a matematiky pro FEI.
Relace mezi pracovními kategoriemi a pracovními vztahy
• Generalizace – nepružné řešení • Typování – vhodné řešení
Flexibilní reprezentace organizační struktury
• Podobné řešení platí i pro organizační strukturu firem. • Výsledné řešení odpovídá vzorům z článku o politikách
public class Objednavka { private int planCas; private double planMnozstvi; private Koruny planCena; public Objednavka(int cas, double mnozstvi, Koruny castka) { planCas = cas; planMnozstvi = mnozstvi; planCena = castka; } public int getPlanCas() { return planCas; } public double getPlanMnozstvi() { return planMnozstvi; } public Koruny getPlanCena() { return planCena; } public String toString() { return String.format("Planovany cas: %d planovane mnozstvi: %.2f planovana cena: %s", getPlanCas(), getPlanMnozstvi(), getPlanCena().toString()); } public void tisk() { System.out.println(this.toString()); }
79
public class Skutecnost { private int skutCas; private double skutMnozstvi; private Koruny skutCena; public Skutecnost(int cas, double mnozstvi, Koruny cena){ skutCas = cas; skutMnozstvi = mnozstvi; skutCena = cena; // skutCena = new Koruny(cena); } public int getSkutCas() { return skutCas; } public double getSkutMnozstvi() { return skutMnozstvi; } public Koruny getSkutCena() { return skutCena; } public String toString() { return String.format("%s \nskutecny cas: %d skutecne mnozstvi: %.2f " + "skutecna cena: %s", Objednavka.this.toString(), getSkutCas(), getSkutMnozstvi(), getSkutCena().toString()); } public int rozdilCas() { return Objednavka.this.getPlanCas() - getSkutCas(); }
Objednávka
80
public double rozdilMnozstvi() { return Objednavka.this.getPlanMnozstvi() - getSkutMnozstvi(); } public Koruny rozdilCena() { return Objednavka.this.getPlanCena().minus(getSkutCena()); } public void tisk() { System.out.println("Plan: " + Objednavka.this.toString() + "\nSkutecnost: "+ this.toString()); } public void tiskRozdilu(){ System.out.println("Rozdil cas: " +rozdilCas() + "\nrozdil mnozstvi: " + rozdilMnozstvi() + "\nrozdil cena: " + rozdilCena()); } } public Skutecnost getSkutecnost(int cas, double mnozstvi, Koruny cena){ return new Skutecnost(cas, mnozstvi, cena); } }
81
public class ObjednavlaTest { public static void main(String[] args) { Objednavka objednavka = new Objednavka(12, 52.36, new Koruny(53.21)); Objednavka.Skutecnost sk1 = objednavka.getSkutecnost(13, 49.83, new Koruny(55.20)); objednavka.tisk(); sk1.tisk(); sk1.tiskRozdilu(); } }
82