Introduction to Object-Oriented Design Patterns – 2015

advertisement
Introduction to
Object-Oriented
Design Patterns
jonas.kvarnstrom@liu.se – 2015
2
First: An illustrating problem

Suppose you need to sort some strings
Implement sorting algorithms
public class StringSorter {
public void quicksort(String[] strings)
public void mergesort(String[] strings)
}
Use the algorithms
String[] strings = new String[numberOfStrings];
…
StringSorter mySorter = new StringSorter();
mySorter.quicksort(strings);
{…}
{…}
jonkv@ida
Sorting 1: String Sorting

A sorting algorithm must be able to compare two elements
Obvious: Comparison inside each sort method
public class StringSorter {
public void quicksort(String[] array) {
…
// Should element #i be before element #j?
if (array[i].compareTo(array[j]) < 0) { … }
}
}
String has a built-in compareTo method
Returns -1, 0 or 1: str1 should be before,
equal to, or after str2
3
jonkv@ida
Sorting 2: String Comparisons

And sometimes you need an inverse sort order
Obvious solution: Add a flag
public class StringSorter {
private boolean inverseOrder;
public StringSorter(boolean inverse)
{ this.inverseOrder = inverse; }
public void quicksort(String[] strings) {
…
// Should element #i be before element #j?
if ( !inverseOrder && array[i].compareTo(array[j]) < 0 ||
inverseOrder && array[i].compareTo(array[j]) > 0) {
…
}
}
Different comparisons
}
depending on standard or inverse order
4
jonkv@ida
Sorting 3: Different Sort Orders

You sometimes use different locales
 Danish:
…xyzäöå
 Swedish:
…xyzåäö
 English:
aåäbcde…
 Add a locale specifier to the StringSorter

Someone wants to sort strings representing dates
 In day/month/year format
 Add a new flag indicating date sorting should be used

…
5
jonkv@ida
Sorting 4: Additional Sort Orders

So what’s the problem?
Every time we want a new sorting criterion,
the sort method must be changed
 can't be in a class library
Having many sorting criteria
leads to long and complex conditional tests
A pure sorting algorithm
should not have knowledge about the type of data being sorted
Inflexible!
6
jonkv@ida
Sorting 5: Lack of Flexibility

8
One general approach: Higher-order functions
Python example from TDDD73
def combine(func,seq):
result = seq[0]
for element in seq[1:]:
result = func(result,element)
return result
Takes another function
as parameter
String sorting example
void quicksort(String[] strings, function compare) {
…
// Should element #i be before element #j?
if (compare(strings[i], strings[j]) < 0) {
…
}
}
Takes a comparison function
as a parameter
Call this callback function
to determine element order!
Many OO languages do not support this!
How do we find a pure OO solution?
jonkv@ida
Callbacks using Higher-Order Functions

9
OO approach: Let an object compare the elements for us
public interface StringComparator {
int compare(String str1, String str2);
}
public class StringSorter {
private final StringComparator comp;
public StringSorter(StringComparator comp) {
this.comp = comp;
}
public void quicksort(String[] strings) {
…
// Should element #i be before element #j?
if (comp.compare(strings[i], strings[j]) < 0) { … }
}
}
Call the specified comparator's method
Returns -1, 0 or 1: str1 should be
before, equal to, or after str2
Specify a comparator
when the sorter is created
This is an object-oriented way of implementing callbacks!
jonkv@ida
Callbacks using Objects

10
Now you can implement as many comparators as needed!
 No need to change the StringSorter – reuse it for arbitrary sort orders
public class LengthComparator implements StringComparator {
public int compare(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
if (len1 > len2) return 1;
else if (len1 < len2) return -1;
else return 0;
}
}
jonkv@ida
Implementing Comparators
11
StringComparator lengthcomp= new LengthComparator();
StringSorter sorter = new StringSorter(lengthcomp);
sorter.quicksort(strings);
sorter
The sorter
is told which
comparator
to use
… comp.compare() …
Object
header:
comp
…
The comparator
may have no fields / data,
but it has methods
that the sorter can call
StringSorter class
sort()
[code]
…
[code]
…
[code]
(data)
…
Object
header:
(data)
LengthComparator
class
compare() [code]
…
[code]
…
[code]
jonkv@ida
Using Comparators

