• Keine Ergebnisse gefunden

Design Patterns

N/A
N/A
Protected

Academic year: 2022

Aktie "Design Patterns"

Copied!
35
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Oliver Haase

Design Patterns

Abstract Factory

(2)

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.

(3)

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();

}

(4)

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 )

(5)

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

(6)

Structure

createDoc() openDocs()

DocManager

open() close()

Document

open() close()

LatexDoc

newDocument() DocumentFactory

newDocument()

LatexDocFactory

(7)

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

(8)

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

(9)

How does factory get into client?

out of scope of this pattern!

(10)

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

(11)

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:

(12)

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

(13)

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();

}

(14)

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)

(15)

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));

(16)

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);

} }

(17)

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));

} }

}

(18)

Singleton

(19)

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

(20)

Description II

Structure

Interactions

‣ clients access singleton instance through getInstance

operation

(21)

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

(22)

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

(23)

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:

(24)

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

(25)

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

(26)

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!

(27)

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?

(28)

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

(29)

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:

(30)

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…

(31)

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();

(32)

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

(33)

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();

} }

(34)

Relationship With Other Patterns

or, using the enum implementation:

public enum ConcreteDocFactory implements DocumentFactory { INSTANCE;

@Override public Document newDocument() { return new LatexDoc();

} }

(35)

Discussion

‣ singleton pattern often argued to be overused with negative consequences

‣ mainly due to second purpose ‘provide global access point to single instance’

‣ leads to widely spread coupling because singleton class is explicitly mentioned ( obstacle for unit testing)

‣ not mandatory, singleton instance can just as well be passed

around as a parameter

Referenzen

ÄHNLICHE DOKUMENTE

According to GoF, there are two fundamental principles of reusable software design (that also underly most if not all patterns):2. 1 st Principle of

public final class PhoneNumber implements Product { private final short areaCode;. private final

@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister implements MovieFinderInjector { private MovieFinder finder;.. public

increased runtime, because client has to compute or access stored extrinsic state information, and pass it into flyweight objects. ‣ Memory saving

public static final class Memento { private final int state;. private Memento(int state) { this.state

‣ The class TCPConnection maintains a state object that represents the current state of the TCP connection.. ‣ The class TCPConnection delegates all state-specific requests

Die Definition deutet an, daß die Projektentwicklung als eine komplexe Perspektive zu verstehen ist, die über einzelne Vorstellungen bezüglich Nutzungskonzept, Architektur,

Women, on the other hand, have been seen to take care of the household (domus) and were therefore more likely to be in the private sphere 6. According to this understanding,