Oliver Haase
Design Patterns
Abstract Factory
Idea
If client knows when to create certain object(s), but doesn't know (neither care) how, then...
. . . provide client with factory object that can be asked to
create objects as needed.
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();
}
DocManager Reloaded
With abstract factory:
@ThreadSafe // assuming that LatexDoc is threadsafe public class DocManager {
private final Collection<Document> docs;
private final DocumentFactory docFactory;
public DocManager(DocumentFactory docFactory) { docs = new ConcurrentLinkedQueue<Document>();
this.docFactory = docFactory;
}
public void createDoc() {
Document doc = docFactory.newDocument();
docs.add(doc);
doc.open();
}
public void openDocs() {
for ( Document doc : docs )
DocManager Reloaded
Requires the definition of a suitable factory interface
public interface DocumentFactory { Document newDocument();
}
Sample usage of the factory-based DocManager :
DocManager docManager = new DocManager(new LatexDocFactory());
docManager.createDoc();
public class LatexDocFactory implements DocumentFactory { @Override public Document newDocument() {
return new LatexDoc();
}
…and of a concrete factory
Structure
createDoc() openDocs()
DocManager
open() close()
Document
open() close()
LatexDoc
newDocument() DocumentFactory
newDocument()
LatexDocFactory
Structure - in General
Client
ProductB
ConcreteProductB
getProductA() getProductB()
AbstractFactory
getProductA() getProductB()
ConcreteFactory ProductA
ConcreteProductA uses interfaces to create
and use products
declares operations for product creation (usually product family)
defines operations for product family creation interface for product type
concrete product
Pros and Cons
Pros:
‣ easy replacement of whole product family
‣ well-defined concrete factory ensures consistent creation of products of the same product family
Cons:
‣ may lead to bloated type hierarchy factory interfaces, one concrete factory per product family ‘conceptual weight’
‣ difficult to change product family, e.g. to add a product type
How does factory get into client?
out of scope of this pattern!
Class Objects as Factories
‣ Java’s reflection mechanism allows to create instances using Class objects
‣ Class objects can be denoted as class literals, such as
Document.class
‣ Class objects can be considered built-in factory objects
Class Objects as Factories
@ThreadSafe
public class ReflexiveDocManager<T extends Document> { private final Collection<Document> docs;
private final Class<T> documentClass;
public ReflexiveDocManager(Class<T> documentClass) { docs = new ConcurrentLinkedQueue<Document>();
this.documentClass = documentClass;
}
public void createDoc() throws InstantiationException, IllegalAccessException { Document doc = documentClass.newInstance();
docs.add(doc);
doc.open();
}
public void openDocs() {
for ( Document doc : docs ) doc.open();
} }
new ReflexiveDocManager<LatexDoc>(LatexDoc.class).createDoc();
Usage:
Pros & Cons
Pros:
‣ no factory interface and no concrete factory class required Cons:
‣ becomes more complicated if c’tor requires params
‣ less type safety, existence of suitable c’tor checked at runtime only
‣ works for single product, not product family
Realization with Prototype Pattern
‣ Mostly, factories implemented using factory methods
‣ However, implementation using prototypes also possible
public interface DocumentFactory { Document newDocument();
}
DocManager docManager =
public class ConcreteDocFactory implements DocumentFactory { private Document prototype;
public ConcreteDocFactory(Document prototype) { this.prototype = prototype;
}
@Override public Document newDocument() { return prototype.clone();
}
Properties
‣ only one concrete factory class less type bloating
‣ prototypes get fed in dynamically guarantee for consistent product family creation gets lost
‣ carries usual prototype cons (setter required, loss of
immutability, clone/copy operation in product interface
required)
Excercise
Use the abstract factory 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));
public interface BufferAndTaskFactory { BufferHolder newBufferHolder();
Runnable newUdpTask(DatagramPacket packet);
}
public class ConcreteBufferAndTaskFactory implements BufferAndTaskFactory { @Override public BufferHolder newBufferHolder() {
return new ConcreteBufferHolder();
}
@Override public Runnable newUdpTask(DatagramPacket packet) { return new ConcreteUdpTask(packet);
} }
public class UdpServer {
private static final int POOL_SIZE = 10;
private final DatagramSocket udpSocket;
private final ExecutorService executor;
private final BufferAndTaskFactory factory;
public UdpServer(int port, BufferAndTaskFactory factory) throws SocketException {
udpSocket = new DatagramSocket(port);
executor = Executors.newFixedThreadPool(POOL_SIZE);
this.factory = factory;
}
public void start() throws IOException { while ( true ) {
BufferHolder bufferHolder = factory.newBufferHolder();
byte[] buffer = bufferHolder.getBuffer();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
udpSocket.receive(packet);
executor.execute(factory.newUdpTask(packet));
} }
}
Singleton
Description
Purpose
‣ ensure that a class can be instantiated exactly once
‣ provide global access point to single instance Application
‣ exactly one driver for a piece of hardware (e.g. printer)
‣ exactly one socket listener that receives incoming requests
‣ global event queue for discrete event simulation
Description II
Structure
Interactions
‣ clients access singleton instance through getInstance
operation
Alternative
Alternative: Class with static variables and static methods
Drawbacks:
‣ Java interfaces cannot contain static methods class cannot be hidden behind interface
‣ each method invocation contains class name undermines
polymorphism
Implementation
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() { ...
}
public static MySingleton getInstance() { if ( instance == null ) {
instance = new MySingleton();
}
return instance;
}
public void use() { ...
} }
Singleton implementation with lazy instantiation:
‣ Race condition between lines 7 and 8 (check-then-act)!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Implementation
@Threadsafe
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() { ...
}
public static synchronized MySingleton getInstance() { if ( instance == null ) {
instance = new MySingleton();
}
return instance;
}
// use might have to be sync’ed, too...
public void use() { ...
} }
Resolution: correctly synchronize getInstance method:
Implementation
Problem: Only first call of getInstance needs to be synchronized:
‣ unnecessary reduction of concurrency
‣ synchronized Methods always slower than unsynchronized methods: test program with 10 10 sequential
getInstance calls on MacBook Air, 2,13 GHz Intel Core 2 Duo, 4 GB RAM, 251 GB flash drive:
unsynchronized 6 sec
synchronized 410 sec
Implementation
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() { ...
}
public static MySingleton getInstance() { if ( instance == null ) {
synchronized ( MySingleton.class ) { instance = new MySingleton();
} }
return instance;
}
...
}
Implementation with Checked Locking:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Implementation
Scenario:
‣ Thread 1 gets interrupted between lines 8 and 9 holds
monitor of MySingleton class object, no singleton instance created yet
‣ Thread 2 passes 7, waits at 8 for monitor lock
‣ Thread 1 continues, creates singleton instance, releases lock
‣ Thread 2 continues, creates second ‘singleton’ instance
not thread-safe!
Implementation
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() { ...
}
public static MySingleton getInstance() { if ( instance == null ) {
synchronized ( MySingleton.class ) { if ( instance == null ) {
instance = new MySingleton();
} } }
return instance;
}
...
}
Double-Checked-Locking-(Anti-)Pattern
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Works well - or does it?
Implementation
Problem: Thread scheduling happens on the level of byte code, not on the Java source code level… object creation in line 10
instance = new MySingleton();
is mapped to the following pseudo byte code
ptrMemory = allocateMemory()
assignMemory(instance, ptrMemory) callMySingleConstructor(instance) 1
2 3
If scheduling takes place between lines 2 and 3, then
‣ instance != null , even though
‣ instance has not yet been properly constructed
Implementation
@Threadsafe
public class MySingleton {
private static final MySingleton instance = new MySingleton();
private MySingleton() { ...
}
public static MySingleton getInstance() { return instance;
}
// use might have to be sync’ed public void use() {
...
} }
The simple solutions are often the best - or: Life can be that
simple! fully functional, performant solution:
Implementation
Consequences of the simple implementation:
‣ Singleton instance is created at class loading time
‣ even if it is never used, but
• this rarely happens because class loading is usually triggered by usage
• damage comparably small
‣ Time consumption at different point in time neither
better nor worse, only different…
Alternative Implementation
@Threadsafe
public enum MySingleton { INSTANCE;
// use might have to be sync’ed public void use() {
...
} }
Singleton as a single-element enum:
Usage:
MySingleton.INSTANCE.use();
Singleton as Enum
Pros
‣ short, concise implementation
‣ protection against instantiation through reflection and serialization
Cons
‣ not widely adopted (yet) needs some getting used to
‣ not applicable if singleton has supertype (use composition and delegation instead)
‣ singleton decision cannot easily be changed b/c INSTANCE is
Relationship With Other Patterns
‣ Factories are often implemented as singletons
public class ConcreteDocFactory implements DocumentFactory { private static final ConcreteDocFactory instance =
new ConcreteDocFactory();
private ConcreteDocFactory() {}
public static DocumentFactory getInstance() { return instance;
}
@Override public Document newDocument() { return new LatexDoc();
} }
Relationship With Other Patterns
or, using the enum implementation:
public enum ConcreteDocFactory implements DocumentFactory { INSTANCE;
@Override public Document newDocument() { return new LatexDoc();
} }