1 Projektowanie Obiektowe Konsultacje wt. 10:15-11:00 [email protected] http://kiwi.if.uj.edu.pl/~pbialas/ProjektowanieObiektowe
2 ● Chcemy utworzyć obiekt który posiada ● Kilka parametrów obowiązowych, ew. kilka zestawów parametrów obowiązkowych ● Większą liczbę parametrów nieobowiązkowych ● Utworzony obiekt ma być niezmienny (immutable), lub ma przynajmniej cześć cech stałych.
3 Telescoping constructors public class SomeClass { private String mandatory1; private String mandatory2; private String optional1; private String optional2; private int optional3; private int optional4; public SomeClass(String m1,String m2) { this(m1,m2,""); } public SomeClass(String m1,String m2,String o1) { this(m1,m2,o1,""); } public SomeClass(String m1,String m2,String o1, String o2) { this(m1,m2,o1,o2,0); } public class SomeClass { private String mandatory1; private String mandatory2; private String optional1; private String optional2; private int optional3; private int optional4; public SomeClass(String m1,String m2) { this(m1,m2,""); } public SomeClass(String m1,String m2,String o1) { this(m1,m2,o1,""); } public SomeClass(String m1,String m2,String o1, String o2) { this(m1,m2,o1,o2,0); }
4 public SomeClass(String m1,String m2,String o1,String o2, int o3) { this(m1,m2,o1,o2,o3,0); } public SomeClass(String m1,String m2,String o1, String o2, int o3,int o4) { mandatory1=m1; mandatory2=m2; optional1=o1; optional2=o2; optional3=o3; optional4=o4; } public SomeClass(String m1,String m2,String o1,String o2, int o3) { this(m1,m2,o1,o2,o3,0); } public SomeClass(String m1,String m2,String o1, String o2, int o3,int o4) { mandatory1=m1; mandatory2=m2; optional1=o1; optional2=o2; optional3=o3; optional4=o4; }
5 Essence public class SomeClass { private String mandatory1; private String mandatory2; private String optional1; private String optional2; private int optional3; private int optional4; String getMandatory1() {return mandatory1;} //... String getOptional1() {return optional1;} //... private SomeClass(Builder b) { mandatory1 = b.mandatory1; mandatory2 = b.mandatory2; optional1 = b.optional1; optional2 = b.optional2; optional3 = b.optional3; optional4 = b.optional4; } public class SomeClass { private String mandatory1; private String mandatory2; private String optional1; private String optional2; private int optional3; private int optional4; String getMandatory1() {return mandatory1;} //... String getOptional1() {return optional1;} //... private SomeClass(Builder b) { mandatory1 = b.mandatory1; mandatory2 = b.mandatory2; optional1 = b.optional1; optional2 = b.optional2; optional3 = b.optional3; optional4 = b.optional4; }
6 public static class Builder { private final String mandatory1; private final String mandatory2; private String optional1 = ""; private String optional2 = ""; private int optional3 = 0; private int optional4 = 0; public Builder(String m1, String m2) { this.mandatory1 = m1; this.mandatory2 = m2; } public Builder optional1(String o) { this.optional1 = o; return this; } public SomeClass create() { return new SomeClass(this); } public static class Builder { private final String mandatory1; private final String mandatory2; private String optional1 = ""; private String optional2 = ""; private int optional3 = 0; private int optional4 = 0; public Builder(String m1, String m2) { this.mandatory1 = m1; this.mandatory2 = m2; } public Builder optional1(String o) { this.optional1 = o; return this; } public SomeClass create() { return new SomeClass(this); }
7 Builder ● Tworzenie obiektu tak naprawde składa się z dwu etapów: ● Stworzenie obiektu ● Zainicjalizowania go ● Obie te funkcje powinien wykonywac konstruktor, ale ● Jeśli obiekt jest skomplikowany, to można rozważyć stworzenie osobnego obiektu do jego budowy ● W najprostszej postaci Budowniczy to dowolny obiekt który służy do konstruowania innych obiektów
8 ● Obiekt Builder umożliwia ● uproszczenie kodu tworzącego złożone obiekty ● odzielenie kodu klienta od kodu tworzonego obiektu ● tworzenie różnych reprezentacji tego samego obiektu
9 Builder ● Chcemy napisać prosty parser dokumentów XML ● mający możliwość tworzenia różnych postaci dokumentu ● Wykorzystamy model SAX ● sterowanie zdarzeniami ● parser rozponaje elementy XML i wywołuje przekazane mu przez klienta funkcje: ● mogą to być metody klasy Builder ● Możemy np: ● wypisać sformatowany tekst ● Przekształcić test do formatu DOM
10 enum TOKEN {CHAR,START_TAG,END_TAG,END_OF_FILE,ERROR}; class token { TOKEN _token; char _char; std::string _name; token() {}; token(TOKEN t):_token(t) {}; void set_type(TOKEN t) { _token=t;} void set_char(char c) { _char=c;} void set_name(std::string n) {_name=n;} public: TOKEN get_type() {return _token;} char get_char() {return _char;} std::string get_name() {return _name;} friend class parser; }; enum TOKEN {CHAR,START_TAG,END_TAG,END_OF_FILE,ERROR}; class token { TOKEN _token; char _char; std::string _name; token() {}; token(TOKEN t):_token(t) {}; void set_type(TOKEN t) { _token=t;} void set_char(char c) { _char=c;} void set_name(std::string n) {_name=n;} public: TOKEN get_type() {return _token;} char get_char() {return _char;} std::string get_name() {return _name;} friend class parser; };
11 class parser { builder *_builder; token next_token(std::istream&) ; public: parser(builder *b):_builder(b){}; void parse(std::istream &in); }; class parser { builder *_builder; token next_token(std::istream&) ; public: parser(builder *b):_builder(b){}; void parse(std::istream &in); };
12 Director void parser::parse(std::istream &in) { while(1) { token t=next_token(in); switch(t.get_type()) { case END_OF_FILE: _builder->put_eof(); goto end; case START_TAG: _builder->put_start_tag(t.get_name()); break; case END_TAG: _builder->put_end_tag(t.get_name()); break; case CHAR: _builder->put_char(t.get_char()); break; } end:; } void parser::parse(std::istream &in) { while(1) { token t=next_token(in); switch(t.get_type()) { case END_OF_FILE: _builder->put_eof(); goto end; case START_TAG: _builder->put_start_tag(t.get_name()); break; case END_TAG: _builder->put_end_tag(t.get_name()); break; case CHAR: _builder->put_char(t.get_char()); break; } end:; }
13 Builder class builder { public: virtual void put_char(char c) {}; virtual void put_start_tag(std::string name){}; virtual void put_end_tag(std::string name){}; virtual void put_eof() {}; }; class builder { public: virtual void put_char(char c) {}; virtual void put_start_tag(std::string name){}; virtual void put_end_tag(std::string name){}; virtual void put_eof() {}; }; Funkcja parse korzysta z interfejsu klasy builder Nie jest to klasa abstrakcyjna. Implementuje wszystkie metody jako puste.
14 class to_text: public builder { bool tag_read; public: to_text():tag_read(false){}; virtual void put_char(char c) { if(!(tag_read && c=='\n')) std::cout.put(c); tag_read=false; }; virtual void put_start_tag(string name){tag_read=true;}; virtual void put_end_tag(string name) {tag_read=true;}; }; class to_text: public builder { bool tag_read; public: to_text():tag_read(false){}; virtual void put_char(char c) { if(!(tag_read && c=='\n')) std::cout.put(c); tag_read=false; }; virtual void put_start_tag(string name){tag_read=true;}; virtual void put_end_tag(string name) {tag_read=true;}; };
15 main() { builder *b = new to_text; parser p(b); p.parse(std::cin); } main() { builder *b = new to_text; parser p(b); p.parse(std::cin); }
16 DOM ● Document Object Model ● zapisuje document XML jako drzewo ● My wykorzystamy wzorzec kompozyt node element text
17 class node { public: virtual void print() = 0; }; class text : public node { std::string _content; public: void append(char c){_content.append(1,c);} void print() { cout
18 class element :public node { std::string _name; std::list _content; public: element(std::string name):_name(name) {}; void add(node *n) {_content.push_back(n);} void print() { std::list ::iterator it = _content.begin(); cout "; for(;it!=_content.end();++it) (*it)->print(); cout "; } }; class element :public node { std::string _name; std::list _content; public: element(std::string name):_name(name) {}; void add(node *n) {_content.push_back(n);} void print() { std::list ::iterator it = _content.begin(); cout "; for(;it!=_content.end();++it) (*it)->print(); cout "; } };
19 class dom_builder: public builder { std::stack _elements; text *_current_text; element *current_element() {return _elements.top();} element *_document; public: dom_builder():_current_text(0),_document(new element("document")) { _elements.push(_document); }; virtual void put_char(char c); virtual void put_start_tag(std::string name); virtual void put_end_tag(std::string name); virtual void put_eof(); node *document() {return _document;} }; class dom_builder: public builder { std::stack _elements; text *_current_text; element *current_element() {return _elements.top();} element *_document; public: dom_builder():_current_text(0),_document(new element("document")) { _elements.push(_document); }; virtual void put_char(char c); virtual void put_start_tag(std::string name); virtual void put_end_tag(std::string name); virtual void put_eof(); node *document() {return _document;} };
20 void dom_builder::put_char(char c) { if(!_current_text) { _current_text = new text(); current_element()->add(_current_text); } _current_text->append(c); }; void dom_builder::put_start_tag(std::string name) { _current_text=0; element *e=new element(name); current_element()->add(e); _elements.push(e); }; void dom_builder::put_end_tag(std::string name) { _current_text=0; _elements.pop(); }; void dom_builder::put_eof() { _elements.pop(); }; void dom_builder::put_char(char c) { if(!_current_text) { _current_text = new text(); current_element()->add(_current_text); } _current_text->append(c); }; void dom_builder::put_start_tag(std::string name) { _current_text=0; element *e=new element(name); current_element()->add(e); _elements.push(e); }; void dom_builder::put_end_tag(std::string name) { _current_text=0; _elements.pop(); }; void dom_builder::put_eof() { _elements.pop(); };
21 main() { builder *b = new dom_builder; parser p(b); node *doc=dynamic_cast (b)->document(); p.parse(std::cin); doc->print(); } main() { builder *b = new dom_builder; parser p(b); node *doc=dynamic_cast (b)->document(); p.parse(std::cin); doc->print(); }
22 ● We wzorcu biorą udział: ● Klient (funkcja main) ● Klient konfiguruje Zarządcę odpowiednim Budowniczym ● Klient rząda od zarządcy budowy Produktu ● Zarządca ● Zarządca kieruje procesem budowy, oddelegowując jej poszczególne skladowe/etapy do budowniczego ● Budowniczy ● Wykonuje poszczególne etapy konstrukcji ● Produkt ● Jest budowany
23 ● Zarządca (parser) używa interfejsu buildera (builder) ● Zarządca nie wie jaki produkt tworzy! ● zwykły tekst ● Kompozyt ● Klient wie jaki rodzaj produktu zarządał
24 ● Samo odzielenie i uproszczenie kodu jest wystarczającym powodem użycia Buildera ● np DOMBuilder ● Groovy i Ruby mja mnostwo roznych Builderów
25 Dekorator
26 Widget GuzikEtykieta
27 Widget GuzikEtykieta GuzikZRamkaEtykietaZRamka
28 Widget GuzikEtykieta GuzikZRamkaEtykietaZRamka GuzikLogującyEtykietaLogujaca
29 Widget GuzikEtykieta GuzikZRamkaEtykietaZRamka GuzikLogującyEtykietaLogujaca GuzikLogujacyZRamką
30 Dekorator Widget GuzikEtykietaDekoratorRamka DekoratorLogujacy
31 Dekorator ● Dekorator pozwala dodać dadatkowe funkcje do obiektu w sposób bez używania dziedzieczenia ● Dekorator przekazuje żądanie do obiektu dekorowanego wykonując przed i/lub po dodatkowe działania Component Dekorator ConcreteComponent
32 Logging decorator class GraphicsShape { }; class LogingShape extends GraphicsShape { GraphicsShape shape_; LogingShape(Graphics shape) {shape_=shape;} public void draw() { System.out.println(“draw “); shape_.draw(); }..... } class GraphicsShape { }; class LogingShape extends GraphicsShape { GraphicsShape shape_; LogingShape(Graphics shape) {shape_=shape;} public void draw() { System.out.println(“draw “); shape_.draw(); }..... }
33 jSlider1:JSliderjSlider2:JSlider model:BoundedRangeModel
34 jSlider1:JSliderjSlider2:JSlider model1:BoundedRangeModelmodel2:BoundedRangeModel
35 jSlider1:JSliderjSlider2:JSlider model1:BoundedRangeModel model2: ReverseBoundedRangeModel Decorator
36 BoundedRangeModel DefaultBoundedRangeModel ReverseBoundedRangeModel Decorator
37 public class ReverseBoundedModelDecorator implements BoundedRangeModel { private BoundedRangeModel model; public ReverseBoundedModelDecorator(BoundedRangeModel modela) { model = modela; } public void addChangeListener(ChangeListener listener){ model.addChangeListener(listener); } public int getExtent() { return model.getExtent(); } // w sumie 9 metod //... public class ReverseBoundedModelDecorator implements BoundedRangeModel { private BoundedRangeModel model; public ReverseBoundedModelDecorator(BoundedRangeModel modela) { model = modela; } public void addChangeListener(ChangeListener listener){ model.addChangeListener(listener); } public int getExtent() { return model.getExtent(); } // w sumie 9 metod //...
38 public int getValue() { return model.getMaximum() - (model.getValue() - model.getMinimum()); } public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting){ model.setRangeProperties(model.getMaximum()-(value- model.getMinimum()),extent,min,max,adjusting); } public void setValue(int v) { model.setValue(model.getMaximum()-(v-model.getMinimum())); } public int getValue() { return model.getMaximum() - (model.getValue() - model.getMinimum()); } public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting){ model.setRangeProperties(model.getMaximum()-(value- model.getMinimum()),extent,min,max,adjusting); } public void setValue(int v) { model.setValue(model.getMaximum()-(v-model.getMinimum())); }
39 BoundedRangeModel DefaultBoundedRangeModel BoundedRangeModel Decorator ReverseBoundedRangeModel Decorator
40 Dynamic Proxy -- Java ● Dekorator oddelegowywuje wykonanie większości metod do obiektu dekorowanego ● To może być bardzo pracochłonne ● Można użyć operator -> w C++ ● W Javie istnieje DynamicProxy która umożliwia dynamiczną implementację wzorca Decorator
41 $Proxy.??? java.lang.reflect.Proxy BoundedRangeModel > InvocatioHandler > AlarmInvocationHandler DefaultBoundedRange Model
42 Proxy newProxyInstance bmr:$Proxy.??? AlarmDecorato r h:AlarmInvHandler
43 bmr:$Proxy.??? h:AlarmInvocation H invoke(bmr, „setValue”,10) setValue(10) model:DefaultBRM setValue(10)
44 public class AlarmDecorator { static class AlarmInvocationHandler implements InvocationHandler { private BoundedRangeModel model; public AlarmInvocationHandler (BoundedRangeModel modela) { model = modela;} public void alarm() { System.out.println("Beebbeep!");} public void check(int value) { if(value>=model.getMaximum() || value=model.getMaximum() || value
45 public Object invoke(Object proxy, Method method, Object[] args) { Object obj=null; try { String name=method.getName(); if(name=="setValue" || name=="setRangeProperties") { Integer value=(Integer)args[0]; check(value); } obj = method.invoke(model, args); } catch... return obj; } public Object invoke(Object proxy, Method method, Object[] args) { Object obj=null; try { String name=method.getName(); if(name=="setValue" || name=="setRangeProperties") { Integer value=(Integer)args[0]; check(value); } obj = method.invoke(model, args); } catch... return obj; }
46 public static BoundedRangeModel create(BoundedRangeModel model) { BoundedRangeModel brm=null; try { InvocationHandler handler = new AlarmInvocationHandler(model); Class proxyClass = Proxy.getProxyClass( BoundedRangeModel.class.getClassLoader(), new Class[]{BoundedRangeModel.class}); brm = (BoundedRangeModel) proxyClass.getConstructor(new Class[]{InvocationHandler.class}). newInstance(new Object[]{handler}); } catch //... return brm; } public static BoundedRangeModel create(BoundedRangeModel model) { BoundedRangeModel brm=null; try { InvocationHandler handler = new AlarmInvocationHandler(model); Class proxyClass = Proxy.getProxyClass( BoundedRangeModel.class.getClassLoader(), new Class[]{BoundedRangeModel.class}); brm = (BoundedRangeModel) proxyClass.getConstructor(new Class[]{InvocationHandler.class}). newInstance(new Object[]{handler}); } catch //... return brm; }