SLIDE 1 Case Study: The Number Hierarchy
Goal: Develop feel for programming in the large Issues we’ll consider along the way:
– What methods go where?
– Class methods establish invariants – Instance methods must maintain them
– “semi-private” methods for like objects – Double-dispatch when argument form matters.
SLIDE 2
The Number Hierarchy
Object Magnitude Number Fraction Float Integer
SLIDE 3 Instance protocol for Magnitude
= aMagnitude equality (like Magnitudes) < aMagnitude comparison (ditto) > aMagnitude comparison (ditto) <= aMagnitude comparison (ditto) >= aMagnitude comparison (ditto) min: aMagnitude minimum (ditto) max: aMagnitude maximum (ditto) Subclasses: Date, Natural
- Compare Date with Date, Natural w/Natural, . . .
SLIDE 4
Implementation of Magnitude: Reuse
(class Magnitude ; abstract class [subclass-of Object] (method = (x) (self subclassResponsibility)) ; may not inherit = from Object (method < (x) (self subclassResponsibility)) (method > (y) (y < self)) (method <= (x) ((self > x) not)) (method >= (x) ((self < x) not)) (method min: (aMag) ((self < aMag) ifTrue:ifFalse: {self} {aMag})) (method max: (aMag) ((self > aMag) ifTrue:ifFalse: {self} {aMag})) )
SLIDE 5
The Number Hierarchy, Reprise
Object Magnitude Number Fraction Float Integer
SLIDE 6 Instance protocol for Number
negated reciprocal abs absolute value + aNumber addition
subtraction * aNumber multiplication / aNumber division (may answer a Fraction!) isNegative sign check isNonnegative sign check isStrictlyPositive sign check coerce: aNumber class of receiver, value of argument asInteger conversion asFraction conversion asFloat conversion
SLIDE 7 Object-oriented design
Given that class Number inherits from class Magnitude, which of these methods of class Number can be implemented in terms of others? negated * coerce: reciprocal / asInteger abs isNegative asFraction + isNonnegative asFloat
SLIDE 8
Concrete Number Methods
(method - (y) (self + (y negated))) (method abs () ((self isNegative) ifTrue:ifFalse: {(self negated)} {self})) (method / (y) (self * (y reciprocal))) (method isNegative () (self < (self coerce: 0))) (method isNonnegative () (self >= (self coerce: 0))) (method isStrictlyPositive () (self > (self coerce: 0)))
SLIDE 9
Abstract Number Methods
(class Number [subclass-of Magnitude] ; abstract class ;;;;;;; arithmetic (method + (aNumber) (self subclassResponsibility)) (method * (aNumber) (self subclassResponsibility)) (method negated () (self subclassResponsibility)) (method reciprocal () (self subclassResponsibility)) ;;;;;;; coercion (method asInteger () (self subclassResponsibility)) (method asFraction () (self subclassResponsibility)) (method asFloat () (self subclassResponsibility)) (method coerce: (aNumber) (self subclassResponsibility)) )
SLIDE 10
Extended Number Hierarchy
Object Magnitude Natural Number Fraction Float Integer SmallInteger LargeInteger LargePositiveInteger LargeNegativeInteger
SLIDE 11
Example class Fraction: Initialization
(class Fraction [subclass-of Number] [ivars num den] ;; invariants: lowest terms, minus sign on top ;; maintained by divReduce, signReduce (class-method num:den: (a b) ((self new) initNum:den: a b)) (method initNum:den: (a b) ; private (self setNum:den: a b) (self signReduce) (self divReduce)) (method setNum:den: (a b) (set num a) (set den b) self) ; private .. other methods of class Fraction ... )
SLIDE 12 Information revealed to self
Instance variables num and den
- Directly available
- Always and only go with self
Object knows its own representation, invariants, private methods:
(method asFraction () self) (method print () (num print) (’/ print) (den print) self) (method reciprocal () (((Fraction new) setNum:den: den num) signReduce))
SLIDE 13
Information revealed to others
How would you implement coerce:? (Value of argument, representation of receiver)
(method asFraction () self) (method print () (num print) (’/ print) (den print) self) (method reciprocal () (((Fraction new) setNum:den: den num) signReduce)) (method coerce: (aNumber) ...)
Saved: Number protocol includes asFraction!
SLIDE 14 Information revealed to others
How would you implement coerce:?
- Value of argument, rep of receiver
- Challenge: Can’t access rep of argument!
(method asFraction () self) (method print () (num print) (’/ print) (den print) self) (method reciprocal () (((Fraction new) setNum:den: den num) signReduce)) (method coerce: (aNumber) (aNumber asFraction))
Saved: Number protocol includes asFraction!
SLIDE 15
The Number Hierarchy, Reprise
How to implement comparisons on Fractions? Object Magnitude Number Fraction Float Integer
SLIDE 16 Instance protocol for Magnitude, Reprise
How to implement comparisons on Fractions?
= aMagnitude equality (like Magnitudes) < aMagnitude comparison (ditto) > aMagnitude comparison (ditto) <= aMagnitude comparison (ditto) >= aMagnitude comparison (ditto) min: aMagnitude minimum (ditto) max: aMagnitude maximum (ditto) Subclasses: Date, Natural
- Compare Date with Date, Natural w/Natural, . . .
SLIDE 17
Implementing comparison for Fractions
Alas! Cannot see representation of argument How will you know “equal, less or greater”?
SLIDE 18
Implementing comparison for Fractions
Alas! Cannot see representation of argument Protocol says “like with like”? Extend the protocol
(method num () num) ; extension, semi-private (method den () den) ; extension, semi-private (method = (fr) ((num = (fr num)) and: {(den = (fr den))})) (method < (fr) ((num * (fr den)) < ((fr num) * den)))
SLIDE 19
Extended protocol: Multiply two fractions
How will you multiply two fractions?
SLIDE 20
Extending the protocol to multiply fractions
How will you multiply two fractions?
(method * (aFraction) (((Fraction new) setNum:den: (num * (aFraction num)) (den * (aFraction den))) divReduce))
SLIDE 21 Information access in an open system
Number protocol: like multiplies with like What about large and small integers?
- How to multiply two small integers?
- How to multiply two large integers?
How is algorithm known? Each object knows its own algorithm:
- Small: Use machine-primitive multiplication
- Large: Multiply magnitudes; choose sign
SLIDE 22 Review: Two kinds of knowledge
I can send message to you:
I can inherit from you:
- I know my subclass responsibilities
SLIDE 23 Knowledge of protocol
Three levels of knowledge:
- 1. I know only your public methods
Example: send select: to any collection
- 2. You are like me: share semi-private methods
Example: send * or + to Fraction
- 3. I must get to know you: double dispatch
Example: send * to + to any integer
SLIDE 24 Double dispatch: Extending open systems
Design constraints:
- Large integers and small integers are both Integers
- Messages =, <, +, * ought to mix freely
- Large integers have a unique private protocol
They implement different algorithms than small integers.
SLIDE 25 Double dispatch: Forms of argument
Laws of multiplication:
(:+ n) * (:- m) == :- (n * m) (:+ n) * (:+ m) == :+ (n * m) (:+ n) * small == (:+ n) * (small asLargeInteger)
But! Can’t distinguish forms of argument Solution: “Dispatch laws”
(:+ n) * (:- m) == ((:- m) timesLP: self) (:+ n) * (:+ m) == ((:+ m) timesLP: self) (:+ n) * small == (small timesLP: self)
Argument to timesLP:
- Understands “large positive integer” protocol
SLIDE 26 Double dispatch codes operation & protocol
Example messages:
- timesLP: I answer the large-positive integer
protocol, multiply me by yourself
- plusSP: I answer the small-integer protocol, add
me to yourself Message encodes
- Operation to be performed
- Protocol accepted by argument
SLIDE 27 Double dispatch to implement addition
How do you act?
- 1. As small integer, you receive “add large positive
integer N by self”
- 2. As small integer, you receive “add small
integer n to self”
- 3. As large positive integer, you receive “add large
positive integer N by self”
- 4. As large positive integer, you receive “add small
integer n to self”
SLIDE 28 Your turn: Double dispatch to implement multiplication
How do you act?
- 1. As small integer, you receive “multiply large
positive integer N by self”
- 2. As small integer, you receive “multiply small
integer n to self”
- 3. As large positive integer, you receive “multiply
large positive integer N by self”
- 4. As large positive integer, you receive “multiply
small integer n to self”
SLIDE 29 Your turn: Using double dispatch
On what class does each method go?
(aNumber addSmallIntegerTo: self))
(anInteger multiplyByLargePositiveInteger: self))
(See the “double dispatch”: + then addSmallIntegerTo:)
SLIDE 30 Information-hiding summary
Three levels
- 1. I use your public protocol
- 2. We are alike; I add our semi-private protocol
- 3. Your protocol is revealed by double dispatch
SLIDE 31 Extra: Dealing with overflow
New law for multiplication: (self * small) = ((primitive mulWithOverflow self small {((self asLargeInteger) * small)}) value)) Primitive is not a method
- Answers good block or exception block
- Answer is then sent value