Design Patterns

Download Report

Transcript Design Patterns

ECE452/CS446/SE464
Design Patterns: Part I
A Tutorial by Peter Kim
Based on slides prepared by
Krzysztof Pietroszek
Problem 1
public class GUI
{
…
public void setPerson(Person person)
{
nameLabel.setText(person.getName());
ageLabel.setText(person.getAge());
}
}
public class Person
{
protected String name; …
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge() {…}
public void setAge(int age) {…}
}
To display Person on GUI, GUI.setPerson(Person) must be invoked explicitly.
What design pattern could be used so that changes to Person are more
implicitly and “automatically” reflected in GUI and potentially in other classes
that are interested in Person? Show a) the design pattern and b) an interaction
diagram demonstrating a change in Person
Answer 1a): Observer (behavioural) design pattern
•What’s the problem
between Observer.update()
and Subject.getState() here?
•Granularity of
updates (e.g. age
updated even though
only name was
changed)
Person
-name: String
-age: int
GUI
1
+setName(name: String): void
+getName(): String
+setAge(age: int): void
+getAge(): int
{ this.age = age;
notify(); }
{ this.name = name;
notify(); }
*
-nameLabel: Label
-ageLabel: Label
+GUI(person: Person): GUI
+update(): void
{ nameLabel = person.getName();
ageLabel = person.getAge().toString(); }
{ this.person = person;
this.person.attach(this); }
Answer 1b)
p := new Person()
:Program
p:Person
gui := new GUI(p)
gui:GUI
attach(gui)
setAge(23)
notify(AGE)
update(AGE)
getAge()
Overlapping
execution
occurrences on the
same lifeline are
represented by
overlapping
rectangles
Problem 2
●
Assume that you are implementing a file system. The
main concepts in a file system are directories and files.
A directory may contain several files and directories.
Additionally, you would like to treat files and
directories in a uniform way as nodes, i.e., you would
like to be able to write code that can treat both files and
directories using the same interface.
➔
What design pattern could you use to achieve these
goals?
➔
Draw a class diagram explaining your design.
Answer 2
●
Composite Pattern
Problem 3: Java API related
What design pattern is java.util.Collections.sort(List,
Comparator) method an example of?
package java.util;
public interface Comparator
{
public int compare (Object arg1, Object arg2);
…
}
Draw a class diagram and draw some sample code.
Answer 3: Strategy
public class AgeComparator implementsComparator
Inside java.util.Collections.sort(.), comparator.compare(Object,
Object) is used *somehow*:
{
…
void Collections.sort(List list, Comparator comparator)
public int compare(Object arg1, arg2)
{
{
Age a1 = (Age)arg1;
…
Age a2 = (Age)arg2;
int comparison = comparator.compare(list.get(i), list.get(i+1));
if(a1.getValue() > a1.getValue())
…
return 1;
}
else if(a1.getValue() < a2.getValue())
return -1;
return 0;
}
}
Collections.sort(ages, new AgeComparator());
Problem 4: Java API related
Consider the following Java I/O streams, where an indentation represents
class inheritance (e.g. FileOutputStream extends OutputStream).
OutputStream: the generic output stream.
FileOutputStream: output stream for writing to files.
FilterOutputStream: takes a regular OutputStream as an input and performs some filtering operations on it.
DeflaterOutputStream: performs compression on the input OutputStream.
ZipOutputStream: produces a compressed output of “Zip” format.
GZIPOutputStream: produces a compressed output of “GZIP” format.
What design pattern is evident (hint: FilterOutputStream)? Draw a class
diagram and provide a sample code.
Answer 4: Decorator (Structural)
public class FilterOutputStream extends OutputStream {
/**
* The underlying output stream to be filtered.
*/
protected OutputStream out;
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*/
public FilterOutputStream(OutputStream out) {
this.out = out;
}
public void write(int b) throws IOException {
out.write(b);
}
…
1
}
1
public class DeflaterOutputStream extends FilterOutputStream
{
public void write(int b) throws IOException {
bCompressed = compress(b);
super.write(bCompressed);
}
}
Problem 5
a) A commonly cited advantage of the Decorator design pattern is
that it avoids subclass explosion. Explain this using an example.
b) Commonly cited disadvantages of the Decorator design pattern
are that it allows
i) identity crisis and
ii) interface occlusion.
Why are these problems not evident with subclassing? Explain
these using an example.
Answer 5a)
Component
• N functionalities typically represented in N
subclasses
• But representing combinations of N
functionalities as subclasses means, for
example, {(N choose 1) + (N choose 2) + … +
(N choose N) – invalid number of
combinations} subclasses
Frameable
Scrollable
Resizable
Frameable
Scrollable
Frameable
Resizable
Scrollable
Resizable
FrameableScrollable
Resizable
• Using the Decorator design pattern
• N combinations are represented through, for
example, {(N choose 1) + (N choose 2) + … +
(N choose N) – invalid number of
combinations} objects. However, there are only
N classes. Object explosion not as bad as
[sub]class explosion as the former is more
manageable and much smaller.
Objects are composed from
right to left. E.g. fs means
that s:Scrollable is
composed by f:Frameable
Component
1
1
Frameable
ComponentDecorator
Scrollable
Window
Resizable
3 choose 1
f: Frameable
s: Scrollable
r: Resizable
3 choose 2
fs: Frameable
fr: Frameable
sr: Scrollable
3 choose 3
fsr: Frameable
Answer 5b)
Basically, problems arise because there are two objects,
whereas with subclassing, there is only one object.
Component
1
paint(): void
dispose(): void
• Interface occlusion
• Interface of the component is blocked by the
decorator in the sense that the decorator has to
explicitly delegate methods to the component
• E.g. If interface of Component class
evolves (e.g. dispose() is added), then the
implementation of the decorator must also
evolve so that the method calls will be
delegated to the component (e.g. dispose()
calls c.dispose()).
• Identity crisis
• Clients are referencing the undecorated object ‘c’
initially. ‘c’ is decorated with ‘cd’, an instance of
ComponentDecorator. Clients must update the
reference ‘c’ to ‘cd’. Hard to update all the clients
(especially with many scattered object references),
e.g. Client1 is not updated.
• With subclassing, there’s only one object so
there’s no such problem
c.paint();
ComponentDecorator
1
Frameable
paint(): void
Scrollable
Resizable
Client1
c: Component
Client2
cd: ComponentDecorator