Modularita. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

Podobné dokumenty
Základy objektové orientace I. Únor 2010

Teoretické minimum z PJV

OMO. 3 - Klíčové koncepty modelování systémů II

Abstraktní datové typy: zásobník

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Úvod do programovacích jazyků (Java)

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Generické programování

typová konverze typová inference

OMO. 4 - Creational design patterns A. Singleton Simple Factory Factory Method Abstract Factory Prototype Builder IoC

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

8 Třídy, objekty, metody, předávání argumentů metod

IRAE 07/08 Přednáška č. 1

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

KTE / ZPE Informační technologie

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

public class Karel { private int position; public boolean issmiling; public int getposition() { return position;

5 Rekurze a zásobník. Rekurzivní volání metody

1 Nejkratší cesta grafem

1. Programování proti rozhraní

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

Principy objektově orientovaného programování

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

PB161 Programování v jazyce C++ Přednáška 7

PB161 Programování v jazyce C++ Přednáška 7

Čipové karty Lekařská informatika

Typický prvek kolekce pro české řazení

Objektově orientované programování

Jazyk C# (seminář 3)

Dědění, polymorfismus

Java a XML. 10/26/09 1/7 Java a XML

Správa paměti. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

Datové typy v Javě. Tomáš Pitner, upravil Marek Šabo

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Infrastruktura UML. Modelování struktury v UML. Superstruktura UML. Notace objektů. Diagramy objektů

Vytváření a použití knihoven tříd

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Využití OOP v praxi -- Knihovna PHP -- Interval.cz

Příklad aplikace Klient/Server s Boss/Worker modelem (informativní)

PŘETĚŽOVÁNÍ OPERÁTORŮ

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Statické proměnné a metody. Tomáš Pitner, upravil Marek Šabo

Návrhové vzory OMO, LS 2014/2015

Abstraktní datové typy

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue.

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám.

Abstraktní třída a rozhraní

Softwarové komponenty a Internet

Iterator & for cyklus

Java - řazení objektů

Neměnné objekty. Tomáš Pitner, upravil Marek Šabo

Code Contracts. Robert Haken [MVP ASP.NET, MCT] Software architect, Owner at HAVIT, s.r.o. knowledge-base.havit.cz

PREPROCESOR POKRAČOVÁNÍ

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

20. Projekt Domácí mediotéka

Výčtový typ strana 67

Algoritmizace a programování

Příklad : String txt1 = new String( Ahoj vsichni! ); //vytvoří instanci třídy String a přiřadí ji vnitřní hodnotu Ahoj vsichni!

Datové struktury. alg12 1

George J. Klir. State University of New York (SUNY) Binghamton, New York 13902, USA

UJO Framework. revoluční architektura beans. verze

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

Fronta (Queue) Úvod do programování. Fronta implementace. Fronta implementace pomocí pole 1/4. Fronta implementace pomocí pole 3/4

Třídy, polymorfismus. A0B36PR2-Programování 2 Fakulta elektrotechnická České vysoké učení technické

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Jazyk C# (seminář 6)

Programování v C++ 2, 4. cvičení

1. Téma 12 - Textové soubory a výjimky

Úvod do programování - Java. Cvičení č.4

Tvorba informačních systémů

Seminář Java IV p.1/38

Java - výjimky. private void vstup() throws IOException {... }

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Seminář Java II p.1/43

Obsah přednášky. 12. Dokumentace zdrojového kódu Tvorba elektronické dokumentace UML. Co je diagram tříd. Ing. Ondřej Guth

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Lambda funkce Novinky v interfaces Streamy Optional - aneb zbavujeme se null. Java 8. Ondřej Hrstka

Stream API. Petr Krajča. Základy programovaní 4 (Java) Katedra informatiky Univerzita Palackého v Olomouci

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

Programování v C++ 3, 3. cvičení

Konstruktory a destruktory

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

Více o konstruktorech a destruktorech

PODOBÁ SE JAZYKU C S NĚKTERÝMI OMEZENÍMI GLOBÁLNÍ PROMĚNNÉ. NSWI162: Sémantika programů 2

DTP Základy programování Úvod do předmětu

IRAE 07/08 Přednáška č. 2. atr1 atr2. atr1 atr2 -33

Dalším příkladem může být například výstup dat na různá zařízení, souborů, grafických rozhraní, sítě atd.

Část I Spojové struktury

Základní datové struktury

Problém, jehož různé instance je třeba často řešit Tyto instance lze vyjádřit větami v jednoduchém jazyce

Návrhové vzory Design Patterns

3. Je defenzivní programování technikou skrývání implementace? Vyberte jednu z nabízených možností: Pravda Nepravda

Programování II. Modularita 2017/18

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Transkript:

Modularita Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016 Objektové modelování, xxb36omo 10/2016, Modularita https://cw.fel.cvut.cz/wiki/courses/xxb36omo/start xxb36omo, 2016, Modularita, 1/41

Modularita Větší program nelze zpravidla realizovat jako monolitický kus kódu. Bylo by to příliš nákladné a často by se opakovalo řešení stejných podproblémů. Šikovnější je složit řešení z komponent modulů. Moduly mohou být připraveny předem, nebo být součástí řešení. Moduly spolu komunikují (jinak by nemělo smysl je do řešení zahrnovat). Na druhé straně by jednotlivé moduly měly být na sobě co nejméně závislé - závislost pouze pomocí rozhraní low coupling (nízká provázanost). Samotný modul by měl naopak zahrnovat vše potřebné pro realizaci služeb high cohesion (vysoká soudržnost). xxb36omo, 2016, Modularita, 2/41

Umožňuje znovupoužívání kódu. Modularita Lze ji vnímat v různých rovinách abstrakce (metody, třídy, balíčky). Pokud je navržený program modulární, pak je obvykle dobře spravovatelný. Umožňuje zaměnitelnost jednotlivých modulů. Výhodou je, že můžeme mít jednu architekturu, která pouhou výměnou jednotlivých modulů může plnit jinou funkci. Zpřehledňuje a snižuje množství kódu. Závislost mezi moduly je dána počtem použitých rozhraní, jejich složitostí a složitostí informace přenášené přes rozhraní. cmp Components cmp Prov ázané komponenty Modul1 Modul2 Modul1 Modul2 požadované rozhraní poskytované rozhraní xxb36omo, 2016, Modularita, 3/41

Deméteřin zákon (Law of Demeter) Pravidlo, které nám říká následující: Každá jednotka by měla mít jen omezené znalosti o dalších jednotkách: pouze jednotky úzce spjaté s touto jednotkou. Každá jednotka by měla mluvit pouze se svými přáteli a nemluvit s cizími jednotkami. Mluvit jen s bezprostředními přáteli. Minimalizuje provázanost jednotlivých modulů. bohyně plodnosti (Demeter = matka) cmp Bankov ní systém Bankov ní systém Internetov é bankov nictv í Obsluha přepážky xxb36omo, 2016, Modularita, 4/41

Aplikace Deméteřina zákona na OOP Z hlediska OOP můžeme toto pravidlo aplikovat pomocí následujících 5-ti pravidel s jakými objekty může pracovat metoda M zaslaná objektu O: s touto instancí (this), s argumenty metody M, s objekty, které metoda vytvoří, s komponentami, které jsou součástí O, s globálními objekty, které jsou metodě v rámci O přístupné. xxb36omo, 2016, Modularita, 5/41

Příklad: Práce s peněženkou class Penezenka { private double obsah; public double getobsah() { return obsah; }; public void zmenobsah(double kolik) { obsah += kolik; }; public Penezenka(double pocatek) { obsah = pocatek; } }; cmp Penezenka Penezenka - obsah: double + Penezenka(double) + getobsah(): double + zmenobsah(double): boolean xxb36omo, 2016, Modularita, 6/41

Příklad: Práce s objektem samotným (this) class Finance { double penize; private Penezenka mojepenezenka; boolean platpenezenkou(double platba) { if (this.mojepenezenka.getobsah() < platba) return false; this.mojepenezenka.zmenobsah(-platba); return true; }; public void platbahotovost(double kolik) { if (platpenezenkou(kolik)) System.out.println("OK"); else System.out.println("Chyba, platbu nelze provest"); }; }; xxb36omo, 2016, Modularita, 7/41

Příklad: Práce s argumenty metody class Finance { private Penezenka mojepenezenka; boolean zmenpenezenku(penezenka pen, double platba) { if (pen.getobsah() < platba) return false; pen.zmenobsah(-platba); return true; }; public void platbahotovost(double kolik) { if (zmenpenezenku(mojepenezenka,kolik)) System.out.println("OK"); else System.out.println("Chyba, platbu nelze provest"); }; }; xxb36omo, 2016, Modularita, 8/41

Příklad: Práce s komponentami objektu public class Penezenka { private double obsah; private List<Transakce> historie; public double getobsah() { return obsah; }; public List<Transakce> gethistorie() { return historie; }; public void zmenobsah(double kolik) { obsah += kolik; }; public Penezenka(double pocatek) { obsah = pocatek; } }; class Transakce { private Date kdy; private double castka; public Transakce(Date k, double p) { kdy = k; castka = p; }; }; xxb36omo, 2016, Modularita, 9/41

Příklad: Práce s komponentami objektu (pokr.) class Finance { }; double penize; private Penezenka mojepenezenka; boolean platpenezenkou(double platba) { }; if (this.mojepenezenka.getobsah() < platba) return false; this.mojepenezenka.zmenobsah(-platba); Transakce e = new Transakce(new Date(),-platba); this.mojepenezenka.gethistorie().add(e); return true; public void platbahotovost(double kolik) { }; if (zmenpenezenku(mojepenezenka,kolik)) System.out.println("OK"); else System.out.println("Chyba, platbu nelze provest"); xxb36omo, 2016, Modularita, 10/41

Příklad: Práce s globálními objekty class Finance { public static double hotovost = 1000; // pod matraci private Penezenka mojepenezenka; boolean zmenpenezenku(penezenka pen, double platba) { if (pen.getobsah() < platba) return false; pen.zmenobsah(-platba); return true; }; public void platbahotovost(double kolik) { if (zmenpenezenku(mojepenezenka,kolik)) System.out.println("OK"); else if (hotovost >= kolik) { hotovost -= kolik; System.out.println("OK, ale placeno z hotovosti"); } else System.out.println("Chyba, platbu nelze provest"); }; }; xxb36omo, 2016, Modularita, 11/41

Nástroj pro správnou definici modulů - ADT ADT = Abstract Data Type ADT má specifikaci: vyjadřuje abstraktní vlastnosti modulu, nezabývá se reprezentací a implementací. ADT není jen struktura dat: datová struktura + sada konvencí Sada konvencí invarianty reprezentace. Invarianty indikují, zda je datová struktura dobře vytvořena definují množinu validních stavů datové struktury. Abstrahující funkce: převod konkrétní reprezentace do abstraktních hodnot. Říká, jaký je význam datové struktury (abstraktní hodnota), jak má být datová struktura interpretována, jak počítat inverzi, zobrazení z abstraktních hodnot do objektů. xxb36omo, 2016, Modularita, 12/41

Specifikace ADT je kolekce procedurálních abstrakcí (nikoliv jen kolekce procedur). Procedurální abstrakce poskytují: definici množiny hodnot, ADT je určen specifikací všechny způsoby, jak lze množinu hodnot používat. Abstrakce pro vytváření (creators) vytvářejí nové hodnoty. Abstrakce pro manipulaci (manipulators) mění hodnoty. Abstrakce pro prohlížení (observers) umožňují prohlížení hodnot. xxb36omo, 2016, Modularita, 13/41

Implementace ADT Implementaci ADT (např v Javě) zajišťujeme pomocí tříd (classes). Při implementaci ADT: volíme reprezentaci instancí, implementujeme operace nad touto reprezentací. Při volbě reprezentace respektujeme: aby bylo možné implementovat operace, aby nejčastěji používané operace byly nejvíce efektivní. Které to budou? To sice nevíme, ale abstrakce nám umožňuje tyto skutečnosti změnit. xxb36omo, 2016, Modularita, 14/41

Zapouzdření Umožňuje nám řídit přístupová práva k metodám, třídám a objektům. Je založeno na předpokladu, že správné fungování tříd je založeno na řadě invariantů (vlastnostech, které platí po celou dobu existence objektu). Programátor, který třídu používá nemůže tyto invarianty všechny znát. Z tohoto důvodu je dobré programátorovi zamezit v přístupu k členským proměnným a metodám, které by mohly nesprávným použitím tyto invarianty porušit. Zapouzdřením se snažíme zabránit problému zpřístupnění implementace (Representation Exposure). xxb36omo, 2016, Modularita, 15/41

Příklad : CharSet - Specifikace class CharSet Character CharSet - elts: List<Character> + delete(character) : void + insert(character) : void + member(character) : boolean + size() : int «realize» «interface» CharSet xxb36omo, 2016, Modularita, 16/41

Implementace (jedna možná) class CharSet { private List<Character> elts = new ArrayList<Character>(); public void CharSet(){}; public void insert(character c) { elts.add(c); } public void delete(character c) { elts.remove(c); } public boolean member(character c) { return elts.contains(c); } public int size() { return elts.size(); } }; CharSet s = new CharSet(); Character a = new Character('a'); s.insert(a); s.insert(a); s.delete(a); if (s.member(a)) System.out.println("Chyba"); else System.out.println("OK"); xxb36omo, 2016, Modularita, 17/41

Kde je chyba? Může být špatně metoda delete. Měla by odstranit všechny výskyty znaku. Může být špatně metoda insert! Neměla by vložit znak pokud již v Listu je (množina může jeden prvek obsahovat nejvýše jednou, jinak to není množina, ale tzv. multi-množina, angl. bag). Měli bychom se to dozvědět z invariantu reprezentace datového typu, který by měl být součástí specifikace ADT. xxb36omo, 2016, Modularita, 18/41

Invarianty reprezentace (Representation Invariants) Specifikují vlastnosti stavu ve kterém platí, že je datová struktura dobře utvořená. Zachycují informaci, která musí platit napříč všemi metodami. Můžeme ho specifikovat např. takto: class CharSet with constrai... Nebo formálněji: CharSet - elts: List<Character> + delete(character) : void + insert(character) : void + member(character) : boolean + size() : int «Invariant» {context S:CharSet inv: forall(c:character S.includes(c) implies c <> NULL and S.isUnique(c))} xxb36omo, 2016, Modularita, 19/41

Nyní můžeme nalézt chybu xxb36omo, 2016, Modularita, 20/41

Další ukázka invariantu xxb36omo, 2016, Modularita, 21/41

CharSet pokračování Uvažujme přidání následující metody do třídy CharSet! S následující implementací! Dodržuje implementace getelts() invariant reprezentace? xxb36omo, 2016, Modularita, 22/41

Representation Exposure Uvažujme následující kód: Zpřístupnění implementace je tedy externí přístup k členské proměnné. Zpřístupnění implementace je téměř vždy problém (může dojít k porušení invariantů)! Snažíme se mu vyhnout. xxb36omo, 2016, Modularita, 23/41

1. Využít immutabilitu Způsoby jak zabránit Representation Exposure 2. Vytvořit kopii 3. Vytvořit immutable kopii xxb36omo, 2016, Modularita, 24/41

Je třeba invarianty reprezentace testovat? Určitě, pokud to není nákladné. Ale i když to nákladné je, při ladění je to nutné. Může být těžké testování za běhu vypnout. Některé privátní metody není nutno testovat (proč?). xxb36omo, 2016, Modularita, 25/41

Testování invariantů reprezentace Standardní pravidlo testuj při vstupu a při výstupu (proč?). /* testuje jedinecnost reprezentace invariant */ private void CheckRep() { for (int i = 0; i < elts.size(); i++) { assert elts.indexof(elts.elementat(i) == i); } } /* delete s testy */ public void delete(character c) { CheckRep(); elts.remove(c); CheckRep(); } xxb36omo, 2016, Modularita, 26/41

Předpokládejte, že budete dělat chyby. Vytvořte a používejte kód pro jejich zachycení. Na vstupu: Testujte invarianty reprezentace. Testujte preconditions (klazule requires ). Při výstupu: Praktické programování s ochranou Testujte invarianty reprezentace. Testujte postconditions (klazule provides ). Testování invariantů reprezentace pomůže při hledání chyb. Porozumění invariantům reprezentace pomůže vyhnout se chybám. Nebo dokažte, že chyby nemohou nastat. xxb36omo, 2016, Modularita, 27/41

Invarianty reprezentace omezují strukturu, nikoliv význam Nová implementace metody insert, která zajišťuje invariant: /* nova implementace insert */ public void insert(character c) { Character cc = new Character (encrypt(c)); if (!elts.contains(cc)) elts.addelement(c); } Program je ale stále špatně: xxb36omo, 2016, Modularita, 28/41

Abstrahující funkce: reprezentace význam Abstrahující funkce mapuje konkrétní reprezentaci na abstraktní hodnotu, kterou reprezentuje. AF: Object abstract value AF(CharSet this) = { c c je obsažen v this.elts } množina znaků, obsažená v this.elts Abstrahující funkce typicky není vykonatelná. Abstrahující funkce nám umožňuje pochopit význam z hlediska uživatele. xxb36omo, 2016, Modularita, 29/41

Abstrahující funkce: reprezentace význam Náš cíl je splnit specifikaci metody insert: // modifies: this // effects: this post = this pre U {c} public void insert (Character c); Musíme zkoumat, kde by mohla vzniknut chyba. Aplikace abstrahující funkce na výsledek volání metody insert dává: AF(elts) U {encrypt( a )} Co když použijeme následující abstrahující funkci? AF(this) = { c encrypt(c) je obsaženo v this.elts } AF(this) = { decrypt(c) c je obsaženo v this.elts } xxb36omo, 2016, Modularita, 30/41

Řešení abstrahující funkce Náš základní cíl je splnit specifikaci metody insert: // modifies: this // effects: this post = this pre U {c} public void insert (Character c); AF nám vysvětluje význam: AF(CharSet this) = { c c je obsaženo v this.elts } Uvažme volání metody insert: Na vstupu je význam AF(this pre ) elts pre Na výstupu je význam AF(this post ) = AF(this pre ) U {encrypt( a )} Pak můžeme použít abstrahující funkci: AF(this) = { c encrypt(c) je obsaženo v this.elts } AF(this) = { decrypt(c) c je obsaženo v this.elts } xxb36omo, 2016, Modularita, 31/41

Povolené vedlejší efekty Jiná implementace metody member: boolean member(character c1) { int i = elts.indexof(c1); if (i == -1) return false; // optimalizace Character c2 = elts.elementat(0); elts.set(0, c1); elts.set(i, c2); return true; } Optimalizace (move-to-front) urychluje test na členství. Modifikuje reprezentaci, ale nemění abstraktní hodnotu. xxb36omo, 2016, Modularita, 32/41

Abstrahující funkce je funkce Otázka: Proč AF mapuje konkrétní objekty na abstraktní a ne obráceně? Obrácené zobrazení nemusí být funkce. A nedá se zpravidla použít vždy. xxb36omo, 2016, Modularita, 33/41

Jak psát abstrahující funkce Doménou jsou všechny reprezentace, které splňují invarianty. Obor hodnot: může být někdy obtížné stanovit. Pro matematické entity (jako množiny) je to jednoduché. Pro složitější abstrakce: rozdělte specifikaci na části (fields). AF definuje hodnotu pro každou takovou část. Přehledová část specifikace by měla poskytovat návod na definici abstraktních hodnot. Tištěný výstup se hodí pro ladění. xxb36omo, 2016, Modularita, 34/41

Příklad: ADT pro racionální čísla public class RatNum { private final int numer; private final int denom; // Rep invariant: denom > 0 // numer/denom is in reduced form // Abstraction Function: // represents the rational number numer / denom /** Make a new Ratnum == n. */ public RatNum(int n) { numer = n; denom = 1; checkrep(); } xxb36omo, 2016, Modularita, 35/41

Příklad: ADT pro racionální čísla (pokr.) /** Make a new RatNum == (n / d). * @throws ArithmeticException if d == 0 */ public RatNum(int n, int d) throws ArithmeticException { // reduce ratio to lowest terms int g = gcd(n, d); n = n / g; d = d / g; // make denominator positive if (d < 0) { numer = -n; denom = -d; } else { numer = n; denom = d; } checkrep(); } } xxb36omo, 2016, Modularita, 36/41

Reprezentace versus abstrakce xxb36omo, 2016, Modularita, 37/41

Příklad: test pro racionální čísla (pokr.) // Check that the rep invariant is true // Warning: this does nothing unless you turn on assertion // checking by passing -enableassertions to Java private void checkrep() { assert denom > 0; assert gcd(numer, denom) == 1; } // Return a string representation of this rational number // This effectively implements the abstraction function public String tostring() { return (denom > 1)? (numer + "/" + denom) : (numer + ""); } xxb36omo, 2016, Modularita, 38/41

Invarianty reprezentace: Které konkrétní hodnoty reprezentují jaké abstraktní hodnoty. Abstrahující funkce: Pro každou konkrétní hodnotu určí jakou konkrétní hodnotu reprezentuje. Dohromady to zajišťuje modularizaci implementace: Můžete zkoušet jednotlivé operátory. Žádný není součástí abstrakce (ADT) V praxi: Shrnutí Vždy definujte invarianty reprezentace. Pokud je to zapotřebí, definujte abstrahující funkci. Pro triviální případy ji udělejte jen informativní, formální definice je mnohem težší. xxb36omo, 2016, Modularita, 39/41

Literatura (zdroj) http://courses.cs.washington.edu/courses/cse331/11wi/lectures/le ct05-af-ri.pdf xxb36omo, 2016, Modularita, 40/41

The End xxb36omo, 2016, Modularita, 41/41