12
The Collections Framework has a generic comparator interface
public interface Comparator<T> {
int compare(T obj1, T obj2);
}
public class Sorter<T> {
private final Comparator<T> comp;
public Sorter(Comparator<T> comp) {
this.comp = comp;
}
public void quicksort(List<T> list) {
// ...
// Should element #i be before element #j?
if (comp.compare(list.get(i), list.get(j)) < 0) { /* ... */}
}
Also, built-in sort method:
}
Collections.sort(students, new NameLengthComparator());
jonkv@ida
Java Collections Framework: Comparator

Similar problems and solutions arise in many places
 Any JComponent in the Java GUI can have a border
▪
▪
▪
▪
Not a built-in repertoire of borders
Instead, callback objects implementing the Border interface
Change border: component.setBorder(Border b)
Can easily define a new Border, with arbitrary design
14
jonkv@ida
Generalization 1: Similar Problems
 Any Container in the Java GUI can lay out its subcomponents
▪ Set a layout callback object
using component.setLayout()
▪ Uses a LayoutManager interface
with concrete managers FlowLayout, BorderLayout, …
▪ Can easily define a new LayoutManager
 A text component might need input validation
▪ JTextField supports an InputVerifier interface
▪ You can provide Numeric, AlphaNumeric, TelNumber verifier classes
 …
15
jonkv@ida
Generalization 2: Similar Problems

16
There is a certain pattern in the design of these classes!
Class
that needs to be parameterized
General interface
for parametrization
StringSorter – which sort order?
JComponent – which border?
Container – which layout?
JTextField – which input pattern?
StringSorter – StringComparator
JComponent – Border
Container – LayoutManager
JTextField – InputVerifier
This parametrization requires
code, not just values!
Concrete
implementation
Concrete
Concrete
implementation
Concrete
implementation
implementation
Strategy pattern:
A comparator object specifies which comparison strategy to use,
a border object specifies which border painting strategy to use, …
jonkv@ida
Generalization 3: A Pattern
17
 StringSorter + Comparator shows one way of using a Strategy
StringSorter
StringComparator
<<interface>>
comp: Comparator
quicksort(String[] array)
mergesort(String[] array)
DefaultOrder
compare(String first,
String second)
compare(String first,
String second)
InverseOrder
compare(String first,
String second)
”Arrow with diamond”
means aggregation:
Any StringSorter
has a Comparator
DateOrder
compare(String first,
String second)
Separation of concerns: StringSorter knows nothing about specific sort orders
jonkv@ida
One Use of Strategy: String Sorting
18
 JComponent + Border shows another way of using a Strategy
JComponent
Border <<interface>>
bord: Border
paint()
paintBorder(Graphics g)
LineBorder
paintBorder(Graphics g)
EtchedBorder
paintBorder(Graphics g)
ThickBorder
paintBorder(Graphics g)
Separation of concerns: JComponent knows nothing about specific border types
jonkv@ida
One Use of Strategy: Border Drawing

19
We can lift out a general design pattern that can be reused!
Strategy<<interface>>
Context
ContextInterface()
AlgorithmInterface()
Standardized terminology
for this pattern type:
Context, Strategy,
ConcreteStrategyX
ConcreteStrategyA
AlgorithmInterface()
ConcreteStrategyB
AlgorithmInterface()
jonkv@ida
The General Strategy Pattern

21
"Pattern" has many meanings
 Some relevant in this context:
▪ 5. a combination of qualities, acts, tendencies, etc.,
forming a consistent or characteristic arrangement
– Dictionary.com
▪ 10: a discernible coherent system based on
the intended interrelationship of component parts <foreign policy patterns>
– Merriam-Webster
jonkv@ida
Patterns – in general

Originates in architecture!
 Christopher Alexander et al. (1977) state that a pattern:
▪ describes a problem
which occurs over and over again in our environment,
and then describes the core of the solution to that problem,
in such a way that you can use this solution a million times over,
without ever doing it the same way twice.
22
jonkv@ida
Design Patterns

