3.5. Prototype 1. pattern name and classification/numele şablonului şi clasificarea: Prototype, obiect 2. intent/scop: specifică tipurile de obiecte care se creează prin folosirea unei instanţe prototip, şi creează noile obiecte prin copierea acestui prototip. 3. also known as (AKA)/alte nume: nu are 4. motivation/motivaţie: editor pt partituri muzicale cadru general de editor grafic obiecte specifice pt note, pauze, durate paletă de instrumente pt gestiunea obiectelor muzicale pe portativ instrumente de selectare, mutare, manipulare clasele Staff, MusicalNote aparţin soluţiei clasa GraphicTool aparţine cadrului nu ştie de existenţa claselor soluţiei cum se poate parametriza GraphicTool ca să creeze instanţe ale claselor de obiecte din soluţie? (I): se derivează din GraphicTool câte o subclasă pt fiecare clasă de obiecte din soluţie explozie de subclase care diferă între ele doar prin tipul de obiect instanţiat (II): GraphicTool creează o nouă instanţă a lui Graphic prin copierea (clonarea) unei instanţe (prototip) a unei subclase a lui Graphic clasa GraphicTool este parametrizată cu prototipul care trebuie să-l cloneze şi apoi să-l adauge la document dacă toate subclasele lui Graphic au o metodă clone(), atunci GraphicTool va putea clona orice tip de obiect Graphic 2/18/2020 Design Patterns - 03 05 Prototype 3 3.5. Prototype 5. applicability/aplicabilitate: şablonul Prototype se foloseşte când sistemul trebuie să fie independent de modul în care sunt create, compuse şi reprezentate produsele şi când clasele ce trebuie instanţiate sunt specificate la execuţie, de exemplu prin încărcare dinamică sau se doreşte evitarea construirii unei ierarhii de clase de fabrici care este în paralel cu ierarhia de clase de produse sau când instanţele unei clase pot avea numai una din câteva (puţine) combinaţii diferite de stare. de regulă este mai convenabilă instalarea unui număr corespunzător de prototipuri şi clonarea lor decât instanţierea manuală a claselor, de fiecare dată cu starea corespunzătoare. 6. structure/structură: 7. participants/participanţi: Prototype (Graphic): interfaţă ce declară o metodă pentru clonarea obiectelor ConcretePrototype (Staff, WholeNote, HalfNote) PMWidgetFactory): clase ce implementează operaţiile de clonare a obiectelor proprii Client (GraphicTool): clasă care creează obiecte noi cerând prototipului să se cloneze 8. collaborations/colaborări: Client-ul cere lui Prototype să se cloneze 9. consequences/consecinţe: avantajele de la AbstractFactory şi Builder ascunde clientului clasele produs concrete, reducând numărul de nume pe care trebuie să le cunoască clientul nu trebuie să modifice clasele specifice aplicaţiei 2/18/2020 Design Patterns - 03 05 Prototype 4 3.5. Prototype 9. consequences/consecinţe: avantaje proprii (1) adăugarea/ştergerea dinamică a produselor clasele produs noi trebuie înregistrate la client - o instanţă prototip a lor prototipurile se pot înregistra şi şterge la execuţie (2) specificarea de obiecte noi prin modificarea valorilor definirea de comportament nou prin compunerea obiectelor (specificarea valorilor pt variabilele de stare), nu prin definirea de clase noi noile obiecte se definesc prin instanţierea claselor existente şi înregistrarea acestora ca prototipuri ale obiectelor client clientul poate expune comportament nou prin delegarea responsabilităţilor la prototip (3) specificarea de obiecte noi prin modificarea structurii obiectele complexe se construiesc din piese şi subansambluri circuitele integrate se construiesc din subcircuite utilizatorul (clientul) poate defini structuri noi, în care se refolosesc subcircuitele existente subcircuitul (circuitul compus) se adaugă ca prototip la o paletă de elemente de circuit disponibile (de bază) dacă circuitul compus implementează metoda clone, folosind copierea în adâncime, circuitele cu structură diferită pot fi şie ele prototipuri (4) reducerea numărului de subclase la Factory Method: ierarhii de clase paralele: Creator şi Product la Prototype: nu mai este nevoie de ierarhia de clase Creator (se clonează prototipul în locul apelului de metode fabrică) (5) configurarea dinamică cu clase a unei aplicaţii fapte: clase Product ce se încarcă dinamic într-o aplicaţie aplicaţia care foloseşte instanţe de Product nu poate referi static constructorii acestora mediul de execuţie creează câte o instanţă a fiecărei clase, la încărcarea dinamică a acesteia instanţa este înregistrată în managerul de prototipuri aplicaţia cere managerului de prototipuri să creeze instanţe ale noilor clase, ce nu sunt legate deloc de programul sursă dezavantaje fiecare subclasă a lui Prototype trebuie să implementeze metoda clone este dificilă adăugarea unei metode clone la clase existente deja când unele variabile de stare nu suportă clonarea când în modelul obiect există referinţe circulare
2/18/2020 Design Patterns - 03 05 Prototype 5
3.5. Prototype 10. implementation/implementare: aspecte de discutat (1) folosirea unui manager de prototipuri fapte variantă recomandabilă când numărul de prototipuri din sistem nu este fixat constă dintr-o listă ce conţine prototipurile respective clienţii memorează/regăsesc prototipurile dorite înainte de clonare, clientul cere managerului prototipul dorit implementare memorie asociativă - listă de perechi (prototip, cheie) operaţii void register(prototip, cheie) void unregister(cheie) prototip find(cheie) navigare etc (2) implementarea operaţiei clone() cea mai dificilă mai ales când structurile de obiecte conţin referinţe circulare dependenţe de limbaj Smalltalk: implementare a operaţiei copy moştenită de toate subclasele - copiere la suprafaţă C++: constructorul de copiere - implicit copiere la suprafaţă Java: metoda Object.clone(), interfaţa Cloneable, constructorul de copiere variante de copiere copiere la suprafaţă (shallow copy): toate variabilele de instanţă sunt considerate de tipuri valoare variabilele obiect sunt partajate între prototip şi clonele acestuia copiere în adâncime (deep copy): se clonează variabilele de instanţă de tipuri referinţă clona şi prototipul sunt total independente: componentele clonei sunt clone ale componentelor prototipului (3) iniţializarea clonelor constructor iniţial (fără parametri) operaţii setter operaţia initialize iniţializează starea internă a clonei 2/18/2020 Design Patterns - 03 05 Prototype 6 3.5. 11. sample code/cod sursă exemplu: Prototype (b) public class House implements Cloneable { soluţia folosind şablonul Prototype // constructor initial (a) clasa HousePrototypeFactory public House() {} are prototipuri pentru fiecare tip de obiecte // constructor de copiere care le creează (House, Room, Wall, Door) public House(House h) { rooms = h.rooms; } defineşte metode fabrică pentru obiectele // initializare public void initialize() { rooms = new ArrayList();} House, Room, Wall şi Door // metoda clone (b,c) clasele House şi Wall implementează public Object clone() { return new House(this); } suplimentar constructorul iniţial, de copiere, // celelalte metode metodele clone şi initialize public void addRoom(Room r) { rooms.add(r);} (a) public Room roomNo(int no) { public class HousePrototypeFactory { for (int i = 0; i < rooms.size(); i++) { public HousePrototypeFactory(House h, Room r = (Room)rooms.get(i); Wall w, Room r, Door d) { if (r.getRoomNumber() == no) prototypeHouse = h; return r; prototypeRoom = r; } prototypeWall = w; return null; prototypeDoor = d; } } public String toString() { public House makeHouse() { String s = new String("Casa are " + rooms.size() + House h = (House)prototypeHouse.clone(); " camere \n"); h.initialize(); for (int i=0; i < rooms.size(); i++) { return h; Room r = (Room)rooms.get(i); }; s = s + " " + r.toString(); public Room makeRoom(int n) { } Room r = (Room)prototypeRoom.clone(); return s; r.initialize(n); } return r; protected ArrayList rooms; } } public Wall makeWall() { (c) Wall w = (Wall)prototypeWall.clone(); public class Wall extends MapSite implements Cloneable{ w.initialize(); // constructor initial return w; public Wall() {} }; // constructor de copiere public Door makeDoor(Room r1, Room r2) { public Wall(Wall w) {} Door d = (Door)prototypeDoor.clone(); // initializare d.initialize(r1, r2, false); public void initialize() {} return d; // metoda clone }; public Object clone() { return new Wall(this); } private House prototypeHouse; // celelalte metode private Room prototypeRoom; public void Enter() {} ; private Wall prototypeWall; public String toString() { return "Perete"; } private Door prototypeDoor; } } 2/18/2020 Design Patterns - 03 05 Prototype 7 3.5. 11. sample code/cod sursă exemplu: Prototype (e) soluţia folosind şablonul Prototype public class Room extends MapSite (d,e) clasele Door şi Room implementează implements Cloneable { suplimentar constructorul iniţial, de copiere, // constructor initial public Room() { } metodele clone şi initialize // constructor de copiere (d) public Room (Room r) { public class Door extends MapSite // copiere de suprafata this.sides = r.sides; implements Cloneable{ this.roomNumber = r.getRoomNumber(); // constructor initial }; public Door() {} // initializare // constructor de copiere public void initialize(int roomNo) { public Door(Door d) { sides = new MapSite[4]; this.r1 = d.r1; roomNumber = roomNo; this.r2 = d.r2; } // metoda clone this.open= d.getOpen(); public Object clone() { }; return new Room(this); } // initializare // celelalte metode de la Ch3.Intro.Room public void initialize(Room r1, Room r2, public void Enter() {} ; boolean open) { public MapSite getSide(Direction d) { this.r1 = r1; return sides[d.getDirection()]; } this.r2 = r2; public MapSite getSide(int i) { return sides[i];} this.open = open; public void setSide(Direction d, MapSite m){ } sides[d.getDirection()] = m; }; // metoda clone public void setSide(int i, MapSite m) { public Object clone() { sides[i] = m; } return new Door(this); } public int getRoomNumber() { // celelalte metode de la Ch3.Intro.Door return roomNumber; } public void Enter() {} ; public String toString() { String s = new String("Camera nr. " + public void setOpen(boolean b) { open = b; } roomNumber + "\n"); public boolean getOpen() { return open; } for (int i=0; i < 4; i++) public String toString() { s = s.concat(" Directia " + return "Usa intre camerele " + Direction.DirectionString(i) + " " + r1.getRoomNumber() + sides[i].toString() + "\n"); " si " + r2.getRoomNumber(); return s; } } protected MapSite sides[]; protected Room r1, r2; protected int roomNumber; protected boolean open; } } 2/18/2020 Design Patterns - 03 05 Prototype 8 3.5. 11. sample code/cod sursă exemplu: Prototype (g) public class BrickHouse extends House { soluţia folosind şablonul Prototype // constructor initial (f) clasa Client foloseşte numai metodele fabrică public BrickHouse() {} definite în HousePrototypeFactory // constructor de copiere (g,h) subclasele lui House implementează public BrickHouse(BrickHouse h) { obligatoriu constructorul iniţial, de copiere, rooms = h.rooms; } metodele clone şi initialize // metoda clone public Object clone() { (f) return new BrickHouse(this); } public class Client { public String toString() { public static House String s = new String("Casa de caramida are "+ createHouse(HousePrototypeFactory hpf) { rooms.size() + " camere \n"); House aHouse = hpf.makeHouse(); for (int i=0; i < rooms.size(); i++) { Room r = (Room)rooms.get(i); Room r1 = hpf.makeRoom(1); s = s + " " + r.toString(); Room r2 = hpf.makeRoom(2); } Door theDoor = hpf.makeDoor(r1, r2); return s; aHouse.addRoom(r1); } aHouse.addRoom(r2); } r1.setSide(new Direction("North"), (h) hpf.makeWall()); public class WoodHouse extends House { r1.setSide(new Direction("East"), theDoor); // constructor initial r1.setSide(new Direction("South"), public WoodHouse() {} hpf.makeWall()); // constructor de copiere r1.setSide(new Direction("West"), public WoodHouse(WoodHouse h) {rooms = h.rooms;} hpf.makeWall()); // metoda clone r2.setSide(new Direction("North"), public Object clone() { hpf.makeWall()); return new WoodHouse(this); } r2.setSide(new Direction("East"), public String toString() { hpf.makeWall()); String s = new String("Casa de lemn are " + r2.setSide(new Direction("South"), rooms.size() + " camere \n"); hpf.makeWall()); for (int i=0; i < rooms.size(); i++) { r2.setSide(new Direction("West"), theDoor); Room r = (Room)rooms.get(i); return aHouse; s = s + " " + r.toString(); } } } return s; } } 2/18/2020 Design Patterns - 03 05 Prototype 9 3.5. 11. sample code/cod sursă exemplu: Prototype (j) public class BrickWall extends Wall { soluţia folosind şablonul Prototype // constructor initial public BrickWall() { } (i) subclasele lui Door implementează // constructor de copiere obligatoriu constructorul iniţial, de copiere, public BrickWall(BrickWall w) { } metodele clone şi initialize // metoda clone public Object clone() { return new BrickWall(this); } (j,k) subclasele lui Wall implementează public String toString() {return "PereteDeCaramida";} obligatoriu constructorul iniţial, de copiere, } metodele clone şi initialize (k)public class WoodWall extends Wall { // constructor initial (l,m,n) clasele Demo instanţiază public WoodWall() { HousePrototypeFactory cu prototipurile woodType = new String("nuc"); } adecvate // constructor de copiere public WoodWall(WoodWall w) {woodType = w.woodType;} (i)public class WoodDoor extends Door { // initializare // constructor initial public void initialize(String woodType){ public WoodDoor() {} this.woodType = woodType; } // constructor de copiere // metoda clone public WoodDoor(WoodDoor d) { public Object clone() { this.r1 = d.r1; return new WoodWall(this); } this.r2 = d.r2; public String getWoodType() { this.open= d.getOpen(); return woodType; } }; public String toString() { // metoda clone return "PereteDeLemn"+(woodType.length() != 0 public Object clone() { ? " de " + woodType : ""); } return new WoodDoor(this); } private String woodType; public String toString() { } return "UsaDeLemn intre camerele " + (m)public class BrickHouseDemo { r1.getRoomNumber() + public static void main(String[] args){ " si " + r2.getRoomNumber(); HousePrototypeFactory hpf = new } HousePrototypeFactory( } new BrickHouse(), new BrickWall(), (l) public class SimpleHouseDemo { new BrickRoom(), new Door()); public static void main(String[] House h = Client.createHouse(hpf); args){ TextIO.putln(”BrickHouseDemo vers.Prototype\n"); HousePrototypeFactory hpf = TextIO.putln(h.toString()); new HousePrototypeFactory( } } new House(), new Wall(), (n)public class WoodHouseDemo { new Room(), new Door()); public static void main(String[] args){ House h = Client.createHouse(hpf); HousePrototypeFactory hpf = TextIO.putln("SimpleHouseDemo new HousePrototypeFactory( versiunea Prototype\n"); new WoodHouse(), new WoodWall(), TextIO.putln(h.toString()); new WoodRoom(), new WoodDoor()); } ........................................ } 2/18/2020 Design Patterns - 03 05 Prototype 10 3.5. Prototype
2/18/2020 Design Patterns - 03 05 Prototype 11
3.5. Prototype 11. sample code/cod sursă exemplu: soluţia folosind şablonul Prototype - observaţii clasa HousePrototypeFactory conţine prototipurile (instanţe ale claselor House, Room, Wall, Door) implementarea metodelor fabrică pentru crearea componentelor din prototipuri apelează metodele clone şi initialize un constructor parametrizat cu instanţele tuturor tipurilor de prototipuri clasele componentelor casei - prototipurile (House, Room, Wall, Door) implementează obligatoriu constructorul iniţial: folosit la instanţierea HousePrototypeFactory constructorul de copiere: folosit la implementarea metodei clone metoda clone: apelată în metodele fabrică de la HousePrototypeFactory metoda initialize : apelată în metodele fabrică de la HousePrototypeFactory orice subclasă a claselor House, Room, Wall, Door trebuie să implementeze obligatoriu constructorii iniţiali şi de copiere metoda clone clientul (clasa Client, care implementează metoda createHouse) foloseşte (cunoaşte) numai clasa HousePrototypeFactory şi clasele prototipurilor - clasele de bază ale componentelor casei în Client se pot pune alte metode createHouse, pentru alte configuraţii de case clasa HousePrototypeFactory nu trebuie extinsă programele demo (DemoAll, SimpleHouseDemo, BrickHouseDemo, WoodHouseDemo) o instanţiază cu prototipurile adecvate de componente se pot crea instanţe de HousePrototypeFactory cu orice combinaţii posibile de prototipuri de componente, scriind cod numai în programul Demo
2/18/2020 Design Patterns - 03 05 Prototype 12
3.5. Prototype 12. known uses/utilizări cunoscute: istoric Sketchpad: A Man-Machine Graphical Communication System, Ivan Sutherland, PhD Thesis, MIT, 1963 ThingLab (Constraint-oriented simulation laboratory), A. Borning menţionat ca şablon de proiectare de Goldberg şi Robson (Smalltalk-80) descris complet de James Coplien (1992) exemple Etgdb - depanator simbolic sub ET++, ce oferă o interfaţă point-and-click pt diverse depanatoare mod linie de comandă fiecărui depanator îi corespunde o clasă DebuggerAdaptor corespunzătoare clasa GdbAdaptor adaptează etgdb la sintaxa comenzilor depanatorului GNU gdb clasa SunDbxAdaptor adaptează etgdb la sintaxa comenzilor depanatorului SUN dbx Etgdb nu are codificate hard clasele DebuggerAdaptor, ci (1) citeşte numele adaptorului curent dintr-o variabilă de mediu, (2) caută prototipul cu numele respectiv într-o tabelă globală (repository?) (3) clonează prototipul găsit Mode composer - biblioteca de tehnici de interacţiune (Shan, 1990) memorează prototipurile obiectelor ce suportă diverse tehnici de interacţiune într-o bibliotecă orice tehnică de interacţiune creată de Mode composer se poate folosi ca prototip prin plasarea sa în bibliotecă Unidraw: A framework for building domain-specific graphical editors (John M. Vlissides, Mark A. Linton, 1990). exemplul editorului de partituri muzicale din 3.5.4 13. related patterns/şabloane înrudite: AbstractFactory (vezi discuţia de la finalul cap 3) se poate folosi împreună cu Prototype - vezi codul sursă Prototype se foloseşte frecvent împreună cu Composite şi Decorator 2/18/2020 Design Patterns - 03 05 Prototype 13