Design Patterns Patterns • 1, 2, 3, … is a sequence that exhibits the pattern: The integers in their natural order.

Download Report

Transcript Design Patterns Patterns • 1, 2, 3, … is a sequence that exhibits the pattern: The integers in their natural order.

Design Patterns
Patterns
• 1, 2, 3, …
is a sequence that exhibits the pattern:
The integers in their natural order
Another Sequence
• 1, 3, 5, …
– The pattern:
The odd integers, in order
What is a Pattern?
• A template or model for imitation
– A dress pattern
– A cookie cutter
– As we shall see, an object-oriented design diagram
• A set of rules
– For the Fibonacci sequence, the rule is:
the ith element (except for f0 and f1) is equal to the sum
of the (i-1)st element and the (i-2)nd element.
Why are Patterns Useful?
• Patterns describing infinite sequences
eliminate having to enumerate all values
0, 1, 1, 2, 3, 5, 8, …
Don’t have to list all the elements
Why are Patterns Useful?
•
A pattern can be a convenient membership test
for a set of elements
– Regular expressions denote patterns of strings
•
For example
[a-zA-Z][a-zA-z0-9]*
is a pattern representing identifiers
– Compilers use regular expressions to test for
identifiers
Why are Patterns Useful?
• Knowledge of a pattern can enable one to
easily produce new objects:
Why are Patterns Useful?
• In particular, patterns discerned from
existing situations may be applied to new
situations
a, b, c, …
Monday, Tuesday, Wednesday, …
But Sometimes the Similarities are
not Obvious
Patterns
and Abstraction
Which Bring
Us To…
• Do
1, 2, 3, …
and
Monday, Tuesday, Wednesday, …
exhibit the same pattern?
Patterns and Abstraction
1, 2, 3, …
the integers in their natural order
Monday, Tuesday, Wednesday, …
the days of the week in their natural order
Are these the same pattern?
Patterns and Abstraction
The pattern we really mean is
The elements of some sequence taken in their
natural order
What about the fact that the integers are infinite
and the days of the week are finite?
Some Patterns are Simple…
ABC
Jackson 5
Michael: you went to school to learn, girl
Things you never, never knew before...
…
…
Jermaine: sit yourself down, take a seat
All you gotta do is repeat after me.
J5: a b c
Michael: easy as...
J5: 1 2 3
Michael: or simple as...
J5: do re mi
Michael: abc, 123, baby, you and me girl!
Others are Less Obvious…
• 2, 3, 5, 7, …
– primes
• March, April, June, …
– Months without a ‘Y’ in their names in natural order
Mensa likes to use patterns like these as part of their
qualification test
Some are Quite Difficult…
• 6, 28, 496, …
– perfect numbers
• e, t, a, …
– most frequent letters in the English language in
descending order
… And Some are Just Downright Ornery
Especially if they’re not mathematical and you don’t know
the context
• 214, 232, 234, …
– Classrooms on 2nd floor of New Ingersoll from east to
west
• 1110, 2210, 3110, 3120, 3130, 3140, …
– For cryin’ out loud– you’re CIS majors!
Patterns Within Patterns
A
B
C
And Now For Something Completely Different…
Summing Integers Read from a File
scanf(“%d”, &i);
while (i >= 0) {
total += i;
scanf(“%d”, &i);
}
Finding the Maximum Double in a File
result = scanf(“%f”, &d);
while (result != EOF) {
if (d > max) max = d;
scanf(“%f”, &d);
}
Building a String from a Line of Characters
i = 0;
C = getchar();
while (c != ‘\n’ && c != EOF)
s[i++] = c;
C = getchar();
}
S[i] = ‘\0’;
Three Individual Pieces of Code…
• Different datum types
– int, double, char
• Different end-of-input conditions
– negative datum, end-of-file, end-of-line/end-offile
• Different tasks:
– summing, maximum, string construction
… and Yet A Pattern Emerges
• In all three cases:
–
–
–
–
Values are read from a file
A condition is used to test for end-of-input
The values are processed in some fashion
A ‘priming-the-pump’ technique is used
An Input Loop Pattern
read first item
while (not end-of-input) {
process item
read next item
}
Why Are Such Patterns Useful?
• Avoids ‘reinventing the wheel’
• Avoids making the same mistakes over and
over again
• Speeds up the development process
• ‘Reusability’ of sorts
Design Patterns
• Pattern concept applied to software design
• Not a finished product
– Reuseability of design ‘ideas’
• Many patterns crop up over and over again
A Non-Software Example- Flywheel
• A flywheel is a massive rotating disk
• The basic idea is to accelerate the
flywheel to a very high speed and thus
maintain the energy as rotational energy
using the disk.
Flywheel - Samples
Why a Flywheel?
• In the 70’s engineers were looking for a lowemissions, non-internal-combustion vehicle.
• Wanted to be able to ‘charge’ the vehicle and have
it store that charge
– Charging involved bringing the flywheel up to a high
speed
• Batteries were too bulky, heavy
– Would need tens of batteries for a small vehicle
Flywheel – Useful For
• Storing kinetic energy – often in a fairly small
space
• Maintaining a uniform force.
• Production of high power pulses
• Can also be used to create a form of gyroscopic
effect
Flywheel – Advantages
• Not affected by temperature changes
• No limit to energy stored
• Simple to measure stored force (measure
rotation speed)
Flywheel – Disadvantages
• Danger of explosive shattering of wheel
Flywheel – Parts
• Massive wheel
• Axle
• Bearings
Flywheel - Applications
• Low-cost toys (the kind you wind up by running the
wheels along the floor)
• Energy-efficient cars (during braking, surplus energy is
used to accelerate the flywheel which can subsequently
power the driveshaft)
• Used on satellites to point the instruments in correct
direction
• Potters wheel
Flywheel - Summary
• Note the variety of applications
• Yet all use the same basic design pattern
Flywheel - Summary
• Notice what we did here
– Provided a motivational situation (low-emission
vehicle)
– Presented the purpose of the flywheel
– Described when to use one
– Presented the parts of the flywheel
– Discussed advantages and disadvantages
– Gave known applications
– Presented some samples
A Simple Software Example
• You’ve got a program from CIS 3130
– Written in C++
– Uses a stack class template
• Which you wrote (whole point of assignment)
• Massive application
– Hundreds of modules
– Thousands of lines of code
– Ok, Ok, two hundred lines of code in one file
– Stack usage scattered throughout system
But FirstA Word From Our Sponsors
A stack class template is a definition of a stack class in which
the element type of the stack is supplied when a particular
stack is declared:
stack<int> si;
stack<ActivationRecord> sa;
Back To Our Simple Software Example
• In 3130, you may have learned about the STL (Standard
Template Library)
– Library of useful data structures, including those you learned in
3130
• You decide you want to play with it
– Good to know for a tech interview
• So you toss out your stack and begin using the one from
the STL
The Problem
• Your stack’s operations:
– push – places argument on top of stack
– pop – pops stack returning value
– isEmpty – true if empty
• STL’s stack’s operations:
–
–
–
–
push – same
peek – returns top of stack
pop – pops stack, no value returned
empty – same semantics
Solution #1
• Change the application code to conform to
the new operations
The Changes
• Replace
s.isEmpty();
// yours
s.empty();
// STL’s
s.pop();
// yours
s.peek();
s.pop();
// STL’s
with
and
with
Global edit replace?
??? !!!
void clear() {
void clear() {
while(!s.isEmpty())
while(!s.empty())
s.pop();
s.peek();
}
s.pop();
}
#^%$!!!
Scratch that solution!!
So??
• What’s Plan B?
Plan B
• Add a new class, StackAdapter
– StackAdapter declares a member variable of type
stack (from the STL).
– StackAdapter defines functions corresponding to
the ones in your original stack class
– Some of the functions do nothing more than call
corresponding functions of the STL stack
– Other functions act as adapters between the old and
new semantics
The StackAdapter Class
template <class E>
class StackAdapter {
public:
push(E val) {s.push(val);}
E pop() {
E x = s.peek();
s.pop();
return x;
}
bool isEmpty() {return s.empty();}
private:
stack<E> s;
}
The Adapter Pattern
• Plan B employs a design pattern known as
Adapter
• The Adapter pattern
Converts the interface of a class into another interface clients
expect. Adapter lets classes work together that otherwise couldn’t
because of incompatible interfaces
The Adapter Pattern Visually
StackAdapter
x = s. pop()
s.pop();
return x;
push()
pop()
isEmpty()
s.isEmpty()
s
Stack
push()
pop()
empty()
You May Have Seen Something Similar
• For example, when coding binary search…
– The recursive call for binary search is
bool binsrch(int a[], int lo, int hi)
… but the user wants to make the call
bool binsearch(int a[], int n)
– We resolve this by adding an intermediate function:
bool binsearch(int a[], int n) {
return binsrch(a, 0, n-1);
}
This is the a procedural analogy of the Adapter pattern;
binsearch is usually called a wrapper function.
Design Patterns
• Introduced by architect Christopher Alexander (A
Pattern Language: Towns, Buildings, Construction) in
the context of buildings and towns:
“Each pattern describes a problem which occurs over and
over again in our environment, the describes the core of
the solution to that problem, in such a way that you can
use the solution a million times over, without ever doing it
the same way twice.”
Architectural Design Patterns
• A PLACE TO WAIT
– Bus stop
– Waiting room
• adresses the common aspects of their design
• ARCADES- “covered walkways at edges of buildings, partly
inside, partly outside”
http://architypes.net/patterns.php
‘The Gang of Four’ Book
• Introduced design patterns
to software design
• It’s fair to say that one
purpose of this talk is to
provide a guide to how to
read this text
• Bulk of text is a catalog of
patterns
Why Only ‘Object-Oriented’?
• Wouldn’t this have been useful before as well?
– ‘Designing object-oriented software is hard, and designing
reusable objected-oriented software is even harder’
(Opening sentence of ‘Design Patterns’, Gamma, et al)
• The number and complexity of classes, objects and their
interactions makes proper design a formidable task
– Also, might have been applicable before, but OOP
(compared to say, procedural) ‘maxes’ out on
reusability
• More opportunities for reuseable design
• Everybody says that, but let’s see why
OOP and Reusability
• So WHAT makes objected-oriented software more
reusable than say applications designed and coded
in a procedural style?
–
–
–
–
Classes?
Inheritance
Overloaded operators?
Access control?
Reusability Mechanisms – Inheritance
• Allows one class to incorporate (reuse) another class’s implementation
as part of its own
• All state (variables) and behavior (functions) of the existing
(super/base/parent)class become part of the new (sub/child)class.
• Subclass can then add its own state/behavior
• The subclass is said to be a subtype of the superclass’ type
Not available in traditional procedural languages
Reusability Mechanisms - Inheritance
class Counter {
Counter() {val = 0;}
void up(val++;}
void down() {val--;}
int get() {return val;}
class BoundedCounter
extends Counter {
BoundedCounter(int m) {
max = m;
}
int val;
}
void up() {
if (val < max) val++;
}
int max;
}
Reusability Mechanisms - Composition
• Assembling or composing objects to get new functionality
• Basically one class contained as a variable of another
• Reusability comes from
–
The containing object (re)using the functionality of the contained
object(s)
• … and thus avoiding implementing that behavior on its own
–
Somewhat available in traditional procedural languages
Reusability Mechanisms - Composition
class SSNum {
boolean equals(SSNum other) {
…
}
…
}
class Employee {
boolean equals(Employee other) {
return id.equals(other.id);
}
…
SSNum id;
}
Reusability Mechanisms - Delegation
• One object carries out a request by delegating it to a second object
(typically an instance variable of the first)
• Used widely in the context of composition, especially as a way of
obtaining some of the flavor of inheritance
• In the previous example, the Employee object delegated the equality
test to the (composed) Name object.
Somewhat available in traditional procedural languages
Reusability Mechanisms - Interfaces
• A set of function signatures (no bodies)
• Can be thought of as representing a type
• Can be specified in C++ via abstract classses and in Java via interfaces.
• A class (i.e., with function bodies) is said to implement the interface if
the class defines (I.e., supplies bodies for) all the interface’s functions
–
As with inheritance, the implementing class is also said to be a subtype of
the interface’s type
–
Not available in traditional procedural languages
Reusability Mechanisms - Interfaces
interface Collection {
boolean add(Object obj);
boolean remove(Object obj);
int size();
boolean isEmpty();
}
The Is-a Relationship
• An object of a subtype is compatible with the
corresponding (parent) type.
• The object of the subtype is considered an object of the
type as well
• This is known as the ‘is-a’ relationship…
• … and is probably the basis for much of the reusability of
OOP
‘Is-a’ in the Context of Inheritance
• An object of a subclass is compatible with the
parent class’ type
– Thus given a Counter variable, a BoundedCounter
object can be assigned to that variable:
BoundedCounter bctr;
Counter ctr = bctr;
‘Is-a’ in the Context of Interface
• An object of a class implementing an interface is
compatible with the interface’s type
– Thus assuming Set implements the Collection
interface. given a Collection variable, a Set object
can be assigned to that variable:
Set set;
Collection coll = set;
So What???
•
Given an interface, Collection, write a method, contains that returns true
if a specified object belongs to a specified collection.
boolean contains(Collection coll, Object obj) {
for(Object element : coll)
if (element.equals(obj)) return true;
return false;
}
contains can accept (as the coll parameter) a Set, a Vector, in fact any
class that implements the Collection interface.
Only one contains function need be written for a whole family of different
(though related by the Collection interface) aggregate classes
Describing Design Patterns
• Recall our Flywheel
• In addition to presenting the flywheel we
also presented
–
–
–
–
Motivation
Purpose
Application
Advantages/disadvantages
Describing Design Patterns
• Design Patterns are presented in a similar
(fairly standardized) fashion
Pattern Description - Name
• Name should be a good reflection of the
pattern’s purpose
Adapter
Pattern Description - Intent
• A statement answering:
– What does the pattern do?
– What is its rationale?
– What problem does it address?
Adapter converts the interface of a class into another interface clients
expect. Adapter lets classes work together that otherwise couldn’t
because of incompatible interfaces
Pattern Description – Also Known As
• Other names by which the pattern is known
Wrapper
Pattern Description – Motivation
• A scenario that illustrates the problem and its
solution via the classes and structures of the
pattern
Our CIS 3130/STL Stack problem and solution
Pattern Description – Applicability
• Under what circumstances can the pattern be applied?
• What are examples of poor designs that the pattern can
address?
• How can such situations be recognized?
Use Adapter when:
– You want to use an existing class and its interface does not match
the one you need
Pattern Description – Structure
• A graphical representation of the relationships among the
classes/objects in the pattern
Client
Class
Interface
Implemented method()
Abstract method()
Target
Adaptee
Request()
SpecificRequest()
Adapter
adaptee
method pseudo-code
Request()
Instance variable
subtype
adaptee.specificRequest()
Pattern Description – Participants
• The classes and objects participating in the pattern
– Target (the interface consisting of push, pop, isEmpty)
• Defines the specific interface that Client uses
– Client (Your 3130 program)
• Interacts with objects conforming to the Target interface
– Adaptee (STL stack type)
• Defines an existing interface that needs adapting
– Adapter (StackAdapter)
• Adapts the interface of Adaptee to the Target interface
Pattern Description – Collaborations
• How the participants interact to realize the pattern
– Clients call operations on an Adapter object
• In turn the adapter calls Adaptee operations
Pattern Description – Consequences
• What are the trade-offs and results of using the pattern?
– How much adapting does Adapter do?
• Simple name changes all the way to supporting completely different
set of operations
There are several other consequences we can’t address here
Pattern Description – Implementation
• Pitfalls, hints, techniques?
• Language-dependent issues?
Fairly straightforward
There are some language issues, but again, not for now
Pattern Description – Sample code
We’ll just let our example be the sample code
Pattern Description – Known uses
• Examples from real systems
Take a look at Gamma
Pattern Description – Related Patterns
•
•
•
•
What other patterns are closely related to this one?
What are the similarities? Differences?
Which patterns use this pattern?
Which patterns does this pattern use?
This was our first one!
Too early to supply answers for this
Why are Design Patterns Useful?
• Avoids ‘reinventing the wheel’
• Avoids making the same mistakes over and over again
• Knowledge of a particular design pattern (like Adapter)
is valuable…
• … but so is simply knowing about the concept of a
design pattern
• Knowing there are catalogs of patterns addressing
design issues
• Gets you thinking about design problems differently
Another Example
• Adapter was an example of a Structural design
pattern
– Structural patterns are concerned with how classes and
objects are composed to form larger structures.
• Our next example will present a Creational design
pattern
– Creational patterns help make a system independent of
how its objects are created and represented.
Another Example
• Us folks at
– Provide interactive programming exercises
– Students submit solution code to our server which
• Does operational and textual checks
• Provides feedback
– In addition, there is a context-sensitive glossary/help
system
• Generates hypertext links into the glossary, ‘on-the-fly’, for
instructions and feedback
Creating a Glossary Object
Glossary glossary = new Glossary();
• Issues
– Glossary is quite large – one is OK, but what if
there are tens (or hundreds) of concurrent
users?
– No need for more than one glossary
• It’s a query-only object
Creating a Glossary Object
• Is there a way to prevent more than one Glossary object
from being created?
– The expression
glossary = new Glossary()
creates a new instance each time
• And if we can restrict to one instnacnce, how does the rest of
the application access that single instance?
– C++ could use a global variable.
– What about Java?
Singleton Design Pattern - Intent
Ensure a class has only one instance and
provide a global point of access to it
Singleton - Motivation
• Just gave it to you, but since you asked
– Ensuring single print spooler in a system
– Ensuring a single buffer/node pool
Singleton - Applicability
• Applicability:
– Used when
• There must be exactly one instance, which must be
accessible from a well-known point
Singleton - Structure
Singleton
static instance()
singletonOperation()
getSingletonData()
static uniqueInstance
singletonData
return uniqueInstance
Singleton - Participants
• Participants
– Singleton
• Defines an instance operation that allows
clients to access its unique instance
– instance is a class function/method
• May be responsible for creating its own unique
instance
Singleton - Collaborations
• Collaborations
– Clients access a Singleton instance solely
through the instance operation
Singleton - Consequences
• Controlled access to single instance
• Reduced name space
– No global variables
• Permits variable number of instances
– I lied– it’s actually one glossary per language
glossary = new Glossary(“Java”)
Singleton - Implementation
class Glossary {
public static Glossary getGlossary() {
if (glossary == null)
glossary = new Glossary();
return glossary;
}
private Glossary();
private Glossary glossary;
}
Note the private Glossary constructor
Behavioral Patterns
• Structural and creational patterns are two of the three
pattern purposes, the third being behavioral patterns
• Behavioral patterns are concerned with
–
–
–
–
algorithms
the assignment of responsibilities between objects
the patterns of communication between objects/classes
characterize complex control flow
• We’ll now present Observer, a baehavioral pattern and in
IMHO, one of the most beautiful patterns of all
This is not Your Father’s Oldsmobile
• On a typical mid-to-high-end car these days
–
–
–
–
–
–
rain sensors turn on the wipers
wipers turn on the lights
shifting out of park turns on day running lights
turning on radio raises antenna
pressing brake disengages cruise control
and a host of other interactions between
sometimes seemingly unrelated components
One Particular Set of Interacting Components
• Let’s focus on just three components
– The interior light
– The interior light switch
• Turning to ‘on’ turns on the interior light
– The car door
• Opening turns on the interior light
Let’s Code a Car Class
class Car {
…
Door door = new Door();;
InteriorLightSwitch interiorLightSwitch =
new InteriorLightSwitch();
InteriorLight interiorLight = new InteriorLight();
}
The InteriorLight Class
class InteriorLight {
public boolean isOn() {return amOn;}
void setOn(boolean b) {
if (amOn != b) {
amOn = b;
System.err.println("interior light turned “ +
(amOn ? "on" : "off"));
}
}
boolean amOn = false;
}
The InteriorLightSwitch Class
class InteriorLightSwitch {
public boolean isOn() {return amOn;}
void setOn(boolean b) {
if (amOn != b) {
amOn = b;
System.err.println("interior light switch moved to " +
(amOn ? "on" : off"));
interiorLight.setOn(amOn);
}
}
boolean amOn = false;
}
The Door Class
class Door {
public boolean isOpen() {return amOpen;}
void setOpen(boolean b) {
if (amOpen != b) {
amOpen = b;
System.err.println("door " + (amOpen ? "opened" : "closed"));
interiorLight.setOn(amOpen);
}
}
boolean amOpen = false;
}
Mind Your Own Business
• Door knows it should turn on light
• Interior switch knows it should turn on light
• An alarm module (keyless entry) would also
have to turn on light
Who should know when to
turn on the interior light?
More Issues
• In ‘luxury’ model opening door causes seat to
slide back
– Now door must know to turn on light and slide seat
back
• But what about non-’luxury’ cars?
– Separate door mechanism for luxury/non-luxury?
– Luxury/non-luxury models ‘wired’ differently?
‘Spaghetti’ Responsibility Logic
• Turning on wiper switch
– Must know to turn on wipers
– Wipers in turn must know to turn on headlights
and activate 4WD sensor
– Headlights must know to dim radio display
‘Spaghetti’ Responsibility Logic
• Pressing brake
– Turns on ‘upper rear brake light’
– Turns on brake lights
– Disengages cruise control, but only if that
option is present
– Initiates ALB sensor
‘Spaghetti’ Responsibility Logic
• Every component must know about all
components dependent upon it
– Furthermore, every component becomes
responsible for those components
Still Not Convinced??
Well how about if I tell you that our
implementation is wrong?
A Sample Car interaction
public static void main(String [] args) {
Car car = new Car();
car.door.setOpen(true);
System.err.println(car);
car.door.setOpen(false);
System.err.println(car);
car.interiorLightSwitch.setOn(true);
System.err.println(car);
car.door.setOpen(true);
System.err.println(car);
car.door.setOpen(false);
System.err.println(car);
}
The Output
door opened
interior light turned on
door: opened / interior light:
door closed
interior light turned off
door: closed / interior light:
interior light switch moved to on
interior light turned on
door: closed / interior light:
door opened
door: opened / interior light:
door closed
interior light turned off
door: closed / interior light:
on / interior light switch: off
off / interior light switch: off
on / interior light switch: on
on / interior light switch: on
off / interior light switch: on
OOP = Responsibility-Driven Programming
• Goal is for objects (components) to be
responsible for themselves
• ‘Decoupling’ objects simplifies the design
• The simpler, more self-responsible objects
are easier to reuse
The Observer Design Pattern - Intent
Define a (many to one) dependency between objects
so that when one object changes state, all its
dependents are notified and updated automatically
Observer - Applicability
• Use Observer when either
– A change to one object requires changing others, and
you don’t know how many others need to be changed
– An object should be able to notify other objects without
making assumptions about who those objects are
(minimize coupling between the objects)
Observer - Structure
The Observer Interface
interface Observer {
void update(Observable observable, Object arg);
}
• An Observer’s update method is called (by the observable
object) when the observable object has changed. arg is an
optional argument containing any additional useful information
about the change.
The Observable Superclass
class Observable {
void addObserver(Observer observer) {
observers.add(observer);
}
void notifyObservers(Object arg) {
for (observer : observers)
observer.update(this, arg);
}
void notifyObservers() {notifyObservers(null);}
Set<Observer> observers = new HashSet<Observer>();
}
The InteriorLight Class
class InteriorLight implements Observer{
InteriorLight() {
interiorLightSwitch.addObserver(this);
door.addObserver(this);
}
public boolean isOn() {…}
private void setOn(boolean b) {…}
public void update(Observable observable, Object arg) {
setOn(interiorLightSwitch.isOn() || door.isOpen());
}
boolean amOn = false;
}
The InteriorLight Class
class InteriorLight implements Observer{
InteriorLight() {…}
public boolean isOn() {return amOn;}
private void setOn(boolean b) {
if (amOn != b) {
amOn = b;
System.err.println("interior light turned " +
(amOn ? "on" : "off"));
}
}
public void update(Observable observable, Object arg) {…}
boolean amOn = false;
}
The InteriorLightSwitch Class
class InteriorLightSwitch extends Observable {
public boolean isOn() {return amOn;}
void setOn(boolean b) {
if (amOn != b) {
amOn = b;
System.err.println("interior light switch “ +
moved to " +
(amOn ? "on" : "off"));
notifyObservers();
}
}
boolean amOn = false;
}
The Door Class
class Door extends Observable{
public boolean isOpen() {return amOpen;}
void setOpen(boolean b) {
if (amOpen != b) {
amOpen = b;
System.err.println("door " +
(amOpen ? "opened" : "closed"));
notifyObservers();
}
}
boolean amOpen = false;
}
The Output This Time
door opened
interior light turned on
door: opened / interior light: on / interior light switch: off
door closed
interior light turned off
door: closed / interior light: off / interior light switch: off
interior light switch moved to on
interior light turned on
door: closed / interior light: on / interior light switch: on
door opened
door: opened / interior light: on / interior light switch: on
door closed
door: closed / interior light: on / interior light switch: on
interior light switch moved to off
interior light turned off
door: closed / interior light: off / interior light switch: off
Minimizing Hard-coding
• The CodeLab engine can check exercises for any language
that has a compiler
• An appropriate set of tools and entities– compilers, linkers,
compiler message analyzers, glossaries – must be created
specific to the language
• We want this done without hard-coding any knowledge of
particular languages into the engine
• This is accomplished using the Factory Method pattern
Maintaining Consistency
• Furthermore, the language-specific tools used in an
exercise must be consistent with each other (i.e., be
restricted to tools of that specific language).
• This is addressed using the Abstract Factory pattern
• This design was introduced into the engine by Josh
Goldsmith as part of his 88.1 project.
Invoking the Tools
• Many of the tools used to build and test exercises form
tree-like hierarchies of sorts
Java Tool
C++ Tool
Java Compiler Tool
C++ Compiler Tool
Java Interpreter Tool
C++ Linker Tool
Executable Tool
• A Java Tool is executed by executing a Java Compiler Tool
followed by executing a Java Interpreter Tool
• Similarly for the C++ Tool
Treating Individual Objects and Compositions
Identically
• Sometimes a full Java Tool is launched
• Other times simply the Java Compiler Tool
• We want to launch and subsequently process both
tools (the composite Java Tool and the atomic Java
Compiler Tool) identically
• This is achieved using the Composite pattern
Filtering Streams
• When processing submissions and exercise output,
we often want to
–
–
–
–
–
Remove whitespace completely
Remove a final trailing linefeed
Compress multiple whitespace to a single whitespace
Remove comments
Ignore case
• This is done using the Strategy pattern
Iteration
• Any class implementing the Collection
interface must supply a uniform means of
iterating over its elements.
• This is done via the Iterator pattern
Whew!
• In summary
– Design patterns provide highly flexible, reusable
solutions to commonly arising design situations
– Patterns are recognized as valuable repositories of
information based upon analysis and experience
– Catalogs exist enumerating collections of patterns
– Conscious use of patterns is widespread