• Keine Ergebnisse gefunden

Design Patterns

N/A
N/A
Protected

Academic year: 2022

Aktie "Design Patterns"

Copied!
32
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Oliver Haase

Design Patterns

Introduction

‘An instrument, a tool, an utensil, whatsoever it be, if it be fit for

the purpose it was made for, it is as it should be though he

perchance that made and fitted it, be out of sight and gone.’ —

(2)

Bibliography

‣ Erich Gamma et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison–Wesley, 1994, ISBN 78-0201633610.

‣ Elisabeth Freeman et al. Head First Design Patterns, O'Reilly, 2004, ISBN 978-0596007126.

‣ Joshua Bloch. Effective Java, Second Edition, Addison Wesley, 2008, ISBN 978-0-321-35668-0.

The bible for design patterns. The authors

are widely known as the Gang of Four (GoF),

their patterns as GoF patterns.

(3)

Not that fast, buddy!

Before we start, let’s do some ground work...

(4)

Relationships between Classes (and Interfaces)

‣ Specialization and generalization

‣ Association

‣ Aggregation

‣ Composition

Creation

(5)

Association

Weakest form of relationship: A is related to B

Undirected Association: no information about navigability

Unidirectional Association: navigation from instances of A to instances of B

Bidirectional Association: navigation both ways

B A

B A

(6)

Aggregation

‣ Somewhat stronger than an association

‣ Models part-of relationship, e.g. B is part of A:

Example:

in Java: A has reference to (collection of) B

B A

Student Course

(7)

Composition

‣ Stronger than aggregation

‣ Part-of relationship, with ownership, i.e. lifespan of B depends on A

Example:

B A

Room

Building

(8)

Don’t you never forget

this again, or...

(9)

Design Patterns - Definition

Design Pattern:

“Each [design] pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem" - Christopher Alexander, 1977.

Christopher Alexander:

born 1936 in Vienna, studied mathematics and architecture (Cambridge),

received PhD in architecture (Harvard), professor for architecture (UoC,

Berkeley), known as the founder of design patterns

(10)

Design Patterns - Goals

“Any fool can write code that a computer can understand. Good programmers write code that

humans can understand." - Martin Fowler.

‣ fast to develop, clean solution through solution reuse

‣ simplified maintenance through clean structures and déjà vu effects

‣ improved extensibility through anticipatory design

‣ (partly) opportunity for code generation or language support (e.g. singleton pattern)

‣ (partly) opportunity for class libraries (e.g. observer

pattern)

(11)

Principles of Reusable Design

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

1 st Principle of Reusable Design:

Program against an interface, not an implementation.

2 nd Principle of Reusable Design:

Prefer composition over inheritance.

(12)

Excursion: 1 st Principle in Java

How it’s usually done:

public interface HelloSayer {

String getGreeting(String name);

}

public class NiceHelloSayer implements HelloSayer { @Override public String getGreeting(String name) { return “hello “ + name + “, how are you?”;

} }

Usage:

HelloSayer helloSayer = new NiceHelloSayer();

helloSayer.getGreeting(“John Doe”);

NiceHelloSayer niceHelloSayer = new NiceHelloSayer();

niceHelloSayer.getGreeting(“John Doe”);

...Or:

(13)

Excursion: 1 st Principle in Java

Implementation with nested class (J. Bloch, Effective Java):

public final class NiceHelloSayer {

private static final class Implementation implements HelloSayer { @Override public String getGreeting(String name) {

return "hello " + name + ", how are you?";

} }

public static Implementation getInstance() { return new Implementation();

}

private NiceHelloSayer() {

throw new AssertionError();

} }

(14)

Excursion: 2 nd Principle

Inheritance breaks encapsulation, leads to strong coupling between super and subclass.

Fragile Base Class Problem: Seemingly correct subclass can

break when superclass is (correctly) changed.

(15)

Excursion: 2 nd Principle

Assume, you want to implement an InstrumentedHashSet

that keeps book of the number of attempted add operations:

@ThreadSafe

public class InstrumentedHashSet<E> extends HashSet<E> { private int addCount = 0;

public InstrumentedHashSet() {}

public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor);

}

@Override public boolean add(E e) { addCount++;

return super.add(e);

}

@Override public boolean addAll(Collection<? extends E> c) {

(16)

Excursion: 2 nd Principle

Problem: HashSet internally uses add to implement addAll

elements added by addAll get counted twice!

(17)

Excursion: 2 nd Principle

Second attempt without overriding addAll :

@ThreadSafe

public class InstrumentedHashSet<E> extends HashSet<E> { private int addCount = 0;

public InstrumentedHashSet() {}

public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor);

}

@Override public boolean add(E e) { addCount++;

return super.add(e);

}

public int getAddCount() { return addCount; } }

