Upload
others
View
5
Download
0
Embed Size (px)
Citation preview
1
Computing Science & MathematicsUniversity of Stirling
1
Design Patterns ITNP90 - Object Oriented Software Design
Dr Sandy Brownlee4B69 [email protected]
Computing Science & MathematicsUniversity of Stirling
2
Design Patterns: overview• Introduction
• Managing change• Concepts & principles• Design Patterns
• Structural Patterns• Composite• Adapter• Decorator
• Behavioural Patterns• Strategy• Observer • Command
• Creational Patterns• Singleton• Factory
Computing Science & MathematicsUniversity of Stirling
MotivationWhat's the one constant in software development?
We will illustrate this with an example…
DuckSim is a game that shows various ducksswimming around a virtual pond.
It was built using OO techniques,With all duck types inheriting fromThe Duck superclass.
The head of the company wantsa new feature: flying ducks.
Example from: Head First Design Patterns, Freeman et al
3
Change
Computing Science & MathematicsUniversity of Stirling
Managing change
We could add a fly() methodto Duck, so that all the ducktypes inherit the ability to fly:an example of reuse
4
2
Computing Science & MathematicsUniversity of Stirling
5
Managing changeUnfortunately, we didn't notice a recentaddition to the game: rubber ducks. Nowthey are flying too!
This is an unintended side-effect of ourchange, arising from the use of inheritance
We could override fly() for inanimate ducks,but this means duplicate code, defeatingthe point of inheritance.
Computing Science & MathematicsUniversity of Stirling
Managing changeWe could specify an interface, which only flying ducks implement. The same could apply to quack() to allow silent ducks. What is wrong with this design?
6
The problem is that now we have to implement a fly() method for every flying duck. This gets more complicated if other methods like quack() and swim() are optional too.
Computing Science & MathematicsUniversity of Stirling
Managing changeWe need a way to capture the parts of the system that vary and separate them out from the parts that stay the same.
We know that we have some duck behaviours which vary:• A duck's flying behaviour can be to fly, or not fly• A duck's quacking behaviour can be to quack, squeak, or be silent
Let's capture these behaviours in separate classes:
7 Computing Science & MathematicsUniversity of Stirling
Managing changeWe can now change the relationship so "duck type" HAS-A "behaviour":
8
3
Computing Science & MathematicsUniversity of Stirling
Managing changeWe have used a technique called composition to get the desired behaviour for our ducks.
This means that we can all the benefits of reuse, but also greatly increased flexibility. We can even change the behaviours of a duck at runtime!
We've also seen two design principles which are very useful in designing applications that are more maintainable:
1. Program to an interface, not an implementation
2. Favour object composition over inheritance
These are not hard rules, but useful guidelines for developing an OO system.
9 Computing Science & MathematicsUniversity of Stirling
Principle 1"Program to an interface, not an implementation"
• In our DuckSim example, the Duck superclass has a reference to an object implementing the FlyBehaviour interface
• The Duck object calls the fly() method in this interface• What actually happens is dependent on whether the object is a
FlyWithWings or FlyNever object; the duck is unaware of the specific implementation that is used
In practice, what this means is that we should always use variables with as abstract a type as possible. This reduces dependence on implementations, making the code more open to future changes.
Note: here, "interface" really means supertype: it could be an Interface, an abstract class or a concrete class in practice (don't confuse with the Java "interface")
10
Computing Science & MathematicsUniversity of Stirling
Principle 1"Program to an interface, not an implementation"
An example based on DuckSim:
FlyWithWings myBehaviour = new FlyWithWings();
we will be tied to using the FlyWithWings implementation. This might lead to problems if we change to FlyNever in the future. We should do this:
FlyBehaviour myBehaviour = new FlyWithWings();
This will allow us to easily change implementation without changing the code:
FlyBehaviour myBehaviour = new FlyNever();
11 Computing Science & MathematicsUniversity of Stirling
Principle 1"Program to an interface, not an implementation"
Example for the Java programmers: our application needs a reference to a List to store several objects. If we write this:
ArrayList myList = new ArrayList();
we will be tied to using the ArrayList implementation, which is good for random access, but not so good at accommodating growth. We should do this:
List myList = new ArrayList();
This will allow us to easily change implementation without changing the code:
List myList = new LinkedList();
12
4
Computing Science & MathematicsUniversity of Stirling
Principle 2"Favour object composition over class inheritance"
• In our DuckSim example, we composed the behaviour for the ducks, by using references to Behaviour objects.
• Duck objects delegate the implementation of flying and quacking to their Behaviour object
Inheritance has its place: it is defined statically at compile-time (so your IDE can help you) and is straightforward to use. However, it can lead to side-effects if we override some parts of behaviour but not others.
Object composition allows new functionality to be obtained by assembling or composing existing objects to get more functionality. It can be done dynamically at run-time, and allows for much greater flexibility.
13 Computing Science & MathematicsUniversity of Stirling
14
Re-using experienceSo far in this module, we have covered several aspects of OO design: object modelling, class / use-case / state / sequence diagrams.
We have seen some pitfalls of a poor object model, and principles that can help, but in general, how do we decide what makes a good object model?
– What guidance do we have about the kind of object that we expect to find in a object model?
– It is always much easier to solve a problem if you have previously solved similar problems or, at least, have access to a solution to a similar problem.
– The final duck behaviour solution was not obvious, but it might be useful in other contexts. In fact, it was an example of a Design Pattern – specifically, the Strategy Pattern.
– Design Patterns are a major topic in object modelling. They allow beginners to benefit from the previous experience of experts when solving problems.
Computing Science & MathematicsUniversity of Stirling
15
Design Patterns
[1977] Alexander's work on urban planning and building architecture: Patterns have a tradition in design/project disciplines.
[OOPSLA 1987] Beck & Cunningham the idea of applying patterns to programming at the ACM Object Oriented Programming Systems and Applications conference.
[1995] Gamma, Helm, Johnson & Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. Often referred to as the GoF (Gang of Four) book:this book made design patterns popular in computer science
Lots of online documentation online: try to find your sources!
Computing Science & MathematicsUniversity of Stirling
16
Design PatternsUML designers' definition:
"A pattern is a common solution to a common problem in a given context.”
Wikipedia's definition:
"In software engineering, a design pattern is a general reusable solution to a commonly occurring problem in software design. ... is not a finished design ... it ... is a description or template for how to solve a problem that can be used in many different situations.
Object-oriented design patterns typically show relationships and interactions between classes or objects."
(Compare these definitions with the usage of the MVC framework for the Noughts and Crosses case study).
5
Computing Science & MathematicsUniversity of Stirling
17
Design Patterns
Following the principle of reuse, it would be wise to precisely describe such a pattern, or better, a well-tested and commonly agreed version of such a pattern, so as to be able
– to easily recognise when it can be applied to a given scenario, and
– to straightforwardly apply it to our system design.
More in general it would be desirable to have a library or catalogue of such design patterns. These embed the principles we discussed earlier.
As well as general catalogues, many have been developed for specific domains: user interfaces, concurrency, … , metaheuristics (MetaDeeP workshops)
Computing Science & MathematicsUniversity of Stirling
18
Description of PatternsPattern catalogues have been produced. Several different descriptions of design patterns, some prevailing in practice, e.g. from Design Patterns (GoF):
Pattern name: A descriptive and unique name. Intent: Goals of the patterns and reasons for using it.Motivation -forces: A scenario, ie a problem and a context, in which the pattern can be used.Applicability: Situations and contexts for pattern usage.Structure: A graphical representation (Class and Interaction diagrams).Participants: Cases and objects used in the pattern and their roles.Collaboration: How patterns' classes and objects interact with each other.Consequences: A description of the results, side effects, and trade offs of the pattern.Implementation: A description of an implementation of the pattern. Sample Code: An illustration the pattern's uses in programming languages.Known Uses: Examples of real usages of the pattern.Related Patterns: Other similar patterns.
Computing Science & MathematicsUniversity of Stirling
19
Classes of Patterns
Main classification:
Behavioural Patterns (interaction, eg. Observer),
Structural (class/objects structure, eg. Adapter, Composite) and
Creational (creating objects, eg. Factory Method).
Computing Science & MathematicsUniversity of Stirling
Selecting a design pattern• Consider what changes and what is static in your application
• Scan the intent sections in the catalogue, then motivation and applicability for confirmation
• Study how the patterns interrelate: the GoF patterns have a related patterns section and an overall illustration
• Study patterns of a similar purpose / class
20
6
Computing Science & MathematicsUniversity of Stirling
21
Design Patterns: overview• Introduction
• Managing change• Concepts & principles• Design Patterns
• Structural Patterns• Composite• Adapter• Decorator
• Behavioural Patterns• Strategy• Observer • Command
• Creational Patterns• Singleton• Factory
Computing Science & MathematicsUniversity of Stirling
22
Design Patterns
Strategy
Computing Science & MathematicsUniversity of Stirling
StrategyMotivation (scenario): • Many algorithms exist for doing the same job• E.g. sorting a list; encrypting data; layout of GUI components• We want to be able to easily change from one algorithm to another
We used the Strategy Pattern to solve our DuckSim problem earlier.
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.
23 Computing Science & MathematicsUniversity of Stirling
Strategy• Taking the earlier example, and making it more general:
• Behaviours become algorithms• Duck becomes the context
24
7
Computing Science & MathematicsUniversity of Stirling
StrategyStructure• Each algorithm or strategy is wrapped up in a class that implements a
common interface• The Context class is configured with a Strategy object, and keeps a
reference to it• Context might also define an interface that lets strategy access its data
25 Computing Science & MathematicsUniversity of Stirling
Comparing objectsA commonly-used example of the Strategy pattern in Java is the Comparator interface. This is used to compare two objects A & B, returning:• Negative if A < B• Zero if A == B• Positive if A > B
List<Integer> l =
Arrays.asList(new Integer[]{3,7,4,9,4,6,2});
Collections.sort(l, new Ascending());
…
public class Ascending implements Comparator {
public int compare(Object o1, Object o2) {
return ((Integer)o1) - ((Integer)o2);
}
}
public class Descending implements Comparator {
public int compare(Object o1, Object o2) {
return ((Integer)o2) - ((Integer)o1);
}
}26
Computing Science & MathematicsUniversity of Stirling
27
Design Patterns
Observer
Computing Science & MathematicsUniversity of Stirling
28
ObserverAlso known as: publisher–subscriber; listener
Motivation (scenario): – An object needs to inform other objects when it has changed– Observers register with a subject: when the subject has some new information,
all its observers are informed.– We have the interfaces Subject and Observer and the concrete classes
ConcreteSubject and ConcreteObserver.
The Observer Pattern defines a one-to-many dependency between objects so that
when one object changes state, all its dependents are notified and updated
automatically.
8
Computing Science & MathematicsUniversity of Stirling
29
ObserverStructure:
– A ConcreteObserver object registers with a ConcreteSubject object using the registerObserver() method defined in the superclass Subject.
– Each ConcreteSubject object maintains a list of all the ConcreteObserverobjects that have registered with it.
Computing Science & MathematicsUniversity of Stirling
30
ObserverStructure:
– The writer of the ConcreteSubject subclass does not need to know anything about the details of ConcreteObserver
– When a ConcreteSubject object has new information, it calls its notifyObservers() operation (defined in its Subject interface).
Computing Science & MathematicsUniversity of Stirling
31
ObserverStructure:
– This causes an update message to be sent to all its registered subscribers.– Each ConcreteObserver object can then call the getState() operation in
the ConcreteSubject object to get the new information.
Computing Science & MathematicsUniversity of Stirling
32
ObserverThere are two important sequence diagrams for Observer:
Register Update
Clearly, Observer is very similar to the MVC.
notifyObservers
9
Computing Science & MathematicsUniversity of Stirling
33
The class diagram on this slide represents the support for the Observer pattern, from a Java perspective.
The abstract superclassObservable (subject) and the interface Observer are defined in the Java library.
http://download.oracle.com/javase/6/docs/api/java/util/Observable.html
Observer Pattern and Java
Computing Science & MathematicsUniversity of Stirling
34
Graphical User Interfaces
A program with a Graphical User Interface (GUI) is event-driven.
– It waits for an event (such as a button press) to occur, handles the event and then waits for the next event.
– Events are handled within an event loop.
This is a natural application for the Observer design pattern.
Computing Science & MathematicsUniversity of Stirling
35
Graphical User InterfaceExample: as seen, in Java to be able to handle events an object must be declared
to be an EventListener (i.e. like an observer) and must register itself with the graphical objects (the subjects) that can generate the events in which it is interested.
This can be easily be thought in terms of Observer:
For instance (considering a generic Boundary class)
public class Boundary …implements ActionListener
...
(ActionListener is a sub interface of EventListener)
ActionListener
Computing Science & MathematicsUniversity of Stirling
36
Graphical User Interface... and a Boundary object that registers itself with a graphical component Button component, a aButton object say, by calling
aButton.addActionListener(this);
The aBoundary event listener object then contains the definition of anappropriate event handler, e.g. in our case the method
void actionPerformed
(ActionEvent e)
When a button is pressed, the aButton object informs all its registered event listeners by calling their actionPerformedmethod.
ActionListener
10
Computing Science & MathematicsUniversity of Stirling
37
Design Patterns
Command
Computing Science & MathematicsUniversity of Stirling
CommandAlso known as: Transaction
Motivation (scenario): • We want to issue requests to objects without knowing anything about
them (what they do, how they do it)• For example: a user interface library will have menu item objects that will
perform an action when clicked on: but they won't have the action implemented directly within them
• We might also want to keep track of requests, so we can log them, queue them, or undo them (roll them back)
The Command Pattern encapsulates a request as an object, allowing us to: parameterise clients with different requests; queue or log requests; and support undoable operations.
38
Computing Science & MathematicsUniversity of Stirling
Command• Command looks quite similar to Observer• Structurally, it is quite similar, but they differ in intent• Command models an action that you need more to do with than just
execute it now• Observer is used when a Subject needs to notify other objects about
events, but doesn't know which objects to notify
39 Computing Science & MathematicsUniversity of Stirling
CommandStructure• The Invoker holds a command and at some point asks the command
to carry out a request by calling its execute() method• The ConcreteCommand represents a binding between an action and
the receiver. When the Invoker makes a request by calling execute(), ConcreteCommand carries it out by calling methods on the Receiver
40
11
Computing Science & MathematicsUniversity of Stirling
CommandStructure• The Receiver class actually does the work: it can be anything• The Client creates a ConcreteCommand object and sets its receiver
41 Computing Science & MathematicsUniversity of Stirling
CommandSequence diagram
42
new Client() user
invoke()
Computing Science & MathematicsUniversity of Stirling
Example: universal controller• We have a Light class, the Receiver• RemoteControl is the invoker
RemoteControl rc = new RemoteControl();
rc.setCommand("Button1", new LightOn());
rc.setCommand("Button2", new LightOff());
------------------------------------------
private interface Command {
public void execute();
public void undo();
}
------------------------------------------
public class LightOn implements Command {
public void execute() {
Light.on();
}
public void undo() {
Light.off();
}
}43
maplin.co.uk
Button1
Button2
Button3
Computing Science & MathematicsUniversity of Stirling
Example: universal controller• We have a Light class, the Receiver• RemoteControl is the invoker
RemoteControl rc = new RemoteControl();
rc.setCommand("Button1", new LightOn());
rc.setCommand("Button2", new LightOff());
------------------------------------------
private interface Command {
public void execute();
public void undo();
}
------------------------------------------
public class LightOff implements Command {
public void execute() {
Light.off();
}
public void undo() {
Light.on();
}
}44
maplin.co.uk
Button1
Button2
Button3
12
Computing Science & MathematicsUniversity of Stirling
Example: universal controller• The invoker will always call execute()• This could cause an exception if the slot is
empty (NullPointerException in Java)• The DoNothing class can be used to fill
blank slots. It simply does nothing when execute() is called.
• DoNothing is an example of a Null Object: null objects are sometimes listed as design patterns themselves
rc.setCommand("Button3", new DoNothing());
------------------------------------------
public class DoNothing implements Command {
public void execute() {
/*do nothing*/
}
public void undo() {
/*do nothing*/
}
}45
maplin.co.uk
Button1
Button2
Button3
Computing Science & MathematicsUniversity of Stirling
CommandDCommandF
CommandHow might we implement an undo function?
46
• Use a stack• This is a last-in-first-out data structure • Each time we run a command, we call
execute() on it and "push" it onto the stack• Each time we undo, we "pop" the top command
of the stack, and call undo() on it• This can mean that commands need to store
the previous state so it can be retrieved as part of undo()
CommandA
CommandB
CommandC
CommandECommandG
Computing Science & MathematicsUniversity of Stirling
47
Design Patterns: overview• Introduction
• Managing change• Concepts & principles• Design Patterns
• Structural Patterns• Composite• Adapter• Decorator
• Behavioural Patterns• Strategy• Observer • Command
• Creational Patterns• Singleton• Factory