510 likes | 618 Views
Modular OOP with Units and Mixins. Paper by Finder/Flatt Presentation by Dan Licata. Scheme in Fixnum Minutes. > 4 4 >(define x 4) > x 4. Scheme in Fixnum Minutes. >(+ 4 4) 8 > (+ (+ 4 (+ 4 4)) (+ 4 4)) 20. Scheme in Fixnum Minutes. > 5 5. Scheme in Fixnum Minutes.
E N D
Modular OOP with Units and Mixins Paper by Finder/Flatt Presentation by Dan Licata
Scheme in Fixnum Minutes > 4 4 >(define x 4) > x 4
Scheme in Fixnum Minutes >(+ 4 4) 8 > (+ (+ 4 (+ 4 4)) (+ 4 4)) 20
Scheme in Fixnum Minutes > 5 5
Scheme in Fixnum Minutes > (define add2 (lambda (x y) (+ x y))) > (add2 4 5) 9 > add2 <#procedure...>
Scheme in Fixnum Minutes (define add2(lambda (x y) (+ x y)) > (add2 4 5) 9 (define mk-add(lambda (x) (lambda(y) (+ x y))))
Scheme in Fixnum Minutes > (mk-add 4) #<procedure> > (define add4 (mk-add 4)) > (add4 5) 1 (define mk-add(lambda (x) (lambda(y) (+ x y))))
Reading a Paper • What problem are they solving? • How do they solve it? • Why should I care?
Reading a Paper • What problem are they solving? • How do they solve it? • Why should I care?
Extensibility! Same story as last time: • Functional programming => easy to add a new operation • OOP => easy to add a new datatype variant What would the ideal be?
Extensibility Shape Clients
Add a New Variant Shape Existing clients should not need to be modified! Clients
Add a New Variant Shape Clients New Clients
Add a New Operation Shape Clients New Clients
Presumptions in the Picture • Add a new operation by subclassing • If the change gets used behind the original interface, unmodified clients should see it In traditional OOP, do they?
Define Original Datatype (define Shape (interface () draw) (define Rectangle (class* null (Shape) (width height) (public [draw (lambda (window x y) …))]))
Define Original Datatype (define Circle (class* null (Shape) (radius) (public [draw (lambda (window x y) ...))]))
Define Original Datatype (define display-shape (lambda (shape) (let ([window ...]) (send shape draw window 0 0))))
Create A Client (display-shape (make-object Rectangle 50 100))
Add a New Variant (define Union (class* null (Shape) (left right) (public [draw (lambda (window x y) ...))]))
Add a Client that Uses It (display-shape (make-object Union (make-object Rectangle 10 30) (make-object Circle 20)))
Add a New Operation (define BB-Shape (interface (shape) bounding-box) (define BB-Rectangle (class* Rectangle (BB-Shape) (w h) (public [bounding-box (lambda () (make-object BB 0 0 w h))]) ...))
Add a New Operation (define display-shape (lambda (shape) (let ([bb (send shape bounding-box)] (let ([window ...] [x ...] [y ...]) (send shape draw window x y)))))
Do the Old Clients Use It? (display-shape (make-object Rectangle 50 100)) (display-shape (make-object Union (make-object Rectangle 10 30) (make-object Circle 20)))
Do the Old Clients Use It? (display-shape (make-object Rectangle 50 100)) (display-shape (make-object Union (make-object Rectangle 10 30) (make-object Circle 20)))
Potential Solution • Always program using Abstract Factory • Downsides: • Requires forethought • Contorts the code How can we add language support?
Reading a Paper • What problem are they solving? • How do they solve it? • Why should I care?
Fortune-Cookie Wisdom Every problem in CS can be solved by adding a layer of indirection. • Layers: • Mixins • Units
Mixin Class that is parameterized by its superclass (define BB-Rect (class* Rectangle ...)) (define BB-Rect (lambda (Rect) (class* Rect ...)))
Unit Module that is • Separately compilable • Black-box reusable (has an interface) • Multiply instantiable • Dynamically linkable
Define Original Datatype (define Basic-Shapes (unit (import) (export Shape Rectangle Circle) (define Shape (interface ...)) (define Rectangle (class* ...)) (define Circle (class* ...))))
Define Original Datatype (define GUI (unit (import Shape) (export display-shape) (define display-shape ...)))
Create a Client (define Picture (unit (import Shape Rectangle Circle display-shape) (export) (display-shape (make-object Rectangle 50 100))))
Link Them All Together (define Basic-Program (compound-unit (import) (link [S (Basic-Shapes)] [G (GUI (S Shape))] [P (Picture (S Rectangle) (S Circle) (G display-shape))]) (export)))
And Run It (invoke-unit Basic-Program)
Add a New Variant (define Union-Shape (unit (import Shape) (export Union) (define Union (class* ...))))
Package It Up with the Others (define Basic+Union-Shapes (compound-unit (import) (link [S (Basic-Shapes)] [US (Union-Shape (S Shape))]) (export (S Shape) (S Rectangle) (S Circle) (US Union))))
Add a Client that Uses It (define Union-Picture (unit (import Rectangle Circle Union display-shape) (export) (display-shape (make-object Union (make-object Rectangle 10 30) (make-object Circle 20))))
Link Them All Together (define Union-Program (compound-unit (import) (link [S (Basic+Union-Shapes)] [G (GUI (S Shape))] [P (Picture (S Rectangle) (S Circle) (G display-shape))] [UP (Union-Picture (S Rectangle) (S Circle) (S Union) (G display-shape))]) (export))) Picture is unchanged!
Add a New Operation (define BB-Shapes (unit (import Shape Rectangle Circle Union) (export BB-Shape BB-Rectangle BB-Circle BB-Union) (define BB-Shape (interface ...)) (define BB-Rectangle (class* Rectangle ...)) (define BB-Circle (class* Circle ...) (define BB-Union (class* Union ...) (define BB ...))) Implicit mixin!
Package It Up with the Others (define Basic+Union+BB-Shapes (compound-unit (import) (link [S (Basic+Union-Shapes)] [BS (BB-Shapes (S Shape) (S Rectangle) (S Circle) (S Union))]) (export (S Shape) (BS BB-Shape) (BS BB) (BS (BB-Rectangle Rectangle)) ...))) Rename to preserve substitutability!
Use the New Functionality (define BB-GUI (unit (import BB-Shape BB) (export display-shape) (define display-shape ...)))
Link Them All Together Picture and UnionPictureare unchanged! (define BB-Program (compound-unit (import) (link [S (Basic+Union+BB-Shapes)] [BG (BB-GUI (S BB-Shape) (S BB))] [P (Picture (S Rectangle) (S Circle) (BG display-shape))] [UP (Union-Picture (S Rectangle) (S Circle) (S Union) (BG display-shape))]) (export)))
Reading a Paper • What problem are they solving? • How do they solve it? • Why should I care?
Solves Extensibility Problem • You can add both new variants and new operations without modifying existing clients • Unmodified clients may use the new operation • Or, they may not
Synergy Between Modules and Classes • Modules are good for: • Separate development • Linking • Classes are good for: • Extensible datatypes • Selective reuse • Language should have both!
Separate Definitions and Linking • Module: externally link dependencies • Classs: externally specify superclass In both, allow substitution of subtypes!
In A Statically-Typed Setting • Would this work for Java? • Would this work for ML-with-objects?
Bigger Picture • Do you buy it? • How could it be improved?