1
Návrhové vzory 2
Command, Observer, Adapter, Factory (továrna) a Abstract Factory (abstraktní továrna)
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. • Událostmi řízené programování – událost (event) činnost zapouzdřena do objektu.
2
3
Command - 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.
4
Kontext • Původní řešení je následující: 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.
5
Řešení • Záměrem návrhového vzoru Command je zapouzdřit požadavek (operaci) do formy objektu. • 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( ).
6
Řešení • 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.
7
Schéma vzoru Command pomocí UML
8
Fungování vzoru Command • Proměnná typu Command (rozhraní), může nabývat hodnot svých podtříd, tedy Command1 nebo Command2. • Podle skutečného typu proměnné je pak vybraná třída Command1 resp. Command2 a metoda execute(). • Metoda execute() v sobě obsahuje odkaz buď na a.nejakaOperace() nebo na b,jinaOperace().
9 interface Command { void execute(); } class Hello implements Command { // private TridaA a; // public Hello( Hello(TridaA a){ // this a. = a; } public void execute() { // a.nejakaOperace(); a.nejakaOperace(); System. System.out. out.println(" println("Hello ("Hello "); } } class World implements Command { public void execute() { System. System.out. out.println(" println("World ("World! World! "); } } class IAm implements Command { public void execute() { System. System.out. out.println("I'm println("I'm the command pattern!"); pattern!"); } }
Osnova
10 public class CommandPattern{ CommandPattern{ private RegisterC rc = new RegisterC(6); RegisterC(6); public void test(){ rc. rc.vlozit( vlozit(new Hello()); Hello()); rc. rc.vlozit( vlozit(new World()); World()); rc. rc.vlozit( vlozit(new IAm()); IAm()); } public void run(){ for( for(int i=0; i<=rc i<=rc. rc.getTop(); getTop(); i++) rc. rc.getPrvek(i).execute(); getPrvek(i).execute(); } public static void main( main(String args[]) args[]) { CommandPattern cp = new CommandPattern(); CommandPattern(); cp.test(); cp.test(); cp.run(); cp.run(); new IAm().execute(); IAm().execute(); // jednoradkove reseni new World().execute(); World().execute(); } }
Osnova
Využití vzoru Command v událostmi řízeném programování
• Programátor pouze doplňuje kód, který se má vykonat, když nastane daná událost.
11
12 private class ButtonHandler implements ActionListener { public void actionPerformed( actionPerformed(ActionEvent event) event) { // programá programátor doplní doplní kód oš ošetř etřují ující danou udá událost if( if(event. event.getSource()== getSource()==increment ()==increment) increment) { c.incr c.incr(); incr(); field. field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } else if( if(event. event.getSource()== getSource()==decrement ()==decrement) decrement) { c.decr c.decr(); decr(); field. field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } else if( if(event. event.getSource()== getSource()==clear ()==clear) clear) { c.clr field. c.clr(); clr(); field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } } }
Osnova Příklad čítač – tři tlačítka
13
Observer – Publisher/ Subscribers • Cílem tohoto návrhového vzoru je definovat závislost one-to-many (jeden k mnoha) mezi objekty • Jeden objekt – sledovaný (observable); mnoho objektů – sledovatelé (observers), závislé objekty • Průběh sledování: když se ten jeden (sledovaný) objekt změní, jsou o tom informováni všichni sledovatelé - „závislé“ objekty, takže mohou reagovat na změnu. • Java v tomto směru poskytuje pro sledovaný objekt třídu java.util.Observable. Sledovaný objekt musí být podtřídou této třídy.
14
Observer • Dále tato třída poskytuje metody setChange() a notifyObservers(), které je třeba vyvolat při každé změne. • Každý objekt typu Observer (sledovatel) musí být v úvodu zareristrovaný u objektu typu Observable pomocí metody addObserver(Observer o), která přidá objekt typu Observer do seznamu objektů, které sledují objekt typu Observable.
15
Observer • Objekty (observers), které sledují objekt (Observable object) musí implementovat rozhraní Observer to znamená implementovat metodu update(Observable os, Object ob) se dvěma argumenty. • První argument je sledovaný objekt, druhý pak libovolný objekt, který může eventuálně poskytovat dodatečné informace.
16
Observer – praktická ukázka • Celý návrhový vzor je demonstrován na jednoduché ukázce s jedním účtem a objekty otec a matka (typu Osoba), které na účet přispívají a objekty třídy Student, které z účtu v okamžiku, když tam přijdou peníze, peníze čerpají. • V příkladu jsou uvedeni dva studenti, kteří čerpají stejným dílem. Objekt Ucet je deklarovaný jako objekt podtřídy Observable (pozorovaný objekt). Objektu třídy student implementují rozhraní Observer.
17 import java. java.util. util.Observable; Observable; public class Ucet extends Observable { private int cislo; cislo; private int stav; public Ucet() Ucet() { this(0, this(0, 0, null, null, null); null); } public Ucet( Ucet(int cislo, cislo, int stav, Student s1, Student s2) { this. this.cislo = cislo; cislo; this.stav this.stav = stav; addObserver(s1); addObserver(s1); addObserver(s2); addObserver(s2); } public void vlozeni (int castka) castka) { stav = stav + castka; castka; System. System.out. out.printf(" printf("Ucet ("Ucet vlozeni: vlozeni: %d stav: %d\ %d\n",castka n",castka, castka, getStav()); getStav()); setChanged(); setChanged(); notifyObservers( notifyObservers(castka); castka); } public void vyber (int { (int castka) castka) stav = stav - castka; castka; System. System.out. out.printf(" printf("Ucet ("Ucet vyber: %d zustatek: zustatek: %d\ %d\n",castka n",castka, castka,getStav()); getStav()); }
Osnova
18 public int getStav(){ getStav(){ return stav; } public String toString() toString() { String tx = "Cislo "Cislo uctu: uctu: " + cislo + " stav: " + stav; return tx; tx; } public void tisk() { System. System.out. out.println( println(this. this.toString()); toString()); } public int getCislo(){ getCislo(){ return cislo; cislo; } }
Osnova
19
Osnova
public class Osoba { private String jmeno; jmeno; private String bydliste; bydliste; public Osoba() { this("neuvedeno", this("neuvedeno", ""); } public Osoba(String Osoba(String jmeno, jmeno, String bydliste) bydliste) { this. this.jmeno= jmeno= jmeno; jmeno; this. this.bydliste = bydliste; bydliste; } public void setJmeno( setJmeno(String jmeno) jmeno) { this. } this.jmeno = jmeno; jmeno; public String getJmeno(){ getJmeno(){ return jmeno; } jmeno; public void setBydliste( setBydliste(String bydliste) bydliste) { this. this.bydliste = bydliste; bydliste; } public String getBydliste() { getBydliste() return bydliste; bydliste; } public String toString() toString() { return String. String.format("%5s format("%5s %s %s %s\ %s\n", "Jmeno", Jmeno",getJmeno ",getJmeno(), getJmeno(), "bydliste "bydliste:", bydliste:",getBydliste :",getBydliste()); getBydliste()); public void tiskKlienta() tiskKlienta() { System. System.out. out.println( println(this. this.toString()); toString()); }
}
}
20 public class Rodic extends Osoba{ private Ucet ucet; ucet; public Rodic( Rodic(String jmeno, jmeno, String bydliste, bydliste, Ucet u){ super(jmeno super(jmeno, jmeno, bydliste); bydliste); ucet = u; } public void vlozit( vlozit(int castka){ castka){ ucet. ucet.vlozeni( vlozeni(castka); castka); System. System.out. out.println( println(getClass(). getClass().getName ().getName()+" getName()+" vlozil castku: castku: "+castka "+castka); castka); } }
Osnova
21 import java. java.util. util.Observer; Observer; import java. java.util. util.Observable; Observable; public class Student extends Osoba implements Observer{ Observer{ public Student(String Student(String jmeno, jmeno, String bydliste){ bydliste){ super(jmeno super(jmeno, jmeno, bydliste); bydliste); } public void update(Observable update(Observable ob, Object castka){ castka){ int celkem = ((Ucet ((Ucet)ob). Ucet)ob).getStav )ob).getStav(); getStav(); int penize; penize; if(celkem<=0) if(celkem<=0)penize (celkem<=0)penize = 0; else penize = (Integer) Integer)castka /2; ((Ucet ((Ucet)ob).vyber( Ucet)ob).vyber(penize )ob).vyber(penize); penize); System. System.out. out.println( println(getClass(). getClass().getName ().getName()+ getName()+ " vybral castku: castku: " + penize); penize); } }
Osnova
22 public class TestObserver { public static void main( main(String[] String[] args) args) { Student karel = new Student("Karel","Praha"); Student jarmila = new Student("Jarmila","Olomouc"); Ucet ucet = new Ucet(1, Ucet(1, 200, karel, karel, jarmila); jarmila); Rodic otec = new Rodic("Josef", Rodic("Josef", "Havirov "Havirov", ucet); Havirov", ucet); Rodic matka = new Rodic("Alena", Rodic("Alena", "Havirov "Havirov", Havirov", ucet); ucet); otec.vlozit otec.vlozit(500); vlozit(500); matka.vlozit matka.vlozit(1200); vlozit(1200); } }
Osnova
Využití vzoru Observer pro jednoduchý čítač v prostředí GUI • Model – objekt typu Observable • Views – objekty typu Observer, sledují model a při každé změně tuto změnu automaticky zobrazí. • Výhodné při více různých pohledech na stejná data
23
24 import java java. .util. util.Observable; Observable; import java. java.util. util.Observer; Observer; public class Counter extends Observable { private int cnt = 0; public Counter( Counter(Observer ob){ super(); addObserver(ob); } public void incr() incr() { cnt++; cnt++; setChanged(); setChanged(); notifyObservers(); notifyObservers(); } public void decr() decr() {cnt {cnt-cnt--; --; setChanged(); setChanged(); notifyObservers(); notifyObservers(); } public void clr() clr() {cnt {cnt = 0; setChanged(); setChanged(); notifyObservers(); notifyObservers(); } public int getCnt() getCnt() {return {return cnt; cnt; } }
Osnova
25 import import import import import import import import import import import import
java. java.awt. awt.FlowLayout; FlowLayout; java. java.awt. awt.GridLayout; GridLayout; java. java.awt. awt.BorderLayout; BorderLayout; java. java.awt. awt.event. event.ActionListener; ActionListener; java. java.awt. awt.event. event.ActionEvent; ActionEvent; javax.swing. javax.swing.JFrame .swing.JFrame; JFrame; javax.swing. javax.swing.JTextField .swing.JTextField; JTextField; javax.swing. javax.swing.JLabel .swing.JLabel; JLabel; javax.swing. javax.swing.JButton .swing.JButton; JButton; javax.swing. javax.swing.JPanel .swing.JPanel; JPanel; java. java.util. util.Observer; Observer; java. java.util. util.Observable; Observable;
public class CounterFrame extends JFrame implements Observer{ Observer{ private Counter c; private JButton increment, increment, decrement, decrement, clear; clear; private JTextField field; field; private JLabel label; label; private JPanel panel; public CounterFrame() CounterFrame() { super("Simple super("Simple counter"); counter"); setLayout( setLayout(new FlowLayout()); FlowLayout()); label = new JLabel(" JLabel("Counter state: "); ("Counter state: add( add(label); label); field = new JTextField(6); JTextField(6); field. field.setEditable( setEditable(false); false); field. field.setHorizontalAlignment( setHorizontalAlignment(JTextField.RIGHT); JTextField.RIGHT); add( add(field); field);
Osnova
26 field. field.setText("0"); setText("0"); panel = new JPanel(); JPanel(); panel.setLayout panel.setLayout( setLayout(new GridLayout(1,3)); GridLayout(1,3)); increment = new JButton(" JButton("Increment ("Increment"); Increment"); panel.add panel.add( add(increment); increment); decrement = new JButton(" JButton("Decrement ("Decrement"); Decrement"); panel.add panel.add( add(decrement); decrement); clear = new JButton(" JButton("Clear ("Clear"); Clear"); panel.add panel.add( add(clear); clear); add(panel, add(panel, BorderLayout.SOUTH); BorderLayout.SOUTH); ButtonHandler handler = new ButtonHandler(); ButtonHandler(); increment. increment.addActionListener( addActionListener(handler); handler); decrement. decrement.addActionListener( addActionListener(handler); handler); clear. clear.addActionListener( addActionListener(handler); handler); }
Osnova
27 private class ButtonHandler implements ActionListener { public void actionPerformed( actionPerformed(ActionEvent event) event) { if( if(event. event.getSource()== getSource()==increment ()==increment) increment) { c.incr c.incr(); incr(); //field //field. field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } else if( if(event. event.getSource()== getSource()==decrement ()==decrement) decrement) { c.decr c.decr(); decr(); //field //field. field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } else if( if(event. event.getSource()== getSource()==clear ()==clear) clear) { c.clr c.clr(); clr(); //field //field. field.setText( setText(String. String.valueOf(c. valueOf(c.getCnt (c.getCnt())); getCnt())); } } } public void setCounter( setCounter(Counter c){ this.c this.c = c; } public void update(Observable update(Observable ob, Object object){ object){ field. field.setText( setText(String. String.valueOf((( valueOf(((Counter (((Counter)ob). Counter)ob).getCnt )ob).getCnt())); getCnt())); } }
Osnova
28 import javax javax.swing. .swing.JFrame .swing.JFrame; JFrame; public class CounterFrameTest { public static void main( main(String args[]) args[]) { CounterFrame counterFrame= counterFrame= new CounterFrame(); CounterFrame(); Counter c = new Counter( Counter(counterFrame); counterFrame); counterFrame. counterFrame.setCounter(c); setCounter(c); counterFrame. counterFrame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JFrame.EXIT_ON_CLOSE); counterFrame. counterFrame.setSize(400, setSize(400, 200); counterFrame. counterFrame.setVisible( setVisible(true); true); } }
Osnova
29
Adapter • Kontext: • při vývoji aplikace je k dispozici nějaká třída k použití, ale tato třída „nepasuje“ do existujících struktur – třída nepodporuje existující rozhraní • Problém: • jak využít existující třídu, která nějak nezapadá do existující struktury • Řešení: • zavést objekt jako spojku mezi klienta, který očekává určité rozhraní a nekompatibilní rozhraní dané třídy, takže klient může využívat neznámé rozhraní
30
Adapter • Záměrem vzoru Adapter je poskytnout (zpřístupnit) rozhraní, které očekává klient zatímco používá služby třídy s odlišným rozhraním
31
Adapter – diagram UML Client 1
1
ExistingClass
«rozhraní» RequiredInterface +reguiredMethod()
+usefulMethod()
Adapter +reqiuredMethod()
• Třída Adapter splňuje požadavky třídy Client
32
Object Adapter – jednodušší varianta
33 class Target { public void request() request() {} } class Adaptee { public void specificRequest() specificRequest() { System. System.out. out.println(" println("Adaptee ("Adaptee: Adaptee: SpecificRequest"); SpecificRequest"); } } class Adapter extends Target { private Adaptee adaptee; adaptee; public Adapter(Adaptee Adapter(Adaptee a) { adaptee = a; } public void request() request() { adaptee. adaptee.specificRequest(); specificRequest(); } } public class SimpleAdapter { Adaptee a = new Adaptee(); Adaptee(); Target t = new Adapter(a); public void test() { t.request t.request(); request(); } public static void main( main(String args[]) args[]) { SimpleAdapter sa = new SimpleAdapter(); SimpleAdapter(); sa.test(); sa.test(); } }
Osnova Simple Adapter
34
Class a Object Adapter • předchozí případy využívaly tzv. class Adapteru, vlastní Adapter je podtřídou třídy, jejíž operaci požadujeme • class Adapter: – implementuje požadované rozhraní – je podtřídou požadované třídy
• tento přístup vždy nefunguje, zejména když množina metod, kterou potřebujeme adaptovat, není specifikována v rozhraní • tehdy se použije object Adapter – používá delegování, místo subclassing (aby se stal podtřídou)
35
Object Adapter
36
Simple Adapter – diagram tříd
37
Kontrétní příklad
38 import junit. junit.framework.*; framework.*; class WhatIHave { public void g() {} public void h() {} } interface WhatIWant { void f(); } class SurrogateAdapter implements WhatIWant { WhatIHave whatIHave; whatIHave; public SurrogateAdapter( SurrogateAdapter(WhatIHave wih) wih) { whatIHave = wih; wih; } public void f() { // Implement behavior using // methods in WhatIHave: WhatIHave: whatIHave.g(); whatIHave.g(); whatIHave.h(); whatIHave.h(); } } class WhatIUse { public void op(WhatIWant op(WhatIWant wiw) wiw) { wiw.f(); wiw.f(); } }
Osnova
39 // Approach 2: build adapter use into op(): class WhatIUse2 extends WhatIUse { public void op(WhatIHave op(WhatIHave wih) wih) { new SurrogateAdapter( SurrogateAdapter(wih).f(); wih).f(); } } // Approach 3: build adapter into WhatIHave: WhatIHave: class WhatIHave2 extends WhatIHave implements WhatIWant { public void f() { g(); h(); } } // Approach 4: use an inner class: class: class WhatIHave3 extends WhatIHave { private class InnerAdapter implements WhatIWant{ WhatIWant{ public void f() { g(); h(); } } public WhatIWant whatIWant() whatIWant() { return new InnerAdapter(); InnerAdapter(); } }
Osnova
40 public class AdapterVariations extends TestCase { WhatIUse whatIUse = new WhatIUse(); WhatIUse(); WhatIHave whatIHave = new WhatIHave(); WhatIHave(); WhatIWant adapt= adapt= new SurrogateAdapter( SurrogateAdapter(whatIHave); whatIHave); WhatIUse2 whatIUse2 = new WhatIUse2(); WhatIHave2 whatIHave2 = new WhatIHave2(); WhatIHave3 whatIHave3 = new WhatIHave3(); public void test() { whatIUse.op( whatIUse.op(adapt .op(adapt); adapt); // Approach 2: whatIUse2.op(whatIHave whatIUse2.op(whatIHave); whatIHave); // Approach 3: whatIUse.op(whatIHave2); whatIUse.op(whatIHave2); // Approach 4: whatIUse.op(whatIHave3. whatIUse.op(whatIHave3.whatIWant .op(whatIHave3.whatIWant()); whatIWant()); } public static void main( main(String args[]) args[]) { junit. junit.textui. textui.TestRunner.run( TestRunner.run(AdapterVariations .run(AdapterVariations. AdapterVariations.class); class); } }
Osnova
41
Adaptace dat pro JTable • JTable – princip práce vychází ze vzoru MVC s tím, že controller a view jsou v jedné komponentě JTable a model je v komponentě TableModel • TableModel uvědomuje o změnách komponentu view v JTable • JTable (komponenta Controller) modifikuje data v komponentě TableModel
42
JTable princip • JTable – Implementuje architekturu delegat-model – Má pravomoc pro TableModels
• TableModel – Declaruje metody • Obnovuje modifikovaná data mod ifie s JTable
TableModel notifies
JTable a TableModel architektura delegát-model
43 public class RocketTableModel extends AbstractTableModel { protected Rocket[] rockets; protected String[] columnNames = new String[] { "Name", "Price", "Apogee" }; public RocketTableModel(Rocket[] RocketTableModel(Rocket[] rockets) { this.rockets = rockets; } public int getColumnCount() getColumnCount() { return columnNames.length; columnNames.length; } // @param index which column is interesting // @return the name of the indicated column public String getColumnName(int i) { return columnNames[i]; columnNames[i]; } // @return public int return }
the number of rows in this table. getRowCount() getRowCount() { rockets.length; rockets.length;
Osnova
44 // @param @param row which row is interesting // @param @param col which column is interesting // @return the value at the indicated row and column. public Object getValueAt(int row, int col) col) { switch (col (col) col) { case 0: return rockets[row].getName(); rockets[row].getName(); case 1: return rockets[row].getPrice(); rockets[row].getPrice(); case 2: return new Double(rockets[row].getApogee()); Double(rockets[row].getApogee()); default: return null; } } }
Osnova
45 public class ShowRocketTable { public static void main( main(String[] String[] args) args) { setFonts(); setFonts(); JTable table = new JTable( JTable(getRocketTable()); getRocketTable()); table.setRowHeight table.setRowHeight(36); setRowHeight(36); JScrollPane pane = new JScrollPane(table); JScrollPane(table); pane.setPreferredSize pane.setPreferredSize( java.awt. awt.Dimension(300, Dimension(300, 100)); setPreferredSize(new java. display(pane, " Rockets"); Rockets"); } /** * Display a Swing component. component. We' We'll refactor this later into a nice facade. facade. * * @param @param c the component to display * @param @param title the window title */ public static void display(Component display(Component c, String title) title) { JFrame frame = new JFrame( JFrame(title); title); frame. frame.getContentPane(). getContentPane().add ().add(c); add(c); frame. frame.setDefaultCloseOperation( setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JFrame.EXIT_ON_CLOSE); frame. frame.pack(); pack(); frame. frame.setVisible( setVisible(true); true); }
Osnova
46 private static RocketTableModel getRocketTable() getRocketTable() { Rocket r1 = new Rocket(" Rocket("Shooter ("Shooter", Shooter", 1.0, new Dollars(3.95), Dollars(3.95), 50.0, 4.5); Rocket r2 = new Rocket("Orbit", Rocket("Orbit", 2.0, new Dollars(29.03), Dollars(29.03), 5000, 3.2); return new RocketTableModel( RocketTableModel(new Rocket[] Rocket[] { r1, r2 }); } private static void setFonts() setFonts() { Font font = new Font("Dialog", Font.PLAIN, 18); UIManager.put("Table.font", UIManager.put("Table.font", font); UIManager.put(" UIManager.put("TableHeader .put("TableHeader.font", TableHeader.font", font); } }
Osnova
47
Shrnutí • Vzor Adapter umožňuje, aby existující třída splnila požadavky třídy Klient. • Pokud klient specifikuje požadavky v rozhraní, vytvoří se nová třída (adapter), která implementuje požadavky rozhraní a zároveň je podtřídou existující třídy – Class Adapter • Pokud klient nespecifikuje své požadavky přes rozhraní, vytvoří se podtřída od klienta, jejímž datovým atributem bude instance existující třídy – Object Adapter.
48
Shrnutí • Komponenta JTable s balíčku javax.swing využívá vzor Adapter. • Adapter se většinou vytvoří jako podtřída od AbstractTableModel
49
Metoda Factory • Kontext: • při vytváření třídy obyčejně doplníme konstruktory na tvorbu instancí • někdy klient potřebuje nový objekt, ale neví, od které z několika možných tříd vytvořit instanci (hierarchie) • Řešení: • metoda Factory umožňuje tvůrci tříd definovat rozhraní pro vytvářený objekt, zatímco si ponechává volbu, ze které třídy instanci vytvoří
50
Klasický příklad použití: Iterátor • Vzor Iterátor poskytuje způsob zpřístupnit sekvenčně prvky kolekce • Při vytváření instancí Iterátorů je použita metoda Factory • V rozhraní Collection je metoda iterator(), kterou pak implementují všechny třídy kolekcí. Metoda iterator() izoluje toho, kdo ji volá od povědomí, od které vlastně třídy vytváří instanci.
51 public class ShowIterator { public static void main( main(String[] String[] args) args) { List list = Arrays. Arrays.asList( asList( new String[] String[] { "fountain "fountain", fountain", "rocket "rocket", rocket", "sparkler "sparkler" sparkler" }); Iterator iter = list.iterator list.iterator(); iterator(); while (iter. iter.hasNext()) hasNext()) System. System.out. out.println( println(iter. iter.next()); next()); // Uncomment the next line to see the iterator's iterator's actual class: class: // System. System.out. out.println( println(iter. iter.getClass(). getClass().getName ().getName()); getName()); } }
Osnova
52
Rozpoznání Metody Factory • mylná domněnka, že každá metoda která vytváří a vrací objekt je metoda „factory“ • v OOP je běžné, že metody vrací nový objekt • fakt, že metoda vytváří nový objekt nemusí znamenat, že je příkladem metody Factory • metoda Factory je metoda, která jednak vytváří nový objekt a dále izoluje klienta od povědomí, od které třídy vytváří objekt • při požadavku na objekt, je definitivní třída objektu, který bude vytvořen závislá na chování objektu Factory, který dostal požadavek na vytvoření nového objektu
53
Řízení vytváření instancí • standardní cesta vytvoření instance – konstruktor • někdy klient, který chce objekt přesně neví od které třídy se má objekt vytvořit • např. iterátor – objekt iterátoru závisí na typu třídy, kterou chce klient procházet
54
Jednoduchá statická metoda factory • Metoda factory přijímá argument, podle kterého určí typ objektu nadtřídy Shape. • Předávaný argument je typu String – může být i jiný typ • V případě přidání další podtřídy – nutno změnit kód metody factory • Konstruktory podtříd nadtřídy Shape mají přístup v rámci paketu, takže jsou přistupni i pro metodu factory(), avšak nejsou přístupni z vnějšku paketu (balíčku)
55
Jednoduchá statická metoda factory • Výsledek – kód se mění pouze v jednom místě
56 // jednoducha staticka metoda factory package factory.shape1; factory.shape1; import java. java.util.*; util.*; import junit. junit.framework.*; framework.*; abstract class Shape { public abstract void draw(); draw(); public abstract void erase(); erase(); public static Shape factory( factory(String type) { if(type. if(type.equals (type.equals(" equals("Circle ("Circle")) Circle")) return new Circle(); Circle(); if(type. if(type.equals (type.equals("Square")) equals("Square")) return new Square(); throw new RuntimeException( RuntimeException( "Bad shape creation: creation: " + type); } } class Circle extends Shape { Circle() Circle() {} // PackagePackage-access constructor public void draw() draw() { System. System.out. out.println(" println("Circle ("Circle. Circle.draw"); draw"); } public void erase() erase() { System. System.out. out.println(" println("Circle erase"); ("Circle. Circle.erase"); } }
Osnova
57 class Square extends Shape { Square() {} // PackagePackage-access constructor public void draw() draw() { System. System.out. out.println("Square. println("Square.draw ("Square.draw"); draw"); } public void erase() erase() { System. System.out. out.println("Square. println("Square.erase ("Square.erase"); erase"); } } public class ShapeFactory1 extends TestCase { String shlist[] shlist[] = { "Circle "Circle", Circle", "Square", "Square", "Circle "Circle", Circle", "Circle "Circle", Circle", "Square" }; List shapes = new ArrayList(); ArrayList(); public void test() { Iterator it = Arrays. Arrays.asList( asList(shlist). shlist).iterator ).iterator(); iterator(); while( while(it. it.hasNext()) hasNext()) shapes. shapes.add( add(Shape. Shape.factory(( factory((String ((String) String)it. it.next())); next())); it = shapes. shapes.iterator(); iterator(); while( while(it. it.hasNext()) hasNext()) { Shape s = (Shape (Shape) Shape)it. it.next(); next(); s.draw s.draw(); draw(); s.erase s.erase(); erase(); } } public static void main( main(String args[]) args[]) { junit. junit.textui. textui.TestRunner.run(ShapeFactory1. TestRunner.run(ShapeFactory1.class .run(ShapeFactory1.class); class); } }
Osnova
58
Polymorfické metody factory • Toto řešení odpovídá původním požadavkům knihy Design Patterns – odlišné třídy od metody factory() mohou být podtřídami základní metody factory() • Předchozí případ byl speciálním případem • V následujícím příkladě jsou metody factory modifikovány a jsou v oddělených třídách, jako virtuální funkce. • Podtřídy Shape jsou dynamicky nahrávány do paměti • Třídy Factory jsou vnitřní třídy
59
Polymorfické metody factory • Metoda create() třídy ShapeFactory je protected – což znamená, že nemůže být volaná přímo, ale může být zastíněna (overriden). • Každá z podtřídy třídy Shape musí vytvořit svoji vlastní podtřídy od FactoryShape a zastínit metodu create() proto, aby vytvořily objekt vlastního typu • Vytvoření objektu je provedeno: ShapeFactory.createShape()
což je statická metoda používající Map a ShapeFactory
60
Polymorfické metody factory • Třída ShapeFactory musí být inicializovaná nahráním Map s objekty factory – což se provádí při statické inicializaci • Při přidání další třídy do tohoto návrhu musíte zdědit třídu vytvořenou třídou Factory a přidat statickou inicializaci k nahrávání Map
UML diagram tříd třídy Factory jsou vnořené třídy
61
62 // Polymorphic factory methods. methods. package factory.shape2; factory.shape2; import java. java.util.*; util.*; import junit. junit.framework.*; framework.*; interface Shape { void draw(); draw(); void erase(); erase(); } abstract class ShapeFactory { protected abstract Shape create(); create(); private static Map <String <String, String, ShapeFactory> ShapeFactory>factories = new HashMap< HashMap<String, String, ShapeFactory>(); ShapeFactory>(); public static void addFactory( addFactory(String id, ShapeFactory f) { factories.put(id, factories.put(id, f); }
Osnova
63 // A Template Method: Method: public static final Shape createShape( createShape(String id) { if(! if(!factories (!factories. factories.containsKey(id)) containsKey(id)) { try { // Load dynamically Class. Class.forName(" forName("factory ("factory.shape2." factory.shape2." + id); System. System.out. out.println("Konec println("Konec try"); try"); } catch( catch(ClassNotFoundException e) { throw new RuntimeException( RuntimeException( "Bad shape creation: creation: " + id); } // See if it was put in: if(! if(!factories (!factories. factories.containsKey(id)) containsKey(id)) throw new RuntimeException( RuntimeException( "Bad shape creation: creation: " + id); } return (factories. factories.get(id)). get(id)).create (id)).create(); create(); } }
Osnova
64 class Circle implements Shape { private Circle() Circle() {} public void draw() draw() { System. System.out. out.println(" println("Circle ("Circle. Circle.draw"); draw"); } public void erase() erase() { System. System.out. out.println(" println("Circle erase"); ("Circle. Circle.erase"); } private static class Factory extends ShapeFactory { protected Shape create() create() { return new Circle(); Circle(); } } static { System. System.out. out.println("Kruh"); println("Kruh"); ShapeFactory. ShapeFactory.addFactory( addFactory( "Circle", Circle", new Factory()); Factory()); } }
Osnova
65 class Square implements Shape { private Square() {} public void draw() draw() { System. System.out. out.println("Square. println("Square.draw ("Square.draw"); draw"); } public void erase() erase() { System. System.out. out.println("Square. println("Square.erase ("Square.erase"); erase"); } private static class Factory extends ShapeFactory { protected Shape create() create() { return new Square(); } } static { System. System.out. out.println(" println("Ctverec ("Ctverec"); Ctverec"); ShapeFactory. ShapeFactory.addFactory( addFactory( "Square", new Factory()); Factory()); } }
Osnova
66 public class ShapeFactory2 extends TestCase { String shlist[] shlist[] = { "Circle "Circle", Circle", "Square", "Square", "Circle "Circle", Circle", "Circle "Circle", Circle", "Square" }; List<Shape List<Shape> Shape> shapes = new ArrayList< ArrayList<Shape>(); Shape>(); public void test() { // This just makes sure it will complete // without throwing an exception. exception. Iterator it = Arrays. Arrays.asList( asList(shlist). shlist).iterator ).iterator(); iterator(); while( while(it. it.hasNext()) hasNext()) shapes. shapes.add( add( ShapeFactory. ShapeFactory.createShape(( createShape((String ((String)( String)(it )(it. it.next()))); next()))); it = shapes. shapes.iterator(); iterator(); while( while(it. it.hasNext()) hasNext()) { Shape s = (Shape (Shape) Shape)it. it.next(); next(); s.draw s.draw(); draw(); s.erase s.erase(); erase(); } } public static void main( main(String args[]) args[]) { junit. junit.textui. textui.TestRunner.run(ShapeFactory2. TestRunner.run(ShapeFactory2.class .run(ShapeFactory2.class); class); } }
Osnova
67
Abstract Factory • Někdy chcete vytvořit objekt, zatím ale není jasné od které třídy • v tomto případě možno použít vzor metoda Factory s metodou, která používá vnější faktor, který řídí, od které třídy se vytvoří instance • Smyslem tohoto vzoru je dovolit vytvoření rodiny souvisejících nebo závislých objektů, přičemž klient je izolován od volání těchto tříd.
68
Abstract Factory • Vzor Abstract Factory je podobný objektům Factory s předchozího příkladu, ale s více factory() metodami. • Každá z těchto factory metod vytváří odlišný typ objektu. • Myšlenka je, že v době tvorby objektu Factory rozhodnete, jak tyto objekty vytvořené Factory použijete.
69
Abstract Factory • V následujícím příkladu existují rozhraní Player a Obstacle ale s různými typy Players a Obstacles (třídy implementující rozhraní) závislé na tom, kterou hru hrajete. • Vy rozhodnete o výběru hry výběrem konkrétní GameElementFactory a poté GameEnvironment řídí nastavení a hraní hry. • V příkladu jsou nastavení a vlastní hra velmi jednoduché, avšak úvodní podmínky a změny stavů mohou určit mnoho ze závěru hry.
70
Abstract Factory • Třída GameEnvironment není navržena k dědění, ačkoli by to dávalo smysl.
Abstract Factory UML diagram tříd
71
72 interface Obstacle { void action(); action(); } interface Player { void interactWith( interactWith(Obstacle o); } class Kitty implements Player { public void interactWith( interactWith(Obstacle ob) { System. System.out. out.print(" print("Kitty ("Kitty has encountered a "); ob.action ob.action(); action(); } } class KungFuGuy implements Player { public void interactWith( interactWith(Obstacle ob) { System. System.out. out.print(" print("KungFuGuy ("KungFuGuy now battles a "); ob.action ob.action(); action(); } } class Puzzle implements Obstacle { public void action() action() { System. System.out. out.println(" println("Puzzle ("Puzzle"); Puzzle"); } }
Osnova
73 class NastyWeapon implements Obstacle { public void action() action() { System. System.out. out.println(" println("NastyWeapon ("NastyWeapon"); NastyWeapon"); } } // The Abstract Factory: Factory: interface GameElementFactory { Player makePlayer(); makePlayer(); Obstacle makeObstacle(); makeObstacle(); } // Concrete factories: factories: class KittiesAndPuzzles implements GameElementFactory { public Player makePlayer() makePlayer() { return new Kitty(); Kitty(); } public Obstacle makeObstacle() makeObstacle() { return new Puzzle(); Puzzle(); } } class KillAndDismember implements GameElementFactory { public Player makePlayer() makePlayer() { return new KungFuGuy(); KungFuGuy(); } public Obstacle makeObstacle() makeObstacle() { return new NastyWeapon(); NastyWeapon(); } }
Osnova
74 class GameEnvironment { private GameElementFactory gef; gef; private Player p; private Obstacle ob; public GameEnvironment( GameEnvironment( GameElementFactory factory) factory) { gef = factory; factory; p = factory. factory.makePlayer(); makePlayer(); ob = factory. factory.makeObstacle(); makeObstacle(); } public void play() play() { p.interactWith p.interactWith(ob); interactWith(ob); } } public class Games { public static void main( main(String args[]) args[]) { GameElementFactory kp = new KittiesAndPuzzles(), KittiesAndPuzzles(), kd = new KillAndDismember(); KillAndDismember(); GameEnvironment g1 = new GameEnvironment( GameEnvironment(kp), kp), // vnejsi parametr g2 = new GameEnvironment( GameEnvironment(kd); kd); // vnejsi parametr g1.play g1.play(); play(); g2.play g2.play(); play(); } }
Osnova