230 likes | 378 Views
6894 · workshop in software design lecture 5 · october 14, 1998 · design patterns. topics. 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
E N D
6894 · workshop in software designlecture 5 · october 14, 1998 · design patterns
topics • 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
origins • 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 Gamma’s 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)
how 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
what’s 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
how 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
GOF 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 • Window’s 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: can’t 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
GOF 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
opaque association • motivation • behaviour of two objects A and B is tightly coupled • want coupling only at runtime, not compile time • change to B’s code should not affect A’s • basic idea • loosen compile time coupling by • having A access B through an opaque association • A’s code no longer depends on B’s 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
class models • elements • a box represents a class • a bar represents a specification • an arrow from box A to box B means • A’s code calls a method on a B object • an arrow from A to B through spec S • A’s 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
why 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 • shouldn’t 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 • OCL’s typecasts: much more complex than Alloy’s 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
disentangling 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
CLIENT ADAPTER ADAPTEE TARGET adapter CLIENT PROXY SERVICE SERVICE proxy IMPL-A CLIENT REFINED IMPL-B ABSTRACTION bridge examples of opaque association (1)
COLL-1 CMEDIATOR COLL-2 MEDIATOR mediator CSUBJECT COBSERVER observer OBSERVER STATE-A CLIENT CONTEXT STATE-B state STATE examples of opaque association (2)
examining 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
visitor 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
sample 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!
visitor as closure • basic idea • visitor provides convenient context for state to be maintained over traversal • examples • encapsulate output stream • PrettyPrintV (Stream s) {stream = s;}void for_Literal (Literal x) {stream.println (x.toString());} • encapsulate Variable -> Literal binding • EvaluateV (Binding b) {…}Object for_Variable (Variable x) {return b.get (x);} • other examples • instrumentation/debugging
functional visitor • basic idea • a kind of ‘map’ • transforms one AST to another with nodes of different types • operation types • C’ Visitor::for_C (C) • C’ C::accept (Visitor) • inflexibility of Java’s 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); }
imperative visitor • basic idea • mutates each element of AST • operation types • void Visitor::for_C (C) • void C::accept (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; }
replacing visitor • replacing visitor • type structure of functional visitor, but mutates like imperative visitor • operation types • C’ Visitor::for_C (C) • C’ C::accept (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; } }
exploiting 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!
but … subclassing snags • can’t 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 • it’s 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!