• Keine Ergebnisse gefunden

Design Patterns

N/A
N/A
Protected

Academic year: 2022

Aktie "Design Patterns"

Copied!
37
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Oliver Haase

Design Patterns

Factory Method

(2)

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.

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

}

(4)

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

} ...

(5)

DocManager Reloaded

@ThreadSafe // assuming that LatexDoc is threadsafe public class LatexDocManager extends DocManager {

@Override

protected Document newDocument() { return new LatexDoc();

} }

Concrete subclass LatexDocManager :

(6)

Structure

newDocument() createDoc()

openDocs()

DocManager

Document doc = newDocument();

docs.add(doc);

doc.open();

open() close()

Document

open() close()

LatexDoc newDocument()

LatexDocManager return

new LatexDoc();

(7)

Structure - in General

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

‣ defines factory method

‣ uses factory method to create products

‣ may provide default implementation

implements factory

(8)

Comparison: Starting Point

someOperation() Client

Product product =

new ConcreteProduct();

product.use();

use()

Product

use()

ConcreteProduct

(9)

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.

(10)

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

(11)

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>

(12)

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

}

(13)

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

(14)

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.

(15)

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

}

(16)

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

} ...

(17)

DocManager Reloaded

DocManager docManager = new DocManager(new LatexDoc());

docManager.createDoc();

Sample usage of prototype-based DocManager :

(18)

Structure

open() close() clone()

Document

open() close() clone()

LatexDoc Document doc =

prototype.clone();

docs.add(doc);

doc.open();

return copy of itself createDoc()

openDocs()

prototype: Document

DocManager

(19)

General Structure

use() clone()

Product

use() clone()

ConcreteProduct Product product =

prototype.clone();

product.use();

return copy of itself someOperation()

prototype: Product Client

superclass/interface of products to be created,

incl. declaration of clone operation

‣ owns a product prototype

‣ creates new products by asking the prototype to clone itself

(20)

How does prototype get into client?

out of scope of this pattern!

(21)

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

(22)

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

(23)

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

(24)

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!

(25)

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

(26)

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;

}

(27)

Cloning: Example II, cont’d

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

(28)

Cloning: Example III

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)

Cloning: Example III, cont’d

@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

clone method, first try:

(30)

Cloning: Example III, cont’d

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)

Cloning: Example III, cont’d

@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,

clone method, second try:

(32)

Java Cloning - Summary

J. Bloch advises to generally avoid clone but to use copy 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)

‘Own’ Cloning: Example I

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

} ...

(34)

‘Own’ Cloning: Example II

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

} }

(35)

Synchronization

If ConcreteProduct is to be thread-safe, clone operation

needs to be synchronized like any other method.

(36)

Discussion

‣ Prototype pattern is flexible, dynamically configurable, and doesn’t bloat type hierarchy

‣ Rather don’t use Java’s cloning concept but provide your own, independent cloning, preferably based on copy

constructors (consistent with final concept)

‣ Prototype pattern not suitable if objects with different states are needed (requires setters for otherwise

immutable fields)

(37)

Excercise

Use the prototype 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 ) {

Referenzen

ÄHNLICHE DOKUMENTE

[r]

(in %)...160 Table 6-23: Respondents by time of involvement in the selected e-infrastructure ...161 Table 6-24: Respondents by intensity of involvement in the selected

The purpose of such categorization is the same as for the electrons: loose muons are input to the MVA that separates prompt leptons from non-prompt lepton; fakeable muons are used

De plus, les régulateurs solaires et solutions de surveillance existants, qui fonctionnent indépendamment de l’installation, ont été analysés afin de déterminer

These questions relate both to the Professional Development needs relating to International student programmes in the tertiary sector, and to issues relating to the mandatory Code

Du point de vue des fabricants, les préoccupations principales concernent la réduction des doses de rayons X et l’augmentation de puissance pour obtenir de meilleures

Wolfgang Prodinger, MME (Bern), Vice Rector for Teaching and Study Matters Approved by the Vice Rector for Teaching and Study Matters YES NO..

Wolfgang Prodinger, MME (Bern), Vice Rector for Teaching and Study Matters Approved by the Vice Rector for Teaching and Study Matters YES NO..