Idioms and patterns
2
- polymorphism -- inheritance and delegation
- idioms -- realizing concrete types
- patterns -- a catalogue of design patterns
- events -- the reactor pattern
Additional keywords and phrases:
generic types, assertions, canonical classes,
event-driven computation
slide: Idioms and patterns
public class envelope { envelope
public envelope() { }
public void message() {
System.out.println("hello ... ");
}
};
slide: Hello World
public class envelope { envelope
letter impl;
public envelope() {
impl = new letter();
}
public void message() {
impl.message();
}
};
public class letter { letter
public letter() { }
public void message() {
System.out.println("Message in a letter");
}
};
slide: Envelope/Letter
public class factory { factory
public factory() { }
letter letter() { return new letter(); }
envelope envelope() { return new envelope(); }
};
public class envelope { envelope
letter impl;
public envelope() {
factory f = new factory();
impl = f.letter(); // obtained from factory
}
public void message() {
impl.message();
}
};
slide: Factory
public class singleton extends letter { singleton
static int number = 0;
protected singleton() { }
static letter instance() {
if (number==0) {
theletter = new letter();
number = 1;
}
return theletter;
}
public void message() {
System.out.println("Message in a letter");
}
static letter theletter;
};
slide: Singleton letter
Overloading
print
extern void print(int);
extern void print(float);
Generic class -- templates
list< T >
template< class T > class list { ... }
list* alist;
Polymorphism by inheritance
shape
class shape { ... };
class circle : public shape { ... }
shape* s = new circle;
slide: Polymorphic type declarations
Standard Template Library
STL
- containers -- to hold objects
- algorithms -- act on containers
- iterators -- to traverse containers
- functions -- as objects
- adaptors -- to transform objects
- allocators -- for memory management
slide: The Standard Template Library
double sqrt( double arg ) { sqrt
require ( arg >= 0 );
double r=arg, x=1, eps=0.0001;
while( fabs(r - x) > eps ) {
r=x; x=r-((r*r-arg)/(2*r));
}
promise ( r - arg * arg <= eps );
return r;
}
slide: Using assertions in C++
class counter { counter
public:
counter(int n = 0) : _n(n) {
require( n >= 0 );
promise( invariant() ); // check initial state
}
virtual void operator++() {
require( true ); // empty pre-condition
hold(); // save the previous state
_n += 1;
promise( _n == old_n + 1 && invariant() );
}
int value() const { return _n; } // no side effects
virtual bool invariant() { return value() >= 0; }
protected:
int _n;
int old_n;
virtual void hold() { old_n = n; }
};
slide: The counter contract
class bounded : public counter { bounded
public:
bounded(int b = MAXINT) : counter(0), max(b) {}
void operator++() {
require( value() < max ); // to prevent overflow
counter::operator++();
}
bool invariant() {
return value() <= max && counter::invariant();
}
private:
int max;
};
slide: Refining the counter contract
Canonical class in C++
- default constructor
- copy constructor
- destructor
- assignment
- operators
Abstract data types must be indistinguishable
from built-in types
slide: Canonical class
hush classes">
Basic hush classes
- session -- to manage (parts of) the application
- kit -- to provide access to the underlying system and interpreter
- handler -- to bind C++ functionality to events
- event -- stores information concerning user actions or system events
- widget -- to display information on a screen
- item -- represents an element of a widget
slide: Basic
interface kit { kit
void eval(string cmd);
string result();
void bind(string name, handler h);
};
interface handler { handler
int dispatch( event e ); to dispatch events
int operator();
};
interface widget : handler { widget
...
void bind( handler h );
void bind( string action, handler h );
...
};
interface event : handler { event
operator();
};
class A { A -- naive
public A() { }
public void f1() { System.out.println("A.f1"); f2(); }
public void f2() { System.out.println("A.f2"); }
};
slide: Running example
class A { A
public A() { body = new BodyOfA(this); }
protected A(int x) { }
public void f1() { body.f1(); }
public void f2() { body.f2(); }
public void f3() { System.out.println("A.f3"); }
private A body;
};
slide: Interface: A
class BodyOfA extends A { BodyOfA -- naive
public BodyOfA() { super(911); }
public void f1() { System.out.println("A.f1"); f2(); }
public void f2() { System.out.println("A.f2"); }
};
slide: Naive: BodyOfA
class C extends A { C
public void f2() { System.out.println("C.f2"); }
};
slide: Usage: C
class BodyOfA extends A { BodyOfA
public BodyOfA(A h) { super(911); handle = h; }
public void f1() { System.out.println("A.f1"); handle.f2(); }
public void f2() { System.out.println("A.f2"); }
A handle; // reference to invocation context
};
slide: Handle/Body: BodyOfA
class item { item
public item(String x) { _name = x; _self = null; }
String name() { return exists()?self().name():_name; }
public void redirect(item x) { _self = x; }
boolean exists() { return _self != null; }
public item self() { return exists()?_self.self():this; }
item _self;
String _name;
};
slide: Item with self()
public class go {
public static void main(String[] args) {
item a = new item("a");
item b = new item("b");
a.redirect(b);
System.out.println(a.name()); indeed, b
}
};
slide: item: go
class actor {
public static final int Person = 0;
public static final int Student = 1;
public static final int Employer = 2;
public static final int Final = 3;
public void walk() { if (exists()) self().walk(); }
public void talk() { if (exists()) self().talk(); }
public void think() { if (exists()) self().think(); }
public void act() { if (exists()) self().act(); }
public boolean exists() { return false; }
public actor self() { return this; }
public void become(actor A) { }
public void become(int R) { }
};
slide: actor.java
class student extends actor { student
public void talk() { System.out.println("OOP"); }
public void think() { System.out.println("Z"); }
};
class employer extends actor { employer
public void talk() { System.out.println("money"); }
public void act() { System.out.println("business"); }
};
slide: Students and Employers
class person extends actor { person
public person() {
role = new actor[ Final+1 ];
for( int i = Person; i <= Final; i++ ) role[i]=this;
become(Person);
}
public boolean exists() { return role [role] != this; }
public actor self() {
if ( role[ Person ] != this ) return role[ Person ].self();
else return role [role];
}
public void become(actor p) { role[ Person ] = p; }
public void become(int R) {
if (role[ Person ] != this) self().become(R);
else {
_role = R;
if ( role [role] == this ) {
switch(_role) {
case Person: break; // nothing changes
case Student: role [role] = new student(); break;
case Employer: role [role] = new employer(); break;
case Final: role [role] = new actor(); break;
default: break; // nothing happens
}
}
} }
int _role;
actor role[];
};
slide: person.java
class adult extends person { adult
public void talk() { System.out.println("interesting"); }
};
slide: adult.java
public class go { example
public static void main(String[] args) {
person p = new person(); p.talk(); // empty
p.become(actor.Student); p.talk(); // OOP
p.become(actor.Employer); p.talk(); // money
p.become(new adult()); p.talk(); // interesting
p.become(actor.Student); p.talk(); // OOP
p.become(p); p.talk(); // old role: employer
p.become(actor.Person); p.talk(); // initial state
}
};
slide: go.java
Invocation context
handle/body
- Problem
- Inheritance breaks with handle/body
- Background
- Envelope/Letter, hiding implementations
- Realization
- Explicit invocation contact in body
- Usage
- sessions, events, kits, widgets, items
slide: Invocation context
Nested components
virtual self-reference
- Problem
- Realizing composites with single inheritance
- Background
- Decorators, prototypes
- Realization
- Smart delegation
- Usage
- Composite widgets, embedded logic
slide: Nested components
Actor pattern
dynamic role switching
- Problem
- Static type hierarchies may be too limited
- Background
- State transitions, self-reference
- Realization
- Dynamic instantiation and delegation
- Usage
- Web viewer, kit -- embedded logic
slide: Actor pattern
pattern schema
Name -- handle
- increases design vocabulary
Problem -- when to apply
- explains the problem and the conflict
Solution -- general arrangement
- design, responsibilities, collaborations
Consequences -- tradeoffs
- to understand the costs and benefits
slide: The pattern schema
design for change
- creating an object by specifying a class explicitly -- Abstract Factory, Factory Method, Prototype
- dependence on specific operations -- Chain of Responsibilty, Command
- dependence on hardware & software platforms -- Abstract Factory, Bridge
- dependence on object implementation or representation --Abstract Factory, Bridge, Memento, Proxy
- algorithm dependence -- Iterator, Strategy, Template Method, Visitor
- extending functionality by subclassing -- Bridge, Composite, Decorator, Observer
- tight coupling -- Abstract Factory, Bridge, Chain of Responsibilities, Command, Facade, Mediator, Observer
- inability to alter classes conveniently -- Adaptor, Decorator, Visitor
slide: Causes for redesign
Creational patterns
- Factory -- hide concrete classes
- Factory Method -- virtual constructors
- Prototype -- dynamic creation by cloning
- Singleton -- one instance only
See Pattern Lectures
slide: Creational patterns
Structural patterns
- object and class composition
Pattern | Alias | Remarks |
Composite | part/whole | collections of components |
Flyweight | handle/body | extrinsic state, many objects |
Adaptor | wrapper | resolve inconsistency between interfaces |
Bridge | handle/body | relate abstraction to implementation |
Decorator | handle/body | to introduce additional functionality |
Facade | handle/body | provides unified interface |
Proxy | handle/body | to defer ... remote, virtual, protection |
Behavioral patterns
cooperation
- algorithms and the assignment of responsibilities between objects
class
- Template Method -- the skeleton of an algorithm
- Interpreter -- to evaluate expressions
object
composition
- Mediator -- provides indirection
- Chain of Responsibility -- connect objects to interact
- Observer -- to handle dependencies
slide: Behavioral patterns
Encapsulating behavior
objectify!
- Command -- action + undo
- Strategy -- choice of algorithms
- Visitor -- decouple traversal and operations
- Iterator -- access and traversal
- State -- object state behavioral change
slide: Encapsulating behavior
Observer
-
one-to-many dependencies and notification
Consequences
- abstract coupling between subject and observer
- constraint propagation
- deals with unexpected updates
slide: Observer pattern
The Reactor pattern
- activate handlers when events occur
- allow events from multiple sources
- in single threaded process
See D.C. Schmidt, Using Design Patterns to Develop Reusable Object-oriented
Communication Software, CACM October '95, 38(10): 65-74
slide: The Reactor pattern
class thermometer { thermometer
protected thermometer( float v ) { temp = v; }
public void set(float v) { temp = v; }
public float get() { return temp; }
protected float temp;
};
class centigrade extends thermometer { centigrade
public centigrade() { super(0); }
public void set(float v) { temp = v + 273; }
public float get() { return temp - 273; }
};
class fahrenheit extends thermometer { fahrenheit
public fahrenheit() { super(0); }
public void set(float v) { temp = (v - 32) * 5/9 + 273; }
public float get() { return temp * 9/5 + 32 - 273; }
};
class displayer extends window { displayer
public displayer() { ... }
public void put(String s) { ... }
public void put(float f) { ... }
};
class prompter extends window { prompter
public prompter(String text) { ... }
public float get() { ... }
public String gets() { ... }
};
abstract class event { event
pubic void dependent(event e) { ... }
pubic void process() { ... }
public void operator(); // abstract method
private event[] dep;
};
class update extends event { update
public update(thermometer th, prompter p) {
_th =th; _p = p;
}
void operator()() {
_th.set( _p.get() );
process();
}
thermometer _th;
prompter _p;
};
class show extends event { show
public show(thermometer th, displayer d) {
_th = th; _d = d;
}
public void operator() {
_d.put( _th.get() );
process();
}
thermometer _th;
displayer _d;
};
thermometer c = new centigrade();
thermometer f = new fahrenheit();
displayer cd = new displayer("centigrade");
displayer fd = new displayer("fahrenheit");
prompter cp = new prompter("enter centigrade value");
prompter fp = new prompter("enter fahrenheit value");
show sc = new show(c,cd);
show sf = new show(f,fd);
update uc = new update(c,cp);
update uf = new update(f,fp);
uc.dependent(sc);
uc.dependent(sf);
uf.dependent(sc);
uf.dependent(sf);
menu.insert(uc);
menu.insert(uf);
- How would you explain the letter/envelope idiom?
- Characterize the notion of polymorphism. Give some examples.
- What is a canonical class? Characterize its ingredients and give an example.
- Give a brief description of
the handle/body idiom, virtual self-reference,
and dynamic role switching.
- What kinds of patterns can you distinguish?
Why do you consider patterns to be of relevance?
- Give a detailed description of the Factory pattern
and also of the Observer pattern.
- Describe the Reactor pattern.
Why is it useful?
- Give an example of a system based on event-driven
computation.
For an introduction to Java, there is ample choice.
An excellent online tutorial can be found on
java.sun.com/docs/books/tutorial .
As textbooks on C++ I recommend
[Lippman91], and for the more advanced reader [Stroustrup98].
For an extensive introduction to STL, read [STL].
[Coplien92] is the original introduction to idioms in C++.
The by now classical book for patterns is [GOF94].
Well worth reading are the many articles in the POPL proceedings,
[POPL1], [POPL2] and [POPL3].