Object-oriented design patterns:
 How do we arrange our classes
to solve a particular type of problem?
▪
▪
▪
▪
▪

Which interfaces and classes to create?
Who knows about whom?
Who inherits from whom?
How do they call each other?
…
Contrast to lower-level idoms:
 How do we write our code
to solve a particular type of problem (such as iteration)?
▪ Keywords
▪ Operations
▪ Function calls
23
jonkv@ida
Design Patterns and Idioms

24
A typical object-oriented design pattern:
 Describes a general way of organizing your classes, objects, methods
to solve a kind of problem
Strategy<<interface>>
Context
ContextInterface()
AlgorithmInterface()
ConcreteStrategyA
AlgorithmInterface()
ConcreteStrategyB
AlgorithmInterface()
 Can be quite different from how the problem would be solved
in a non-OO language
jonkv@ida
Object-Oriented Design Patterns

25
A typical object-oriented design pattern:
 Can be quite different from
how the problem would be
solved in a non-OO language
void
quicksort(String[] strings, function compare) {
…
// Should element #i be before element #j?
if (compare(strings[i], strings[j]) < 0) {
…
}
}
 Gives you a repertoire of techniques
that you might not have thought of on your own
 Has been refined to provide flexibility
that the first solution you think of
might lack – take advantage of others' experience!
jonkv@ida
Object-Oriented Design Patterns (2)

26
A typical object-oriented design pattern:
 Provides a common vocabulary
to talk about solutions
Strategy<<interface>>
Context
ContextInterface()
AlgorithmInterface()
ConcreteStrategyA
AlgorithmInterface()
ConcreteStrategyB
AlgorithmInterface()
 Makes it easier to read existing code, because you know the pattern
 Generally can’t be completely implemented in a class library
▪ Different methods needed, different parameters, … – no single solution!
jonkv@ida
Object-Oriented Design Patterns (3)

Object-oriented patterns are ”mined”!
 Only recognized as patterns
when we see similar solutions reused again and again

Many useful patterns have been found
 Should be aware of them
 Helps you find solutions to complex problems
 Helps you avoid solutions that are inflexible, difficult to maintain
But don’t use them blindly!
Often introduce some additional complexity:
New classes, new levels of indirection
(method calls to Comparator), …
Sometimes worth the complexity, sometimes not!
27
jonkv@ida
Object-Oriented Design Patterns (4)

29
Strategy builds on delegation
 Passing part of your responsibilities to someone else
StringSorter
StringComparator <<interface>>
comp: Comparator
quicksort(String[] array)
mergesort(String[] array)
I need to check the order
between two strings…
compare(String first, String second)
Let some Comparator do it!
public void quicksort(String[] array) {
…
// Should element #i be before element #j?
// Let’s delegate the question!
if (comp.compare(array[i], array[j]) < 0) { … }
…
}
jonkv@ida
Delegation

Some consider delegation to be a design pattern
 But it is very abstract
 The original book says:
It is a technique with which many patterns are implemented
30
jonkv@ida
Delegation (2)