(18)

Excursion: 2 nd Principle

Solution using Composition and Delegation:

@ThreadSafe

public class InstrumentedSet<E> implements Set<E> { private int addCount = 0;

private Set<E> s;

public InstrumentedSet(Set<E> s) { this.s = s; }

public int getAddCount() { return addCount; } @Override public boolean add(E e) {

addCount++;

return s.add(e);

}

@Override public boolean addAll(Collection<? extends E> c) { addCount += c.size();

return s.addAll(c);

}

@Override public void clear() { s.clear(); } // similarly override all other Set methods }

(19)

Excursion: 2 nd Principle

‣ agnostic of specific Set implementation, works not only for

HashSet

‣ more boilerplate code (due to lack of language support)

‣ InstrumentedSet independent of changes in the specific

Set implementation.

‣ loose coupling

Solution by Composition

(20)

Excursion: 2 nd Principle

Now, from the perspective of extending a threadsafe class while preserving its synchronization strategy.

Example: provide a thread-safe list implementation with additional putIfAbsent method (Goetz et al., Java

Concurrency in Practice)

your check-then-act warning bells should ring!

(21)

Excursion: 2 nd Principle

Solution by Inheritance: Extend synchronized Vector class (Java specifies that Vector synchronizes on its intrinsic lock)

@ThreadSafe

public class BetterVector<E> extends Vector<E> { public synchronized boolean putIfAbsent(E x) { boolean absent = !contains(E);

if ( absent ) { add(x);

}

return absent;

} }

Drawback:

(22)

Excursion: 2 nd Principle

@ThreadSafe

public class ImprovedList<E> implements List<E> { private final List<E> list;

public ImprovedList(List<E> list) { this.list = list;

}

public synchronized boolean putIfAbsent(E x) { boolean absent = !list.contains(E);

if ( absent ) { list.add(x);

}

return absent;

}

public synchronized void clear() { list.clear(); } // … similarly delegate all other list methods

}

Solution by Composition

(23)

Excursion: 2 nd Principle

‣ agnostic of specific list implementation

‣ extra layer of synchronization, in fact Java monitor pattern

‣ certain performance penalty

‣ more boilerplate code (due to lack of language support)

‣ ImprovedList ’s synchronization policy independent of changes in the specific list implementation.

Solution by Composition

(24)

Principles of Reusable Design

Key motivation behind both design principles:

Holy Grail of Reusable Software Design:

Avoid code dependencies to the largest extent possible.

(25)

Design Patterns - Categorization

GoF categorize design patterns based on two criteria:

1. Purpose:

creational: patterns for object creation

structural: patterns that compose classes and objects

behavioral: patterns that distribute responsibility for behavior across classes and objects, and make them interact

2. Scope:

class based: based on relationships between classes, mainly inheritance

object based: based on relationships between objects

(26)

Creational Patterns

“The way to get started is

to quit talking and begin

doing" - Walt Disney.

(27)

A Simple, Motivating Example...

Consider an oversimplified document manager that can create, open, and close LaTeX documents:

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

private final Collection<LatexDoc> docs;

public DocManager() {

docs = new ConcurrentLinkedQueue<LatexDoc>();

}

public void createDoc() {

LatexDoc doc = new LatexDoc();

docs.add(doc);

doc.open();

}

(28)

But...

... what about the 1 st principle of reusable software design?

Ok, make LatexDoc an implementation of an interface:

open() close()

Document

open() close()

LatexDoc

(29)

Then...

Use Document , rather than LatexDoc , in DocManager :

@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() {

(30)

Dependency Situation

Now DocManager has become dependent on both LatexDoc

and Document :

createDoc() openDocs()

DocManager

Document doc = new LatexDoc();

docs.add(doc);

doc.open();

open() close()

Document

open() close()

LatexDoc

(31)

So What?

What’s wrong with this creational dependency?

DocManager cannot be used for other document types, e.g.

plain text documents, even though its functionality is applicable to other document types as well;

‣ Difficult to use document mock-up for testing;

‣ In a framework, DocManager would not even know - and

care about - what document type to create.

(32)

More Abstractly

someOperation() Client

Product product =

new ConcreteProduct();

product.use();

use()

Product

use()

ConcreteProduct

Most creational patterns are about removing the creational

dependency between Client and ConcreteProduct .

Referenzen

ÄHNLICHE DOKUMENTE

Managing access to state, in particular to shared, mutable state (directly to member- variables of one class) with. ‣ Atomic change

A program will be free of deadlocks if all locks are acquired in one defined fixed global order... Fairness

public class ConcreteDocFactory implements DocumentFactory { private static final ConcreteDocFactory instance =.

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

Assume, a framework defines a base class (interface) that the specific custom class has to extend (implement), in order to be used in place of the base class (interface).. The

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