DESIGN PATTERNS Factory, Abstract Factory, Singleton and Adapter Dr Simon Spacey B.Sc. Hons. (York), M.Sc. (Lancaster), M.B.A. (Cass), J.L.P. (Keio), D.E.A. (Montpellier), D.CSC. (Cambridge), D.I.U. (Imperial), Ph.D. (Imperial) © Simon Spacey. 20/05/2013 <1> What are Design Patterns? ¡ Design Patterns: ¡ Are Ways to Solve OOL Implementation Problems... ¡ ... that often have Trivially Obvious “Imperative” Solutions ¡ Popularised by the 1995 Book by Erich Gamma et al. ¡ Are often Supported by Libraries in OOLs ¡ Design Patterns Include: ¡ Creational: Factory, Abstract Factory, Builder Prototype, Singleton ¡ Structural: Adapter (DAO), Bridge, Composite, Decorator, Façade, Flyweight, Proxy E. Gamma, R. Helm, R. Johnson, J. Vlissides, Design Patterns, Addison Wesley, 1995 ¡ Behavioural: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer (MVC), State, Strategy, Template, Visitor © Simon Spacey. 18/04/2013 <2> Factory Method: Overview ¡ The Factory Design Pattern Uses Overridable Factory Methods to Allow Code to Instantiate Different Subclasses: i.e can implement runtime ¡ Reducing the Need to Embed a Variety of Concrete Classes! “Dependency Injection” ¡ Notes: ¡ A Factory Method can be Parameterised to Return Different Subclass Instances ¡ Factory Methods can be Used Internally in the Same a Class or in Abstract Factories ¡ Positives and Negatives: ü Isolates Concrete Class Names from Usages (Clients)! ü Makes Exchanging Classes Easier ü Promotes Consistency (Subclasses can have Factories for Related Classes) ✘ Subclass Specific Operations May Require Runtime Dependent Casting Spacey. ¡ © Simon ! 18/04/2013 <3> Factory Method: UML GameEngine GameEngine Pill #newPill() : Pill : Pill #newPill() +initialise() +initialise() Called by the Internal Method initialise() #newPill() : Pill : Pill #newPill() A UML note Pill3 Pill3 GameEngine3 GameEngine3 GameEngine4 GameEngine4 #newPill() : Pill : Pill #newPill() +newInstance() : Pill : Pill +newInstance() GameEngine2 Pill1 Pill1 GameEngine2 returnreturn new Pill2(x, y); y); new Pill2(x, #newPill() : Pill +newInstance() : Pill : Pill #newPill() : Pill +newInstance() GameEngine1 GameEngine1 #newPill() : Pill : Pill #newPill() returnreturn new Pill4(x, y); y); new Pill4(x, An Example UML Diagram for the Internal Factory Method Design Pattern © Simon Spacey. 18/04/2013 <4> Pill Pill2 Pill2 returnreturn new Pill2(x, y); y); new Pill2(x, +newInstance() : Pill +newInstance() : Pill Pill4 Pill4 +newInstance() : Pill : +newInstance() : Pill : Pill +newInstance() Pill +newInstance() returnreturn new Pill4(x, y); y); new Pill4(x, An Example UML Diagram of the External Factory Method Design Pattern Factory Method: Internal Example ¡ Before: ¡ After: ! ! ...! ...! ! ! ! // Class specialized so newPill() returns correct type! protected Pill newPill(int x, int y) {! return new Pill1(x, y);! }! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise() {! ! ! ! ! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise() {! ! int style_id = 1; ! ! ! ! ...! Switch(style_id) {! case 1: displayList.add(new case 2: displayList.add(new case 3: displayList.add(new case 4: displayList.add(new }! ...! ! ...! Pill1(x, Pill2(x, Pill3(x, Pill4(x, y)); y)); y)); y)); break;! break;! break;! break;! ! displayList.add(newPill(x, y)); // internal factory! ! ! ...! ! }! }! ! ...! ! ...! ! 18/04/2013 Would be the same method inherited by each subclass ! ! © Simon Spacey. Can lead to Many Subclasses Each with a Small Change <5> Factory Method: External Example ¡ Before: ¡ After: ! ! ...! ...! ! ! ! ! ! ! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise() {! Change Hard Coded Class Names in One Place ! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise() {! ! int style_id = 1; ! ! ...! Switch(style_id) {! case 1: displayList.add(new case 2: displayList.add(new case 3: displayList.add(new case 4: displayList.add(new }! ...! Pill pill = new Pill1(); // factory in external class! ! ...! ! Pill1(x, Pill2(x, Pill3(x, Pill4(x, y)); y)); y)); y)); ! break;! break;! break;! break;! displayList.add(pill.newInstance(x, y));! ! ! ...! ! }! ! ! }! ! ...! © Simon Spacey. ...! ! 18/04/2013 <6> Rest of the code is generic Abstract Factory: Overview ¡ The Abstract Factory Design Pattern Provides an Abstract Interface for Instantiating Related Classes: ¡ Further Reducing the Need to Embed Concrete Classes! ¡ Notes: ¡ The Abstract Factory Base may be an Interface or Defaulted ¡ Positives and Negatives: ü Isolates Concrete Class Names from Usages (Clients)! ü Makes Exchanging Product Families Easy (Single Usage) ü Promotes Consistency (Products from Same Factory) ✘ Supporting new Products Means a New Interface ✘ Subclass Specific Operations May Require Runtime Dependent Casting © Simon Spacey. 18/04/2013 <7> Abstract Factory: UML Interface, Abstract Class or Insatiable Default Parent Style +newPill() : Pill +newPowerPill() : PowerPill +newGhost() : Ghost +newCherry() : Cherry Concrete Subclass/ Specialism with Factory Methods Style1 Style2 +newPill() : Pill +newPowerPill() : PowerPill +newGhost() : Ghost +newCherry() : Cherry +newPill() : Pill +newPowerPill() : PowerPill +newGhost() : Ghost +newCherry() : Cherry GameEngine3 Style3 return new Pill2(x, y); Style4 return new Pill4(x, y); +newPill() #newPill() : Pill: Pill +newPowerPill() : PowerPill +newGhost() : Ghost +newCherry() : Cherry +newPill() : Pill +newPowerPill() : PowerPill +newGhost() : Ghost +newCherry() : Cherry An Example UML Diagram for the Abstract Factory Design Pattern © Simon Spacey. 18/04/2013 <8> Abstract Factory: Example ¡ Before: ¡ After: ! ! ! ...! ...! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise(int style_id) {! ! ...! switch(style_id) {! case 1: displayList.add(new Pill1(x, y)); break;! case 2: displayList.add(new Pill2(x, y)); break;! case 3: displayList.add(new Pill3(x, y)); break;! case 4: displayList.add(new Pill4(x, y)); break;! }! ...! ! ...! switch(style_id) {! case 1: displayList.add(new Ghost1(x, y)); break;! case 2: displayList.add(new Ghost2(x, y)); break;! case 3: displayList.add(new Ghost3(x, y)); break;! case 4: displayList.add(new Ghost4(x, y)); break;! }! ...! }! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise(Style style) {! ! ...! ...! © Simon Spacey. ! ! displayList.add(style.newPill(x, y));! ! ! ! ...! ! ! ...! ! ! displayList.add(style.newGhost(x, y));! ! ! ...! }! ! ! 18/04/2013 <9> ...! Singleton: Overview ¡ The Singleton Design Pattern Uses Static State to Ensure Only a Single Instance of a Class Exists: ¡ Useful for creating a “global” state variable in OOP! ¡ Notes: ¡ A Single (Static) Instance is Returned by an Access Method ¡ Positives and Negatives: ü Controlled Access to a Single Instance (e.g. could ensure single user at a time)! ü Allows Class Namespace Encapsulation (c.f. C statics) rather than Global Instances ü Allows an Instance Pool (can return 1 of many possible internal instances) ✘ Additional Mechanisms Required to Support Synchronization ✘ Have to call a Non-Standard Method Rather than Using the Standard Constructor © Simon Spacey. 18/04/2013 <10> Singleton: UML Global -style : Style -score : unsigned int -pills_left : unsigned int -lives_left : unsigned int +getStyle() : Style +getScore() : unsigned int +incScore() : unsigned int +getPills() : unsigned int +decPills() : unsigned int +getLives() : unsigned int +decLives() : unsigned int Underlined means “static” in UML An Example UML Diagram for the Singleton Design Pattern © Simon Spacey. 18/04/2013 <11> Public Access Methods return Static Instances Singleton: Example ¡ Before: ¡ After: ! ! "! public class Global {! ! Initialising style to a single instance on first Global class load in JVM to prevent thread issues ! ! private static Style style = new Style1();! ! ! ! public static Style getStyle() {return style;}! ! ! ! ...! ! }! ! ! ! ! ! ! ...! ...! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise(Style style) {! ! "! // Initialises the Pacman Pills, Power Pills and Ghosts! public void initialise() {! ! Style style = Global.getStyle(); // note statics in defs! ! ! ! ! ...! displayList.add(style.newPill(x, y));! ...! displayList.add(style.newGhost(x, y));! ...! ! }! ...! displayList.add(style.newPill(x, y));! ...! displayList.add(style.newGhost(x, y));! ...! }! ! ...! © Simon Spacey. 18/04/2013 <12> ...! ! Adapter (DAO): Overview ¡ The Adapter Pattern Wraps One or More Classes with A Required Interface: ¡ Useful for Constructing say a standard Data Access Object (DAO) interface ¡ Notes: ¡ Can be an Abstract or a Concrete Class with Embedded “Composed-of” Relations ¡ Positives and Negatives: ü Decouples Interface from Implementation ü Client Code Stable if Wrapped Classes Changes (change Adapter Internally) ü The Implementation and Adapter can be Subclassed Separately (//el Hierarchies) ü Can Hide/ Share Implementations (e.g. Compiled Wrapper Libraries) ✘ Extra Layer of Programming Indirection (Code Complication) © Simon Spacey. 18/04/2013 <13> Adapter (DAO): UML Static compositions DAO DriverManager -c : Connection -s : Statement -r : ResultSet 1 +getHighScore() : unsigned int +setHighScore() 0..1 +getConnection() : Connection 1 0..1 1 1 +createStatement() : Statement 0..1 Adapter Methods “Wrap” Composed and Associated Methods An Associated Class (but not composed) Connection Statement +executeQuery() : ResultSet 0..1 ResultSet +next() : bool +getInt() : int +getString() : String An Example UML Diagram for the Adapter (DAO) Design Pattern © Simon Spacey. 18/04/2013 <14> Adapter (DAO): Example ¡ Before: ¡ After: ! ! import java.sql.*;! ! ...! ! import java.sql.*;! ! ...! ! ! ! //*** Simple Adapter between JDBC and the Application! public class DAO {! ! ! ! private static Connection c = null;! private static Statement s = null;! private static RecordSet r = null;! ! ! ! ! ! ! // Prints the High Score! public void printHighScore() {! Connection c = DriverManager.getConnection(! "jdbc:mysql://localhost:3306/pacman",! "packman", "password");! Statement s = c.createStatement();! ResultSet r = s.executeQuery(! "SELECT MAX(score) FROM scores");! unsigned int hc = (r.next() ? r.getInt(1) : 0);! s.close(); c.close(); r.close();! Console.out.println("High Score: " + hc); }! ...! © Simon Spacey. 18/04/2013 <15> ! }! Why synchronized if reading? ...! public synchronized unsigned int getHighScore() {! if(c == null) DAO.initialise();! r = s.executeQuery("SELECT MAX(score) FROM scores");! return (r.next() ? r.getInt(1) : 0);! }! ...! ! ! ...! // Prints the High Score! public void printHighScore() {! Console.out.println("High Score: " + DAO.getHighScore());! }! ... Summary ¡ Design Patterns: ¡ Are Ways to Solve OOL Implementation Problems... ¡ ... that often have Trivially Obvious “Imperative” Solutions ¡ Popularised by the 1995 Book by Erich Gamma et al. ¡ Are often Supported by Libraries in OOLs ¡ Design Patterns Include the: ¡ Factory Method Pattern – Uses a Method to Construct Instances Instead of new! ¡ Abstract Factory Pattern – Provides an Interface with a Collection of Factory Methods ¡ Singleton Pattern – Uses Static State to Ensure Only One Instance Exists ¡ Adapter (DAO) Pattern – Wraps a Required Interface around One or More Classes © Simon Spacey. 18/04/2013 <16>
© Copyright 2024