Example: Game with multiple "modes" per player
public enum PlayerMode {
NORMAL,
// Standard mode
STRONGER,
// Stronger and less vulnerable
FASTER,
// Runs faster
GHOST, …
// Can walk, run or jump through solid matter
}
Many methods affected
public class Player {
by each power-up
private PlayerMode mode;
public void jump() {
if (mode == PlayerMode.NORMAL) {
Long conditional statements
// Ordinary jump
} else if (mode == PlayerMode.STRONGER) {
// Jump a bit higher
New powerups? Many methods
} else if (mode == PlayerMode.GHOST) {
to change – in Player!
// Can jump through platforms from
// below, land on platforms from above
Not flexible +
}…
not good separation of concerns
}
32
jonkv@ida
Game Problem 1

33
Use plain inheritance?
NormalPlayer
GhostPlayer
StrongPlayer
General player code
in NormalPlayer
FastPlayer
Inherit standard behavior,
override
Must replace the player (object)
every time he gets a powerup!
We want one player, whose behavior changes!
jonkv@ida
Game Problem 2

34
Plug in a behavior mode?
public interface PowerupState {
public void jump(Player p);
}
Contains what differs
between powerup modes
public class NormalState implements PowerupState {
public void jump(Player p) {
// Make p jump a short distance
}
}
public class GhostState implements PowerupState {
public void jump(Player p) {
// Significant difference in code: Jump through other objects
}
}
Also: FastState, GhostState, …
jonkv@ida
Game Problem 3

35
Plug in a behavior mode:
public class Player {
private PowerupState pstate;
public Player() {
this.pstate = new NormalState();
}
public void jump() {
…
// Ask the state object to do the actual jump – delegate!
pstate.jump(this);
If you get a new powerup:
…
this.pstate = new FasterState();
}
}
jonkv@ida
Game Problem 4

36
Expressed in UML notation:
Player
PowerupState<<interface>>
pstate: PowerupState
jump()
run()
…
jump()
run()
…
NormalState
jump()
run()
…
GhostState
jump()
run()
…
FastState
jump()
run()
…
jonkv@ida
Game Problem 5

37
Generalization to a State Pattern
Context
+ request()
ConcreteStateA
+ doSomething()
State <<interface>>
+ doSomething()
ConcreteStateB
+ doSomething()
ConcreteStateC
+ doSomething()
jonkv@ida
State Pattern 1

If you only want to represent "which state I'm in":
 No need for State pattern – just use an enum
public enum PlayerMode {
NORMAL,
// Standard mode
STRONGER,
// Stronger and less vulnerable
FASTER,
// Runs faster
GHOST, …
// Can walk, run or jump through solid matter
}

To implement different complex behaviors for each state:
 Could use the state pattern
public interface PowerupState {
public void jump();
}
public class NormalState implements PowerupState {
public void jump() { … }
}
38
jonkv@ida
State Pattern 2

39
Looks similar to Strategy…
 Similar Class diagrams – because we use delegation
 But we use the classes differently!

State:

Strategy:
 There is an object whose behavior
 There is an object whose behavior
 You want to separate code for the
 You want to separate code for the
 Solution: The State pattern,
 Solution: The Strategy pattern,
can be switched dynamically
during the object’s lifetime
▪ Player receives / loses powerups
different behaviors
where the state
is dynamically changed
by the object itself
can be configured
when the object is created
▪ StringSorter needs a sort order
different behaviors
where the strategy
is usually specified once,
generally by the caller
jonkv@ida
State vs. Strategy

40
A class can use several state objects
Player
sstate: StrengthState
vstate: VulnerabilityState
StrengthState<<interface>>
jump()
run()
…
Strength determines what
happens when you jump,
run, …
VulnerabilityState<<int…>>
absorbAttack()
…

Vulnerability determines
what happens when you
are attacked
Avoids the need to create one state for each combination
of strength / vulnerability
jonkv@ida
Several States

41
Inheritance can still be used to avoid code duplication
 But between States, not between Players
PowerupState
AbstractState
NormalState
StrongState
General behaviors inherited
by default
by subclasses
FastState
jonkv@ida
State and Inheritance

42
Use the State pattern only when its flexibility is needed!
public class Player {
private boolean jumpsHigh;
public void jump() {
if (jumpsHigh) {
// Jump a bit higher
} else {
// Ordinary jump
}
}
}
Only two options: High or not high
Localized effects
(here: a single method)
 Stick with a simple flag;
State pattern too complex!
jonkv@ida
Appropriate Use 1

43
Use the State pattern
only to modify behaviors (code)!
public class Player {
private int leapLength;
public void leapForward() {
this.x += leapLength;
}
}
Only a value differs
 Stick with a simple field;
State pattern too complex!
class DefaultState implements StrengthState {
public void leapForward() {
player.x += 10;
} class SuperState implements StrengthState {
}
public void leapForward() {
player.x += 20;
}
}
jonkv@ida
Appropriate Use 2

Consider a spreadsheet
 Edit a value, press enter
▪  Call commitValue(Cell c, Value v)
▪  Must recalculate dependent values

Initial solution:
 Just let commitValue() call recalculate()
45
jonkv@ida
Spreadsheet Problem 1

Weaknesses in the initial solution:
 You have to update the screen as well
▪ Hard-code another call
 A graph may be dependent on this value
▪ Let commitValue() know about graphs
46
Why should commitValue
explicitly know about
these functions?
Violates the Single
Responsibility Principle!
For every new functionality
interested in value changes
you will have to
change commitValue()!
jonkv@ida
Spreadsheet Problem 2

47
Solution, once again: Callback objects
public interface ChangeObserver {
void notifyCellChanged(Cell c);
}
public class Recalculator
implements ChangeObserver
{
public void notifyCellChanged(Cell c) {
// … recalculate dependent values …
}
}
Interface for anyone
interested in changes
public class ScreenUpdater
implements ChangeObserver
{
public void notifyCellChanged(Cell c) {
// … update the screen …
}
}
jonkv@ida
Spreadsheet Problem 3
public interface ChangeObserver {
void notifyCellChanged(Cell c);
}
48
Has a list of interested ChangeObservers
Has no idea there are Recalculators,
ScreenUpdaters, GraphUpdaters, …
 Fewer dependencies, more modular!
public class SpreadsheetPage {
private List<ChangeObserver> observers;
public void addChangeObserver(ChangeObserver obs) {
observers.add(obs);
}
public void commitValue(Cell c, Value v) {
// … make all internal changes …
notifyAllObservers(c);
}
Makes a change,
then calls notification
public void notifyAllObservers(Cell c) {
for (ChangeObserver obs : observers) { obs.notifyCellChanged(c); }
}
}
jonkv@ida
Spreadsheet Problem 4

To use:
SpreadsheetPage page = new SpreadsheetPage();
page.addChangeObserver(new Recalculator());
page.addChangeObserver(new ScreenUpdater());

Similar situations:
 Word processor
▪ Modified:
Text document
▪ Interested:
Spell checker + word counter + text window
 Web browser
▪ Modified:
▪ Interested:
Bookmark list
Multiple windows, each showing bookmarks
49
jonkv@ida
Spreadsheet Problem 5

50
Is there a pattern?
Observable
Observer
Object whose state changes:
Spreadsheet,
Document,
Bookmark list
Objects interested in the change:
Screen updater,
Spell checker,
Word counter
Each observable keeps track of
a set of interested classes:
List<ChangeObserver>,
addChangeObserver(), …
All interested classes
implement an interface:
ChangeObserver, …
When there is a change,
call a method
in every registered observer
The source of the event
does not have to know
who will react or how!
jonkv@ida
Extracting a Pattern

Observer pattern
51
SpreadsheetPage
Called Subject or Observable
ChangeObserver
+notifyCellChanged
Recalculator
+notifyCellChanged
GraphUpdater
+notifyCellChanged
The Subject class only knows
about the general Observer!
These classes are not known
to Subject  looser coupling
jonkv@ida
Observer Pattern
52
Again: Similar class diagram
Again: Different usage
Call methods in a class
where the caller is only aware
of the interface
Call methods in any number of observers
Not delegation: The observers are not
helping the observable achieve its task
but are interested for their own sake
jonkv@ida
Observer Pattern 2


54
User interfaces need to:
Text field
Bar chart
Dropdown
Store
String
Column names,
data points
List of items
Visualize
Rasterize, display
Paint bars, grid lines, … Show list
Manipulate
Accept text input
Drag bars up/down
Could create a single class per component
 Storage, visualization, manipulation
Activate, deactivate,
select item
jonkv@ida
MVC 1: Intro
55
 How to visualize the same data differently?
Reimplement data storage
for each visualization?
 How to visualize the same data in many locations?
Store data in both
the table and the graph?
Difficult to synchronize!
jonkv@ida
MVC 2: Problems

56
Step 1: Extract a model class
 Stores all relevant information
 May support advanced operations on the information
TextModel
class TextModel {
String text;
int
cursorPosition;
void insertAtCursor(String str) { … }
…
}
PlayerModel
class PlayerModel {
int
xpos, ypos;
Orientation
leftright;
List<Package> carrying;
…
}
jonkv@ida
MVC 3: Model

57
Could then use a combined view/controller
TextModel
class TextModel {
String text;
int
cursorPosition;
void insertAtCursor(String str) { … }
…
}
TextComponent
class TextComponent {
TextModel
contents;
// + Visualization
// + Keyboard/mouse input
}
Separation of concerns: Models know nothing about visualization, and vice versa
jonkv@ida
MVC 4: View/Controller

For any Swing component:
 One or more Models define its state
▪ JButton  ButtonModel
▪ JList
 ListModel and ListSelectionModel
 A combined View/Controller defines the graphical interface
▪ How the information in the model is shown
▪ How the information in the model can be manipulated
 Several Views/Controllers can show the same Model
▪ Synchronize a toolbar button and a checkbox menu item
▪ Table data shown in a table view and a graph view
▪ final PlainDocument doc = new PlainDocument();
pane.add("West", new JTextArea(doc, null, 10, 30));
pane.add("East", new JTextArea(doc, null, 10, 30));
58
jonkv@ida
MVC 5: Model + View/Controller in Swing

59
Pure MVC: Separate three distinct responsibilities!
 A model stores all relevant information
 Zero or more views display this information
 Zero or more controllers can alter the information

For example, a Player:
 Model class/object:
▪ Coordinates?
▪ Facing left or right?
▪ Carrying a package?
▪ …
 View class/object:
▪ When requested, reads the model and displays it on screen
 Controller class/object:
▪ One accepts keyboard input; asks the model to move left/right, etc.
▪ One accepts joystick input
Header
x
y
orient
jonkv@ida
MVC 6: Further Separation of Concerns

60
It is useful for the views to be notified about model changes
 This can be done using the Observer pattern
(Keyboard) Controller
(Mouse) Controller
Due to keyboard input,
cell [5,20] should change
Dragged a bar in a chart
 cell [9,13] should change
commitValue()
Model
commitValue()
changes the cell,
notifies all observers
Views do not have to be observers:
Can use other techniques (polling, direct calls)
MVC and Observer are separate patterns
that can be combined if you want
notifyCellChanged()
View
Recalculator
Repaints relevant parts
of the screen
Recalculates part of the
spreadsheet
Some observers
are not views!
jonkv@ida
MVC 7: Notification

In general:
 A factory creates things (objects) for you
 A factory method is a method
that creates things (objects) for you
▪ public class Integer {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
}

In design patterns:
 The factory method pattern is a specific pattern
using factory methods in a particular way
Not all factory methods follow the factory method pattern!
62
jonkv@ida
Terminology

63
A game creating enemy objects:
public class Game {
public void playGame() {
// ...
Enemy enemy = new SimpleEnemy();
// ...
}
}
The concrete enemy class SimpleEnemy
is hardcoded (fixed) at this point
jonkv@ida
Factory Method Pattern 1

64
A "game skeleton" creating enemy objects:
public abstract class Game {
public void playGame() {
// ...
Call a factory method in the same class
Enemy enemy = createEnemy();
Implement this in subclasses
// ...
}
abstract protected Enemy createEnemy();
}
public class SimpleGame extends Game {
protected Enemy createEnemy() {
return new SimpleEnemy();
}
}
public class AIGame extends Game {
protected Enemy createEnemy() {
return new AIEnemy();
}
Subclasses specify the objects (types)
that are used by the superclass
jonkv@ida
Factory Method Pattern 2
65
AbstractProduct <<interf>>
Creator
operation()
factoryMethod()
ConcreteCreator1
<<creates>>
ConcreteProduct1
factoryMethod()
ConcreteCreator2
<<creates>>
ConcreteProduct2
factoryMethod()
ConcreteCreator3
factoryMethod()
(Many concrete game variations
may use the same enemy class)
jonkv@ida
Factory Method Pattern 3

Suppose you are writing a diagram creator such as Dia!
67
jonkv@ida
Intro 1

Users should be able to write plugins providing new shapes
 You provide a shape interface
public interface DiagramShape {
void displayYourself(Component c, int x, int y);
…
}
 A plugin provides concrete classes
public class Diode implements DiagramShape { … }
public class Lamp implements DiagramShape { … }
…
68
jonkv@ida
Intro 2

The shape palette displays all available shapes
 Can't know about plugin shapes in advance
 Provides a method to add them dynamically
public class Palette {
public void addTool(String name, Icon icon);
}
69
jonkv@ida
Intro 3

When you use a plugin:
 It adds shapes and their icons to the palette
 The palette shows the icons
 Clicking the Diode in the palette should insert a Diode in the diagram
▪ But how can the palette create a new Diode object?
70
jonkv@ida
Intro 4

71
The palette can't directly call "new Diode()"
 When the palette code was written, Diode did not yet exist
 So how could the palette class contain the code "new Diode()"?
Standard solution to ”different behavior by type”: Late binding,
but this only works once we have an object…
jonkv@ida
Intro 5

Suppose we give the palette a Diode maker – a factory
 The palette knows about generic ShapeFactories
public interface ShapeFactory {
public DiagramShape createShape();
}
public class Palette {
public void addTool(String name, Icon icon, ShapeFactory factory);
}
 The plugin provides a ShapeFactory for each shape class
public class DiodeFactory implements ShapeFactory {
public DiagramShape createShape() {
return new Diode();
}
}
72
jonkv@ida
Abstract Factory 1
 In the plugin:
void addToolsTo(Palette pal) {
pal.addTool("Diode", diodeIcon, new DiodeFactory());
pal.addTool("Lamp", lampIcon, new LampFactory());
}
 In Palette, when the user has clicked a shape:
DiagramShape newShape =
selectedItem.factory.createShape();
 Uses the abstract factory pattern
▪ Not to be confused with the factory method pattern
73
Palette does not know
the DiodeFactory class –
but it does know
ShapeFactory, which
DF implements
Palette does not know
the Diode class –
but it does know
DiagramShape, which
Diode implements
jonkv@ida
Abstract Factory 2
Abstract specification
of a factory creating
DiagramShapes
74
Palette
Abstract specification
of the product that the
factory can produce
clickedShape()
ShapeFactory <<interf>>
DiagramShape <<interf>>
DiagramShape fig =
item.factory
.createShape();
createShape()
DiodeFactory
Palette does not know
about these concrete
classes
<<creates>>
createShape()
LampFactory
createShape()
displayYourself()
Diode
displayYourself()
<<creates>>
Lamp
displayYourself()
jonkv@ida
Abstract Factory 3: Specific Usage
75
Application
operation()
AbstractFactory <<interf>>
AbstractProduct <<interf>>
create()
(One factory per product, only
responsible for creating that product)
ConcreteFactory1
<<creates>>
ConcreteProduct1
create()
ConcreteFactory2
create()
<<creates>>
ConcreteProduct2
jonkv@ida
Abstract Factory 4: General Pattern

Correct use: Let the needs of the software drive you
 Be aware of patterns
 When implementing, consider whether a pattern is relevant
 If
▪
▪
▪
so, decide whether it is truly useful
For you
In this piece of software
For this particular purpose
  Use it – or don’t!
 Don’t try to squeeze in as many patterns as possible
▪ Class library analogy: ”Oh no! I haven’t used LinkedList!
Maybe I could change the design so that I need linked lists somewhere!”
77
jonkv@ida
Conclusions
Det vi lärde oss mest under projektet
har nog varit objektorientering och hur
ett program blir märkbart mer
modulärt genom designmönster
som verkligen underlättar för en,
speciellt i slutskedet av
kodningsprocessen när man ska minnas
hur alla klasser och metoder hänger
ihop, vad de innehåller och hur de
kommunicerar med varandra.
78
jonkv@ida
Conclusions 2
Som tips till framtida elever så kan vi
rekommendera att verkligen tänka på
att strukturera upp sin kod och följa
designmönster.
Till en början kan det kännas lättare att
göra lite som man vill men det blir
ganska snart ohållbart när man har flera
hundra rader av kod som man ska sätta
sig in i efter att man kanske inte har
arbetat med koden på ett tag, eller om
någon utomstående ska förstå den.
Download