CSC223 2004F, Class 38: Design Patterns (3): Creational Patterns Admin: * Cool talk tomorrow at 4:15 (2424); Extra credit for stumping Davis * Cool debate tonight at 7:00 p.m. (south lounge); Extra credit for attending * Keep reading "Design Patterns" Overview: * Using "Design Patterns" * Questions and Answers * Three (plus one) patterns for creating objects dynamically w/o using constructors + Abstract Factory + Factory Method + Prototype + (Classes as first-class objects) * Two other creation patterns (plus one) + Builder + Singleton + (Registry) /Using "Design Patterns"/ * Now: Familiarize yourself with the generalities of the patterns * Later: Remember which patterns apply * Continue with: Read the details! /Questions on the Creational patterns?/ * What's the purpose of Builder (repeated) * What's the difference between factory and abstract factory? /The Big Three/ * Abstract Factory, Factory Method, Prototype * A way to create objects (creational) * A dynamic thing: We can "dynamically" choose/specify which particular objects to create * Today we create objects of type A * Tomorrow we create objects of type B * Without changing the Client code that asks for objects to be created Abstract Factory (what we called Factory): * An abstract class (interface) specifies which operations can be called to create objects. * Concrete instantiations of the factory implement those operations. public interface BugFactory { public Bug makeBug(); } public class BugWorld { BugFactory factory; ... this.add(factory.makeBug()); } public class AntFactory implements BugFactory { public Bug makeBug() { return new Ant(); } } public class AntWorldGame { public static void main(String[] args) { BugWorld bw = new BugWorld(new AntFactory()): bw.run(); } } * Why are these called *abstract* factories? Because you start with an abstract class (an interface) /Factory Method/ * Why are these called factory *methods*? Because you focus on a method. public abstract class BugWorld { public void setup() { this.add(this.makeBug()); } public abstract Bug makeBug(); } To make ants, we override makeBug public class AntWorld extends BugWorld { public Bug makeBug() { return new Ant(); } } Prototype * The client receives a "prototypical" object * The client calls "clone" on that object when it needs new copies. public class BugWorld { Bug b; ... public BugWorld(Bug _b) { this.b = _b; setup(); } public void setup() { this.b1 = this.b.clone(); this.b2 = this.b.clone(); this.b3 = this.b.clone(); this.add(this.b.clone()); this.add(this.b.clone()); this.add(this.b.clone()); this.add(this.b.clone()); this.add(this.b.clone()); this.add(this.b.clone()); this.add(this.b.clone()); ... } public void run() { ... } } public class AntGame { public static void main(String[] args) { BugWorld bw = new BugWorld(new Ant()); bw.run(); } } What's the difference between this.b1 = _b.clone(); this.b2 = _b.clone(); this.b3 = _b.clone(); and this.b1 = _b; this.b2 = _b; this.b3 = _b; In the first case, b1, b2, and b3 refer to *different* objects; if b1 changes, b2 and b3 don't. In the second case, b1, b2, and b3 all refer to the *same* object; if b1 changes, b2 and b3 change identically The authors note that if "classes are first class objects", then you can use classes as prototypes. * If you can treat classes as values, including creating them. * In Java, you can pass classes, but not create them at runtime. Why would we use Abstract Factory vs. Factory Methods vs. Prototype vs. Classes as First Class Objects * Consider the least coding * Factory Method may be easiest * Factory Method causes proliferation of classes * In addition to needing a class for each kind of bug, we need a ???World for each kind of bug * Abstract Factory also causes proliferation * In addition to needing a class for each kind of bug, we need a ???Factory for each kind of bug * So ... Prototype may be best for many applications * Abstract Factory is the best if you need to create different but related things * Prototypes make it easiest to be dynamic. * Prototypes make it easiest to configure your objects. Set the color or personality of the bug once. Clones automatically get copies. * Factory methods are slightly faster and limit proliferation of objects Observation: It is possible to combine patterns public class PrototypicalBugFactory implements BugFactory { Bug this.b ; Eater this.e; public PrototypicalBugFactory(Bug _b, Eater _e) { this.b = _b; this.e = _e; } public Bug getBug() { this.b.clone(); } public Eater getEater() { this.e.clone(); } } public class AntGame { BugWorld bf = new BugWorld(new PrototypicalFactory(new Ant(), new Cat()); } /Other Things we May Create/ * Problem: Sometimes creation is complex enough that you can't just use a constructor. * Solution: Create an intermediate class (a Builder) that calls lots of methods on the thing that is built. * Return the value that is built. public BugWorld Builder builder; ... { builder.prepareToBuild(); builder.setColor("blue"); builder.setSize(Size.LARGE); Bug b = builder.get(); } public class SlimyWormBuilder public class AntBuilder public class JellyfishBuilder public class SquidBuilder public class CatBuilder /Singleton: Ensure only one copy is created/ public class AntFactory { private AntFactory() { } public AntFactory getFactory() { } ... } Why have only one copy of an object? * Share information or state * Beats global or static variables b/c you can pass it around Often when you want to share information, you should use a singleton.