Oliver Haase
Design Patterns
1
Design Patterns Factory Method Oliver Haase 1 Idea If client - - PowerPoint PPT Presentation
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
Oliver Haase
1
If client knows when to create certain object(s), but doesn't know (neither care) how, then...
2
Also known as virtual constructor.
3
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(); } ... }
4
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(); } ... }
5 @ThreadSafe // assuming that LatexDoc is threadsafe public class LatexDocManager extends DocManager { @Override protected Document newDocument() { return new LatexDoc(); } }
Concrete subclass LatexDocManager:
6
newDocument() createDoc()
DocManager Document doc = newDocument(); docs.add(doc); doc.open();
close() Document
close() LatexDoc newDocument() LatexDocManager return new LatexDoc();
7
factoryMethod() someOperation() Creator Product product = factoryMethod(); product.use(); use() Product use() ConcreteProduct factoryMethod() ConcreteCreator return new ConcreteProduct();
superclass/interface of products to be created implementation of concrete product
implements factory method such that concrete product gets created
8
someOperation() Client Product product = new ConcreteProduct(); product.use(); use() Product use() ConcreteProduct
9
creation
method ⇒ might lead to bloated class hierarchy
usually use factory methods that are implemented by concrete factories.
input params; can be either an existing or a new instance
return a new instance
10
Don't confuse with static factory methods that a class provides to instantiate itself. ⇒ Alternative to direct constructor invocation, see Joshua Bloch: Effective Java, 2nd Edition, item 1. Naming convention for static factory methods:
11
Use the factory method pattern to remove the dependencies
12 @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)); } } }
13
… 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.
If client knows when to create certain object(s), but doesn't know (neither care) how, then...
14
. . . provide client with prototype of the object that can be cloned to get more instances.
15
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(); } ... }
16
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(); } ... }
17
DocManager docManager = new DocManager(new LatexDoc()); docManager.createDoc();
Sample usage of prototype-based DocManager:
18
close() clone() Document
close() clone() LatexDoc Document doc = prototype.clone(); docs.add(doc); doc.open(); return copy of itself createDoc()
prototype: Document DocManager
19
use() clone() Product use() clone() ConcreteProduct Product product = prototype.clone(); product.use(); return copy of itself someOperation() prototype: Product Client
implementation
product, incl. clone
superclass/interface of products to be created,
clone operation
20
How does prototype get into client? ⇒ out of scope of this pattern!
21
(factory method), no factory interfaces (→abstract factories)
concept in Java seriously flawed ⇒ see later
must provide setters even if it could otherwise be immutable
implemented by concrete factories
product type and uses cloning to create new products ⇒ fewer types, more flexibility, less type safety
22
the Cloneable marker interface, otherwise
Object.clone() throws CloneNotSupportedException
which is not enforced by Cloneable
type
into new instance
23
To implement clone method,
needed; usually done with fields’ clone methods
24
works only if each supertype in the hierarchy adheres to this behavior!
25 @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.
26 @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; }
27
private void ensureCapacity() {
if ( elements.length == size ) elements = Arrays.copyOf(elements, 2 * size +1); } @Override public synchronized Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); // Can’t happen } } }
Note that
references same objects as original stack’s elements array;
28
public final class HashTable implements Cloneable { private Entry[] buckets; private static class Entry { final Object key; Object value; Entry next; Entry(Object key, Object value, Entry next) { this.key = key; this.value = value; this.next = next; } } public HashTable(int size) { buckets = new Entry[size]; } ... }
29
@Override public HashTable clone() {
try { HashTable result = (HashTable) super.clone(); result.buckets = buckets.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); // Can’t happen } } }
Broken, because cloned buckets array references same linked lists as original array!
clone method, first try:
30
private static class Entry { final Object key; Object value; Entry next; Entry(Object key, Object value, Entry next) { this.key = key; this.value = value; this.next = next; } Entry deepCopy() { return new Entry(key, value, next == null ? null: next.deepCopy()); } }
Add deepCopy method to Entry class:
31
@Override public HashTable clone() {
try { HashTable result = (HashTable) super.clone(); result.buckets = new Entry[buckets.length]; for ( int i = 0; i < buckets.length; i++ ) if ( buckets[i] != null ) result.buckets[i] = buckets[i].deepCopy(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); // Can’t happen } } }
Alternatively,
3.use high-level operations to reconstruct state
clone method, second try:
32
constructor instead ⇒ not directly suitable for prototype patterns because Client does not know prototype’s runtime type ⇒ declare clone/copy operation in Product, have each
ConcreteProduct implement clone/copy directly, e.g.
through copy constructor
33 public final class PhoneNumber implements Product { 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; } public PhoneNumber(PhoneNumber pn) { this(pn.areaCode, pn.prefix, pn.lineNumber); } ... @Override public PhoneNumber copy() { return new PhoneNumber(this); } }
34
public final class Stack implements Product { private Object[] elements; private int size = 0; private static final int INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[INITIAL_CAPACITY]; } public Stack(Stack s) { elements = s.elements.clone(); } ... @Override public synchronized Stack copy() { return new Stack(this); } }
If ConcreteProduct is to be thread-safe, clone operation needs to be synchronized like any other method.
35
doesn’t bloat type hierarchy
constructors (consistent with final concept)
states are needed (requires setters for otherwise immutable fields)
36
Use the prototype pattern to remove the dependencies on
ConcreteBufferHolder and ConcreteUdpTask.
37 @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)); } } }