Design Patterns Factory Method Oliver Haase 1
Idea If client knows when to create certain object(s), but doesn't know (neither care) how, then... ‣ make client an abstract class; ‣ define an abstract method for object creation; ‣ leave implementation to concrete subclass(es). Also known as virtual constructor . 2
DocManager Reloaded Originally: @ThreadSafe // assuming that LatexDoc is threadsafe public class DocManager { private final Collection<Document> docs; public DocManager() { docs = new ConcurrentLinkedQueue<Document>(); } public void createDoc() { Document doc = new LatexDoc(); docs.add(doc); doc.open(); } public void openDocs() { for ( Document doc : docs ) doc.open(); } ... } 3
DocManager Reloaded With factory method: @ThreadSafe // assuming that concrete Document is threadsafe public abstract class DocManager { private final Collection<Document> docs; public DocManager() { docs = new ConcurrentLinkedQueue<Document>(); } protected abstract Document newDocument(); public void createDoc() { Document doc = newDocument(); docs.add(doc); doc.open(); } public void openDocs() { for ( Document doc : docs ) doc.open(); } ... } 4
DocManager Reloaded Concrete subclass LatexDocManager : @ThreadSafe // assuming that LatexDoc is threadsafe public class LatexDocManager extends DocManager { @Override protected Document newDocument() { return new LatexDoc(); } } 5
Structure DocManager Document newDocument() open() createDoc() Document doc = close() openDocs() newDocument(); docs.add(doc); doc.open(); LatexDocManager LatexDoc newDocument() open() return close() new LatexDoc(); 6
Structure - in General ‣ defines factory method superclass/interface of ‣ uses factory method to create products products to be created ‣ may provide default implementation Creator Product factoryMethod() use() someOperation() Product product = factoryMethod(); product.use(); ConcreteCreator ConcreteProduct factoryMethod() use() return new ConcreteProduct(); implementation of implements factory concrete product method such that concrete product gets created 7
Comparison: Starting Point Client Product someOperation() use() Product product = new ConcreteProduct(); ConcreteProduct product.use(); use() 8
Misc ‣ Pro : Simple technique to decouple client from object creation ‣ Con : Creator class must be extended to override a single method ⇒ might lead to bloated class hierarchy ‣ Relationship with other patterns : Abstract factories usually use factory methods that are implemented by concrete factories. 9
Naming Conventions ‣ get<Type> : returns an instance that is described by the input params; can be either an existing or a new instance ‣ new<Type> : as above; guarantees that each invocation return a new instance 10
Caveat Don't confuse with static factory methods that a class provides to instantiate itself. ⇒ Alternative to direct constructor invocation, see Joshua Bloch: Effective Java, 2 nd Edition, item 1 . Naming convention for static factory methods: ‣ getInstance : see get<Type> ‣ newInstance : see new<Type> 11
Excercise Use the factory method pattern to remove the dependencies on ConcreteBufferHolder and ConcreteUdpTask . @ThreadSafe // because DatagramSocket and Executor are threadsafe public class UdpServer { private static final int POOL_SIZE = 10; private final DatagramSocket udpSocket; private final ExecutorService executor; public UdpServer(int port) throws SocketException { udpSocket = new DatagramSocket(port); executor = Executors.newFixedThreadPool(POOL_SIZE); } public void start() throws IOException { while ( true ) { BufferHolder bufferHolder = new ConcreteBufferHolder(); byte[] buffer = bufferHolder.getBuffer(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length); udpSocket.receive(packet); executor.execute(new ConcreteUdpTask(packet)); } } } 12
Prototype … Somewhere in the deeply remote past it seriously traumatized a small random group of atoms drifting through the empty sterility of space and made them cling together in the most extraordinarily unlikely patterns. These patterns quickly learnt to copy themselves (this was part of what was so extraordinary about the patterns) and went on to cause massive trouble on every planet they drifted on to. That was how life began in the Universe … - Douglas Adams, The Hitchhiker's Guide to the Galaxy . 13
Idea If client knows when to create certain object(s), but doesn't know (neither care) how, then... . . . provide client with prototype of the object that can be cloned to get more instances. 14
DocManager Reloaded Originally: @ThreadSafe // assuming that LatexDoc is threadsafe public class DocManager { private final Collection<Document> docs; public DocManager() { docs = new ConcurrentLinkedQueue<Document>(); } public void createDoc() { Document doc = new LatexDoc(); docs.add(doc); doc.open(); } public void openDocs() { for ( Document doc : docs ) doc.open(); } ... } 15
DocManager Reloaded With prototype: @ThreadSafe // assuming that concrete Document is threadsafe public class DocManager { private final Collection<Document> docs; private final Document prototype; public DocManager(Document prototype) { docs = new ConcurrentLinkedQueue<Document>(); this.prototype = prototype; } public void createDoc() { Document doc = prototype.clone(); docs.add(doc); doc.open(); } public void openDocs() { for ( Document doc : docs ) doc.open(); } ... } 16
DocManager Reloaded Sample usage of prototype-based DocManager : DocManager docManager = new DocManager(new LatexDoc()); docManager.createDoc(); 17
Structure DocManager Document prototype: Document open() createDoc() close() Document doc = openDocs() clone() prototype.clone(); docs.add(doc); doc.open(); LatexDoc open() close() clone() return copy of itself 18
General Structure superclass/interface of products to be created, ‣ owns a product prototype incl. declaration of ‣ creates new products by asking the prototype to clone itself clone operation Client Product prototype: Product use() someOperation() Product product = clone() prototype.clone(); product.use(); ConcreteProduct use() clone() return copy of itself implementation of concrete product, incl. clone operation 19
How does prototype get into client? ⇒ out of scope of this pattern! 20
Pros & Cons ‣ Pro: • avoids bloating of type hierarchy, i.e. no client subclass (factory method), no factory interfaces ( → abstract factories) • client can be dynamically configured with new prototype ‣ Con: • product must support clone operation • difficult or impossible if product already exists • correct implementation of clone operation difficult, clone concept in Java seriously flawed ⇒ see later • if products with various settings are needed, product type must provide setters even if it could otherwise be immutable 21
Relationship with Other Patterns ‣ Abstract factories usually use factory methods that are implemented by concrete factories ‣ Alternative option : concrete factory gets prototype for each product type and uses cloning to create new products ⇒ fewer types, more flexibility, less type safety 22
Java’s Clone Concept ‣ class Object contains a protected clone method ‣ a subclass can only use Object.clone() if it implements the Cloneable marker interface, otherwise Object.clone() throws CloneNotSupportedException ‣ cloneable subclass must provide public clone method which is not enforced by Cloneable ‣ Object.clone • returns a copy of the right type, i.e. the object’s runtime type • makes a shallow copy of all fields, i.e. each field is copied into new instance 23
Java’s Clone Concept To implement clone method, ‣ call super.clone() ‣ replace shallow copies of own fields with deep copies as needed; usually done with fields’ clone methods works only if each supertype in the hierarchy adheres to this behavior! 24
Cloning: Example I @Immutable public final class PhoneNumber implements Cloneable { private final short areaCode; private final short prefix; private final short lineNumber; public PhoneNumber(short areaCode, short prefix, short lineNumber) { this.areaCode = areaCode; this.prefix = prefix; this.lineNumber = lineNumber; } ... @Override public PhoneNumber clone() { try { return (PhoneNumber) super.clone(); } catch ( CloneNotSupportedException e) { throw new AssertionError(); // Can’t happen } } } all cloning code samples from J. Bloch: Effective Java , Second Edition. 25
Cloning: Example II @Threadsafe public final class Stack implements Cloneable { private Object[] elements; private int size = 0; private static final int INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[INITIAL_CAPACITY]; } public synchronized void push(Object e) { ensureCapacity(); elements[size++] = e; } public synchronized Object pop() { if ( size == 0 ) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; } 26
Recommend
More recommend