advanced object oriented systems
DESCRIPTION
Advanced Object Oriented Systems. (CM0318) Lecture 8 (Last updated 15th February 2002). Purpose of Lectures 8 & 9. To illustrate why Smalltalk is different, in particular because: implementation of system classes is readily available - PowerPoint PPT PresentationTRANSCRIPT
1
Advanced Object Oriented Systems
(CM0318)
Lecture 8
(Last updated 15th February 2002)
2
Purpose of Lectures 8 & 9
• To illustrate why Smalltalk is different, in particular because:– implementation of system classes is readily
available– debugging tends to involve understanding
system classes as well as one’s own– Smalltalk is a highly dynamic system
3
Topics
• Implementation of collections
• Introspection and dynamic modification of classes/objects
• The Model-View-Controller paradigm
• Debugging strategies
• (Implementation & introspection are the topics of the present lecture)
4
Partial Collection hierarchy
ProtoObjectObject at: at:put:
Collection includes: isEmptyadd: remove:do: size
BagSequenceableCollection , first at:ifAbsent:
indexOf:ArrayedCollection
Array (“Variable subclass”)String < = beginsWith:
SymbolOrderedCollection
SetDictionary at: at:put: includesKey:
5
Recommended Smalltalk reading
• Byte, August 1981 (dedicated to Smalltalk)
• Goldberg, A., and Robson, D., ‘Smalltalk-80 : the language’, Addison-Wesley, 1989.
• Goldberg, A., ‘Smalltalk-80 : the interactive programming environment’, Addison-Wesley, 1984.
6
Implementation of Collections
• NB it’s essential to annotate these handouts - otherwise the code won’t make much sense!!
• You can verify all of what I am telling you in this lecture simply by browsing in Squeak - indeed, the main message is the ‘openness’ of the Smalltalk code. We’ll see how this affects debugging, etc., later.
7
Implementation of Array
• Creation method: class method new:, which is actually inherited from Behavior(Remember Array is itself an object; instance of its metaclass, which is a subclass - with a few intermediaries in between - of Behavior).
8
Array (ctd.)
• Main accessing methods: at: (implemented in Object), at:put: (implemented in Object), size (implemented in ArrayedCollection)
• Example of code to replace every element in an array by that element + 1:
a ← #(3 4 5).
1 to: a size do:
[:i|a at: i put: (a at: i) + 1].
^a
9Array (ctd.)• Source code for Object>>at:put:at: index put: value
"Primitive. Assumes receiver is indexable. Store the argument value in
the indexable element of the receiver indicated by index. Fail if the
index is not an Integer or is out of bounds. Or fail if the value is not of the right type for this kind of collection. Answer the value
that was stored. Essential. See Object documentation whatIsAPrimitive."
<primitive: 61>
index isInteger ifTrue:
[self class isVariable
ifTrue: [(index >= 1 and: [index <= self size])
ifTrue: [self errorImproperStore]
ifFalse: [self errorSubscriptBounds: index]]
ifFalse: [self error: (self class name) , 's are not indexable']].
index isNumber
ifTrue: [^self at: index asInteger put: value]
ifFalse: [self errorNonIntegerIndex]
10
Array (ctd.)
• Methods like at:, at:put: are defined in Object because there are 2 fundamental types of class:
• Fixed (named instance variables only)– Defined <class> subclass: #<classname> ...
• Variable (named instance variables and subscripted instance variables)– Defined <class> variableSubclass: #<classname> ...
11
FIXED
Inst
var
1In
st v
ar 2
Inst
var
3In
st v
ar 4
Inst
var
5
Inst
var
n-1
Inst
var
n
VARIABLE
Inst
var
1In
st v
ar 2
Inst
var
3In
st v
ar 4
Inst
var
5
Inst
var
n-1
Inst
var
n1 2 3 m
12Array (ctd.)• Testing for equality (inherited from SequenceableCollection)= otherCollection "Answer true if the receiver is equivalent to the <otherCollection>. First test for identity, then rule out different species and sizes of
collections. As a last resort, examine each element of the receiver and the
<otherCollection>."
| size | self == otherCollection ifTrue: [^ true]. (self species == otherCollection species) ifFalse: [^ false]. (size ← self size) = otherCollection size ifFalse: [^ false].
1 to: size do:[:index |(self at: index) = (otherCollection at: index) ifFalse: [^ false]].
^ true
13
Array (ctd.)
• Testing for inclusion (inherited from Collection):includes: anObject
"Answer whether anObject is one of the receiver's elements."
self do: [:each | anObject = each ifTrue: [^true]].
^false
14
Array (ctd.)
• Iteration - do: (inherited from SequenceableCollection)
do: aBlock
"Refer to the comment in Collection|do:."
1 to: self size do:
[:index | aBlock value: (self at: index)]
15
OrderedCollection• Similar to Vector in Java• Comprises an Array, a firstIndex and a lastIndex (only
part of the array is used). So an ordered collection with elements 34, 46, 25 might be represented thus:
Element no Contents1 ?2 ?3 34 <- firstIndex=34 465 25 <- lastIndex=56 ?7 ?
16
Compared with Vector ...
• Allows the collection to grow at the beginning and the end.
17
OrderedCollection example
oc ← #(3 4 5) asOrderedCollection.
oc addFirst: 'ho'.
oc addLast: 'hoho'.
^oc at: 2
• Result is 3.
18
OrderedCollection (ctd.)
• Instance creation (class methods):new
^self new: 10
new: anInteger
"If a subclass adds fields, then it is necessary for that subclass to reimplement new:."
^ super new setCollection: (Array new: anInteger)
19
OrderedCollection (ctd.)
• Instance creation (instance methods):setCollection: anArray
array ← anArray.
self reset
reset
firstIndex ← array size // 3 max: 1.
lastIndex ← firstIndex - 1
20
OrderedCollection (ctd.)
• Accessing using at:at: anInteger
"Answer my element at index anInteger. at: is used by a knowledgeable
client to access an existing element"
(anInteger < 1 or: [anInteger + firstIndex - 1 > lastIndex])
ifTrue: [self errorNoSuchElement]
ifFalse: [^ array at: anInteger + firstIndex - 1]
21
OrderedCollection (ctd.)
• Adding a new element:addLast: newObject
"Add newObject to the end of the receiver. Answer newObject."
lastIndex = array size ifTrue: [self makeRoomAtLast].
lastIndex ← lastIndex + 1.
array at: lastIndex put: newObject.
^ newObject
22OrderedCollection (ctd.)• Growing ...makeRoomAtLast| newLast delta |newLast ← self size.array size - self size = 0 ifTrue: [self grow].(delta ← firstIndex - 1) = 0 ifTrue: [^ self]."we might be here under false premises or grow did the job for us"1 to: newLast do:
[:index |array at: index put: (array at: index + delta).array at: index + delta put: nil].
firstIndex ← 1.lastIndex ← newLast
23
OrderedCollection (ctd.)
• More growing:grow
"Become larger. Typically, a subclass has to override this if the subclass
adds instance variables."
| newArray |
newArray ← Array new: self size + self growSize.
newArray replaceFrom: 1 to: array size with: array startingAt: 1.
array ← newArray
24
Exercise (unassessed)
• Investigate:– Other methods implemented by/inherited by
Array and OrderedCollection– The implementation of the Set class
25
Introspection; dynamic modification to classes/objects
• Java provides limited introspection via the reflection API. Main things you can do:– determine an object’s class– get information about a class’ fields, methods, etc.– create an instance of a class whose name is only determined
at run-time– get and set the value of an object’s field– invoke a method on an object
• these things can be achieved even if the method name, for example, is only determined at run-time
26
Suggested reading
• Tutorial on Java reflection:http://web2.java.sun.com/docs/books/tutorial/reflect/index.html
27
Example: invoking a method
import java.lang.reflect.*;
class SampleInvoke {
public static void main(String[] args) { String firstWord = "Hello "; String secondWord = "everybody."; String bothWords = append(firstWord,
secondWord); System.out.println(bothWords); }
28public static String append(String firstWord, String secondWord) { String result = null; Class c = String.class; Class[] parameterTypes = new Class[] {String.class}; Method concatMethod; Object[] arguments = new Object[] {secondWord}; try { concatMethod = c.getMethod("concat", parameterTypes); result = (String) concatMethod.invoke(firstWord,
arguments); } catch (NoSuchMethodException e) { System.out.println(e); } catch (IllegalAccessException e) { System.out.println(e); } catch (InvocationTargetException e) { System.out.println(e); } return result; } }
29
Doing the same in Smalltalk
meth ← #,.
^'Hello ' perform: meth
with: 'everybody.'
30
Finding an object’s class
• Simply send it the message class
• Example:t ← Test new.
^t class
• Result:Test
31
Finding out if an object responds to a message
• Send respondsTo: to an object or canUnderstand: to a class. E.g.
o ← 'hello'.
^o respondsTo: #first
Collection canUnderstand: #size
32
Implementation of canUnderstand:
canUnderstand: selector "Answer whether the receiver can respond to the message whose selector is the argument. The selector can be in the method dictionary of the receiver's class or any of its superclasses."
(self includesSelector: selector) ifTrue: [^true].superclass == nil ifTrue: [^false].^superclass canUnderstand: selector
• (Implemented in class Behaviour)
33
Finding all messages to which it responds
• Collection allSelectors• Returns a Set of Symbols:• Set (rootStubInImageSegment: hash
copyAddedStateFrom: propertyList anyOne cCode: hashMappedBy: caseOf:otherwise: wantsSteps truncated ifNil:ifNotNil: asBag respondsTo: species detectMax: caseOf: initialDeepCopierSize range confirm: adaptToInteger:andSend: ~~ readDataFrom:size: windowActiveOnFirstClick * perform:with: finalize comeFullyUpOnReload: primitiveError: deepCopy ...
34
Compiling a new method into a class
• Suppose that we have a class Test. Then evaluating:Test compile:
'double: aNumber
^aNumber * 2’
creates a new method for Test on the fly! So now we can do:Test new double: 42
and get the result 84.
35
Individual object behaviour
• Consider the following class definition:Object subclass: #Test
instanceVariableNames: 'meth 'classVariableNames: ''poolDictionaries: ''category: 'Kernel-Objects'
• Instance methods:setSpecialMethod: aBlock
meth ← aBlockspecialMethod: aValue
^meth value: aValue
36
Indiv. object behaviour (ctd.)
• Then can create objects that behave differently. E.g.
t1 ← Test new.
t1 setSpecialMethod: [:i | i * 2].
t2 ← Test new.
t2 setSpecialMethod: [:i| i - 1].
^Array with: (t1 specialMethod: 42) with: (t2 specialMethod: 42)
• Result is:(84 41 )