===== Osnova ===== * diagram tříd - třídy, atributy, metody, asociace, kardinalita asociací * hierarchické vztahy - generalizace, podtyp/nadtyp, dědičnost * diagramy stavů - stavy, přechody, události, spouštěcí podmínky (guards), akce * diagram sekvencí * diagram komunikace * návrhové vzory - tovární metoda (factory method), jedináček (singleton), abstraktní továrna (abstract factory), prototyp (prototype), stav/strategie (state/strategy), kompozit (composite), interpretr (interpreter), návštěvník (visitor) * double dispatch ===== Příklady na návrhové vzory ===== * Třída ''Rectangle'' reprezentuje obdélník, třída ''Square'' čtverec. Napište tovární metodu ''Shape getShape(int height, int width)'', která vrátí instanci vhodné třídy. Vhodné konstruktory si domyslete. Tip: použijte tovární metodu. * Fronta se od zásobníku liší tím, že metoda pop vrací nejstarší prvek, zatímco u zásobníku ta samá metoda vrací prvek nejmladší. Naimplementujte třídu ''Container'', která se chová jako fronta nebo jako zásobník podle toho, co si uživatel zvolí. Tip: při implementaci využijte návrhový vzor state nebo strategy. * Naimplementujte abstraktní továrnu: třídy továren by měly být ''Zerg'', ''Protoss'' a ''Terran'', jejich společné rozhraní ''Race'' s tovární metodou ''Unit getUnit()'', statická metoda vracející abstraktní továrnu by měla být ve třídě ''Game'' a jmenovat se ''getMyRace()''. * Napište třídy implementující návrhový vzor interpret, který by byl schopen interpretovat níže uvedený kousek kódu. a = 10; b = a; c = b + 1; print c; * Níže uvedený kód implementuje návrhový vzor. Který? interface A { void f(D d); } class B implements A { public void f(D d) { d.g(this); } } class C implements A { public void f(D d) { d.h(this); } } interface D { void g(B b); void h(C c); } * Třída ''List'' reprezentuje seznam čísel. Upravte kód níže tak, aby šlo ve spojovém seznamu kromě instancí třídy ''Chunk'' libovolně střídat také instance tříd ''Node'' a ''Pair'' (upravit budete muset všechny třídy, neřešte změny v kódu, který nevidíte). class List { Chunk first; /* kod */ } class Chunk { int[] contents = new int[10]; int length; Chunk next; /* kod */ } class Node { int contents; Node next; /* kod */ } class Pair { int first; int second; Pair next; /* kod */ } * Do třídy ''Cell'' dopište podporu návrhového vzoru jedináček (singleton). Dejte si pozor, aby tato podpora skutečně zajistila, že nepůjde vytvořit víc než jedna instance ''Cell''. class Cell { int contents; void set(int c) { contents = c; } int get() { return contents; } } * Navrhněte strukturu tříd (tzn. rozhraní, třídy, jejich instanční proměnné a hlavičky metod) odpovídající návrhovému vzoru interpretr, která by byla schopná interpretovat následující kód: begin a = 8 b = 9 r = 0 while a > 0 do begin r = r + b a = a – 1 end end * Napište kód implementující návrhový vzor abstract factory: továrna by měla být schopná vracet ''BlueCircle'' a ''BlueSquare'' nebo ''RedCircle'' a ''RedSquare'' v závislosti na tom, jestli je ve stavu //blue// nebo //red//. * Změňte níže uvedený kód tak, abyste se zbavili použití operátoru ''instanceof''. Pokud chcete, můžete změnit také design souvisejícího kódu. Inspirujte se návrhovým vzorem visitor/double dispatchem, vyhněte se řešením založeným na metodách typu ''getTypeId''. void f(Vehicle v) { if (v instanceof Car) v.g(); else v.h(); } * Třída ''InfiniteArray'' reprezentuje nekonečné pole (s indexy od nuly výš), které je na počátku celé naplněné nulami. Má dvě možné reprezentace – pole, které se automaticky “prodlužuje”, nebo spojový seznam dvojic (index, hodnota). Napište implementaci třídy ''InfiniteArray'', která mezi těmito dvěma reprezentacemi přepíná – pokud počet nulových prvků v intervalu (0, nejvyšší index s nenulovým prvkem) klesne pod třetinu, přepne na reprezentaci polem, pokud naopak vzroste nad dvě třetiny, přepne na reprezentaci spojovým seznamem. Při implementaci použijte návrhový vzor state/strategy. class InfiniteArray { /* atributy */ void set(int index, int value) { /* kod */ } int get(int index) { /* kod */ } } * Do rozhraní ''Zajic'' a tříd ''Bob'' a ''Bobek'' doplňte podporu pro návrhový vzor visitor a využijte ji v metodě ''Vecernicek.zacni'' tak, abyste se zbavili použití operátoru ''instanceof''. interface Zajic { void vstavej(); void spi(); } class Bob implements Zajic { public void vstavej() { /* kod */ } public void spi() { /* kod */ } } class Bobek implements Zajic { public void vstavej() { /* kod */ } public void spi() { /* kod */ } } class Vecernicek { void zacni(Zajic z) { if (z instanceof Bob) z.vstavej(); if (z instanceof Bobek) z.spi(); } } * Instance třídy ''Fraction'' jsou imutabilní – během svého života nemění svoji hodnotu. Napište tovární metodu ''Fraction get(int n, int d)'', která pro stejné hodnoty ''n'' a ''d'' vrací stejné instance. Pokud chcete, můžete použít třídu ''HashMap''. class Fraction { int nominator, denominator; Fraction(int n, int d) { nominator = n; denominator = d; } Fraction times(Fraction f) { return new Fraction( nominator * f.nominator, denominator * f.denominator); } } * Napište třídy implementující návrhový vzor interpret, který by byl schopen interpretovat níže uvedený kousek kódu. a = 10; b = a; c = b + 1; print c; * Níže uvedený kód implementuje návrhový vzor. Který? interface A { B getB(); C getC(); } interface B {} interface C {} class D implements B {} class E implements B {} class F implements C {} class G implements C {} class H implements A { public B getB() { return new D(); } public C getC() { return new F(); } } class I implements A { public B getB() { return new E(); } public C getC() { return new G(); } } class J { A a = new H(); void h() { a = new H(); } void i() { a = new I(); } A getA() { return a; } } * Naprogramujte třídu ''Fraction'' reprezentující zlomek. Použijte návrhový vzor kompozit tak, aby v čitateli i jmenovateli zlomku mohla být jak reálná, tak komplexní třída. * Naprogramujte tovární metodu ''static Number getNumber(int re, int im)'', která bude pro reprezentaci stejných čísel vracet stejné objekty (tzn. například volání ''getNumber(1, 2)'' pokaždé vrátí ukazatel na stejnou instanci). Pokud chcete, můžete použít ''HashMap'' nebo jinou kolekci. * Do kódu níže dopište podporu pro visitora a visitora, který dané dvě instance sečte. interface Number { int getReal(); int getImaginary(); } class Real implements Number { int value; Real(int v) { value = v; } public int getReal() { return value; } public int getImaginary() { return 0; } } class Complex implements Number { int re, im; Complex(int r, int i) { re = r; im = i; } public int getReal() { return re; } public int getImaginary() { return im; } } * Třídy ''Vladimir'' a ''Iljic'' implementují interface ''Lenin''. Napište tovární metodu, která pomocí abstract factory při prvních pěti voláních vrátí novou instanci třídy ''Vladimir'' a pak už jen instance třídy ''Iljic''. * V JDBC se spojení s databází naváže pomocí kódu napsaného níže. Instance vrácená voláním ''createStatement'' implementuje rozhraní ''Statement'', ale její skutečnou třídu JDBC určí podle řetězce zadaného jako parametr metody ''getConnection''. Který návrhový vzor (z těch, které jsme se učili) je tomu nejblíže a proč? Connection con = DriverManager.getConnection ("jdbc:myDriver:wombat","myLogin","myPassword"); Statement stmt = con.createStatement(); * Uživatel vašeho matematického systému chce ve svých programech psát zápisy typu ''A + B'' (sjednocení dvou množin), ''A – B'' (množinový rozdíl) a ''A * B'' (kartézský součin dvou množin). Navrhněte strukturu tříd implementující návrhový vzor interpretr, která by tyto operace byla schopná realizovat. * V kódu níže je neoptimalita související s tím, že všechny třídy implementující rozhraní ''Sort'' nemají žádné instanční proměnné a jsou tudíž bezestavové. Zlepšete ho. Tip: spojte tovární metodu s návrhovým vzorem stav. class Sorter { static void bubbleSort(int[] array) { /* kod */ } static void quickSort(int[] array) { /* kod */ } static void heapSort(int[] array) { /* kod */ } Sort s = new BubbleSort(); void setPreferredSortType(int type) { if (type == 1) s = new BubbleSort(); if (type == 2) s = new QuickSort(); if (type == 3) s = new HeapSort(); } void sort(int[] array) { s.sort(array); } } interface Sort { void sort(int[] array); } class BubbleSort implements Sort { public void sort(int[] array) { Sorter.bubbleSort(array); } } class QuickSort implements Sort { public void sort(int[] array) { Sorter.quickSort(array); } } class HeapSort implements Sort { public void sort(int[] array) { Sorter.heapSort(array); } } * Třída ''Triangle'' implementuje návrhový vzor state se stavy pravoúhlý, ostroúhlý a tupoúhlý. Naimplementujte do této třídy podporu pro návrhový vzor visitor, která se bude rozhodovat podle stavu dané instance. class Triangle { TriangleState s; void visit(TriangleVisitor v) { /* … */ } /* ... */ } interface TriangleVisitor { void visitRectangular(Triangle t); void visitObtuse(Triangle t); void visitAcute(Triangle t); }