Title: 6894
16894 workshop in software designlecture 5
october 14, 1998 design patterns
2topics
- schedule
- October 19
- reading on concurrent design patterns
- catalog of about 10 design patterns
- http//gee.cs.oswego.edu/dl/cpj/index.html
- October 21
- instead of Problem Frames, discuss design
proposals - each team bring a 5-10 minute presentation
- design proposals
- scope
- can be on some aspect of the CM
- on an entire redesign of the CM
- form
- 1. problem addressed
- 2. solution proposed
- 3. strategy, esp. minimal subset
3origins
- motivation
- capture expertise of experienced designers
- reuse of design practice and form
- provide vocabulary for design
- history
- Christopher Alexander et al A Pattern Language
(1977) - Erich Gammas PhD thesis (1991) about half of
the GOF patterns - Coplien Advanced C Styles and Idioms (1992)
- Helm, Vlissides, Johnson summary of pattern
catalog in ECOOP (1993) - Gamma et al Design Patterns (1995)
- often referred to as the Gang of Four Book (GOF)
4how patterns help
- flexibility
- make code less susceptible to changes
- confine changes as much as possible
- examples confine changes in
- representation (Factory, Bridge, Memento, Proxy)
- algorithm (Builder, Iterator, Strategy, Template,
Visitor) - platform (Factory, Bridge)
- adding new features (Observer, State, Mediator)
- documentation
- patterns are familiar to programmers
- name alone says a lot
- archaeology
- patterns record best practices
- target for research ideas, language evaluation,
etc
5whats in a pattern?
- GOF style presentation
- name
- very important!
- problem
- difficulty addressed by pattern
- motivating example
- solution
- general form of pattern
- implementation advice
- variants
- consequences
- not just benefits liabilities too
- all patterns add complexity
- most reduce efficiency
6how patterns work
- standard use of language constructs
- algebraic datatypes (Composite)
- closures (Factory)
- class variables (Singleton)
- inheritance (Template) but maybe not standard
use? - ad hoc tricks
- hand-coded dispatch (Visitor)
- dynamic reclassification (State)
- cloning cursor (Iterator)
- opaque association
- (my term)
- Adapter, Bridge, Mediator, Observer, Proxy
7GOF bias delegation over inheritance
- example
- Window and Rectangle
- with inheritance
- Window subclass of Rectangle
- getArea method of Window is inherited
- resize method of Window calls methods and uses
instance vars of Rectangle - with delegation
- Windows rep includes a Rectangle
- getArea method of Window just invokes getArea of
Rectangle - advantages of delegation
- runtime composition Window can become circular
by switching Rep - better modularity avoid abstraction violations
of inheritance - disadvantages of delegation
- a bit clumsier than inheritance
- objects have distinct types cant pass Window to
method expecting Rectangle - but Window is probably not a subtype of Rectangle
anyway - less of an issue in dynamically-typed languages,
eg. Scheme
8GOF bias others
- no parametric polymorphism
- most useful at lower level of abstraction?
- notion of association (from OMT) hides
representation of set or table - objects, not functions
- closures rarely used
- with exception of Visitor, State, ?
- patterns are very imperative in flavour
9opaque association
- motivation
- behaviour of two objects A and B is tightly
coupled - want coupling only at runtime, not compile time
- change to Bs code should not affect As
- basic idea
- loosen compile time coupling by
- having A access B through an opaque association
- As code no longer depends on Bs interface
- two ways to achieve
- indirection
- A accesses B through another object, X
- specification
- A accesses B as if it has a specification S that
is weaker than its actual specification - consequences of this style of design
- more complex compile-time structure
- greater disparity between compile-time and
runtime structures - more elaborate runtime invariants
10class models
- elements
- a box represents a class
- a bar represents a specification
- an arrow from box A to box B means
- As code calls a method on a B object
- an arrow from A to B through spec S
- As code calls a method of a B object, but views
B as having spec S - if arrow is dotted, association is transient B
object not in rep of A
A
B
A
B
S
11why yet another notation?
- object model plays two roles
- abstract state
- what objects exist, what invariants hold, etc
- class structure
- what code modules exist, dependences, namespace,
etc - shouldnt be conflated
- specs are not just sets of objects!
- spec describes what properties are expected in
the future - class structure should be postponed
- abstract state description is a specification
activity - allocation of methods and state to classes is a
design activity - design patterns suggest major deviation from
problem domain structure - snags caused by shared notation
- OCLs typecasts much more complex than Alloys
simple set ops - no superclass/interface distinction
- only one occurrence of an interface node with a
given name - so Enumeration, eg, can only occur once!
- but different Enumerations often unrelated
12disentangling design patterns observer
- essential feature
- ConcreteSubject views ConcreteObserver through
Observer - code sharing in Subject is a minor detail
Subject Attach (Observer)Detach
(Observer)Notify ()
Observer Update ()
observers
for all o in observers o.Update ()
observerState subject.GetState ()
Concrete Subject GetState ()SetState
() subjectState
Concrete Observer Update () observerState
!
subject
return subjectState
13examples of opaque association (1)
14examples of opaque association (2)
15examining a DP visitor
- visitor is unusual
- designed to overcome OO-ness of OO language
- a great idea or a clever hack?
- discussed in detail in
- Felleisen Friedman, A Little Java, A Few
Patterns
ELEMENT-A
VISITOR-X
ELEMENT-B
16visitor example
- AST for arithmetic expressions
- suppose language is
- expr literal variable expr expr expr
expr - expr - might implement as AST with
- interface Expr
- class Literal implements Expr
- class Variable implements Expr
- abstract class BinaryExpr implements Expr
- class MinusExpr implements Expr
- operations on AST
- evaluate for given binding of variables to
literals - pretty print
- reduce (eg, replace e 0 by e)
- observation
- many involve similar traversals
- awkward to implement each new operation by adding
method to each class - would rather add a new class for each operation
17sample Visitor code
- interface Visitor void for_Literal (Literal
x) void for_Variable (Variable x) void
for_BinaryExpr (BinaryExpr x) void
for_MinusExpr (MinusExpr x) - class Literal implements Expr void
accept (Visitor v) v.for_Literal (this)
- class PrettyPrintV implements Visitor
PrettyPrintV () void for_Literal (Literal
x) System.out.println (x.toString())
void for_BinaryExpr (BinaryExpr x)
x.left.accept (this) System.out.println
(x.op.toString()) x.right.accept (this)
- note
- Visitor accesses representation of visited class!
18visitor as closure
- basic idea
- visitor provides convenient context for state to
be maintained over traversal - examples
- encapsulate output stream
- PrettyPrintV (Stream s) stream svoid
for_Literal (Literal x) stream.println
(x.toString()) - encapsulate Variable -gt Literal binding
- EvaluateV (Binding b) Object for_Variable
(Variable x) return b.get (x) - other examples
- instrumentation/debugging
19functional visitor
- basic idea
- a kind of map
- transforms one AST to another with nodes of
different types - operation types
- C Visitorfor_C (C)
- C Caccept (Visitor)
- inflexibility of Javas subtyping rules a pain
- lots of unnecessary downcasts C must be Object
- sample code
- class C
- D d E e Object accept _functional
(FunctionalVisitor v) return v.for_C
(this) - class VisitorX implements FunctionalVisitor
Object for_C (C c) DD dd (DD)
c.d.accept_functional (this) EE ee (EE)
c.e.accept _functional (this) return
someFunction (dd, ee)
20imperative visitor
- basic idea
- mutates each element of AST
- operation types
- void Visitorfor_C (C)
- void Caccept (Visitor)
- sample code
- class C
- D d E e void accept_imperative
(ImperativeVisitor v) v.for_C (this)
- class VisitorX implements ImperativeVisitor
void for_C (C c) c.d.accept_imperative
(this) c.e.accept_imperative (this)
return
21replacing visitor
- replacing visitor
- type structure of functional visitor, but mutates
like imperative visitor - operation types
- C Visitorfor_C (C)
- C Caccept (Visitor)
- sample code
- class C D d E e Object accept _replacer
(ReplacingVisitor v) return v.for_C
(this) - class VisitorX implements ReplacingVisitor
Object for_C (C c) if () return
new C () else c.d (D)
c.d.accept_replacer (this) c.e (E)
c.e.accept_replacer (this) return c
22exploiting subclassing
- basic idea
- make Visitor a superclass, not an interface
- dummy implementation for each element type
- imperative visitor applies the visitor to the
subelements - replacing visitor replaces the subelements using
the visitor - each visitor implementation only overrides
behaviour for some elements - examples
- imperative to print out all literals
- class PrintLiteralV extends ImperativeVisitor
void for_Literal (Literal x) stream.println
(x.toString()) - replacing replace all variables by dummy
literals - class InstantiateR extends ReplacingVisitor
Object for_Variable (Variable x) return new
Literal () - then all you need is the first call
- expr.accept (new FooVisitor ())
- this is a major advantage of Visitors!
23but subclassing snags
- cant subclass in the element hierarchy
- suppose we have
- abstract class BinaryExprclass PlusExpr extends
BinaryExprclass TimesExpr extends BinaryExpr - now suppose we want a visitor that prints out
binary exprs only - will this work?
- class PrintBinExprV implements Visitor
for_BinaryExpr (BinaryExpr x)
stream.println (x.toString ()) - no!
- for_BinaryExpr is never called
- its a method associated only with the abstract
class, which has no objects - a flawed solution
- default implementation of for_PlusExpr calls
for_BinaryExpr - can now override for_BinaryExpr and get intended
behaviour for this PrintBinExprV - but cannot write PrintLiteralV by overriding
for_Literal alone - for_PlusExpr calls for_BinaryExpr and not
left.accept!