510 likes | 626 Vues
This presentation explores the integration of modular design principles within both object-oriented programming (OOP) and functional programming paradigms, focusing on the use of units and mixins. It discusses the challenges of extensibility, emphasizing how new operations can be added without modifying existing clients. By examining the definition of shapes and displaying them, the presentation illustrates how programming with an abstract factory can facilitate the addition of new datatypes and operations dynamically, addressing the core problems in software design and enhancing code reuse.
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?