270 likes | 280 Vues
Explore the core concepts of Smalltalk by studying the Object and Class classes. Learn about methods, blocks, booleans, and runtime type information through practical implementation examples.
E N D
Everything is an Object • Examining builtin classes • All these are classes in Little Smalltalk: • Object • Class • Method • Block • Boolean • True • False • Run time type information • Implementation of new • Printing objects
Learning Smalltalk The best way to learn Smalltalk is to study the definitions of the builtin classes. A file containing the implementation of any class may be produced by following the steps bellow: >F <- File new name: 'block.st' File >F open: 'w' File >Block fileOutOn: F Block >F close File >^D
The Class Object • The class Object is the root of Little Smalltalk’s hierarchy. • Every other class inherits from it, either directly or indirectly. • The existence of a single root is useful to assure that some messages are understood by any object. • The methods defined in the class Object may be: • Used to define a common behaviour. These methods need not be overridden. Their implementation is based on other methods. • Used to provide a default behaviour. These methods should be overridden by subclasses when appropriate. • Some methods are implemented by calling primitives. A primitive is a kind of external function. • The class Object is an Abstract Class. • Question: What is the superclass of Object ?
Objects’ Printability • The method print provides a common behaviour. Its implementation is based on the method printString. Class Object nil Methods Object print self printString print • The method printString provides a default behaviour. The default is returning the name of the receiver’s class. Methods Object printString ^self class printString • The method class is implemented by a primitive. Methods Object class ^< 11 self >
Equality Tests • The method == returns true if the receiver and argument are the same object. It is implemented by a primitive. Methods Object == aValue ^ < 21 self aValue > • The method ~~ is based on ==. It needs not be overridden. Methods Object ~~ aValue ^ ( self == aValue ) not • The method = returns true if the receiver and argument are objects with the same value. It must be overridden. Methods Object = aValue ^ self == aValue
Collection’s Equality • The class Collection overrides the method = Methods Collection = coll self do: [ :x | ( self occurrencesOf: x ) = ( coll occurrencesOf: x ) ifFalse: [ ^false ] ]. ^true • The class Collection provides a default behaviour that is more appropriate for its subclasses, based on method do: • We should override the method = in class Complex Class Complex Object r i Methods Complex = aComplex ^ r = aComplex realpart and: [ i = aComplex imagpart ] BUG if selfcoll
Copying Objects • The method shallowCopy returns a copy of the receiver. Methods Object shallowCopy | newObj | newObj <- self class new. ( 1 to: self basicSize ) do: [:i | newObj basicAt:i put: ( self basicAt:i ) ] ^newObj • The method shallowCopy provides common behaviour, based on methods basicSize and basicAt:, which are implemented by primitives. • The method deepCopy copies also all of the objects within the receiver, recursively. It must be overridden. • The method copy may be either a shallow or a deep copy, depending upon what is appropriate for the receiver.
Deep Copy in Array The class Array overrides the method deepCopy. Class Array IndexedCollection Methods Array deepCopy ^ self deepCopyFrom: 1 to: self size | deepCopyFrom: low to: high | newArray newlow newhigh | newlow <- low max: 1. newhigh <- high min: self size. newArray <- self class new: (0 max: newhigh - newlow + 1). ( newlow to: newhigh ) do: [ :i | newArray at: i - newlow + 1 put: ( self at: i ) copy ]. ^newArray
The Class Class • In Smalltalk, everything is an object. • Classes are part of Smalltalk, hence classes are objects. • The class Class defines the attributes and methods that are common to any Smalltalk class. • The class Class is not an Abstract Class. • Every class in Little Smalltalk is an instance of Class. • As any class, the class Class is a subclass of Object. • As any class, the class Object is an instance of Class. • Actually, the class Class is an instance of itself. • Who was born earlier, the chicken or the egg?
Class Attributes • The attributes of any class are: • a name • a superclass • a set of methods • a set of instance variables • the instance size • Additionally, classes have behaviour, which is defined by the methods implemented in the class Class. • The most important method is the one that makes the class able to generate new instances. • When we send to a class the message new, it executes the method new defined in the class Class.
The Method New The method new first creates a new instance and then sends to it the message new. Methods Class new | newObject | newObject <- self new: instanceSize. ^ self == Class ifTrue: [ newObject initialize ] ifFalse: [ newObject new ] | new: size ^ < 22 < 58 size > self > | initialize superClass <- Object. instanceSize <- 0. methods <- Dictionary new new’s implementation is buggy in several fundamental classes, such as: LongInteger, Interval, Collection and Char.
Looking for Methods When we send a message to an object, we first look for the method in its class. If it is not found we look recursively in the superclasses, until it is found or we reach the root. Methods Object respondsTo: message self class upSuperclassChain: [ :c | ( c methodNamed: message ) notNil ifTrue: [ ^true ] ]. ^false Methods Class upSuperclassChain: aBlock aBlock value: self. superClass notNil ifTrue: [ superClass upSuperclassChain: aBlock ]
Run-Time Type Information • Since Smalltalk is dynamically typed it is useful to ask about the identity of the objects. • The methods isMemberOf: and isKindOf: may be used for type checking. Methods Object isMemberOf: aClass ^self class == aClass | isKindOf: aClass self class upSuperclassChain: [ :c | ( c == aClass ) ifTrue: [ ^true ] ]. ^false
Type Checking We could implement type checking for complex numbers. Class Complex Object r i Methods Complex realpart: aNumber ( aNumber isKindOf: Number ) ifTrue: [ r <- aNumber ] ifFalse: [ smalltalk error: 'realpart must be number.' ] | + aComplex ( aComplex isMemberOf: Complex ) ifTrue: [ ^Complex new realpart: r + aComplex realpart; imagpart: i + aComplex imagpart ] ifFalse: [ smalltalk error: 'operand must be complex.' ]
Design Techniques • The builtin classes of Little Smalltalk provide several examples of design techniques that are characteristic of Object Oriented Programming. • Some of these techniques are: • Providing additional functionality using composite methods, implemented over basic ones. • Example: The class Block. • The use of abstract classes implementing the common behavior of its subclasses. • Example: The classes Boolean, True and False. • Implementing a functionality through cooperation between distinct classes. • Example: The classes Block, Method and Context. • Example: The classes List and Link.
Composite Methods • A class should provide to its clients all the functionality that may be useful, even if not absolutely necessary. • The protocol of a class may be extended by the addition of composite methods, implemented over basic ones. • The implementation of these additional methods requires very little effort, but makes the class more powerful. • The main advantages of composite methods are: • The interface of the class becomes more abstract. • The protocol of the class becomes more complete. • The class becomes more useful. • The class becomes easier to use. • Composite methods improve reusability.
The Class Block The basic method whileTrue: is used to implement the composite methods whileTrue and whileFalse: Class Block Object context argCount argLoc bytePointer Methods Block whileTrue: aBlock ( self value ) ifTrue: [ aBlock value. self whileTrue: aBlock ] | whileFalse: aBlock [ self value not ] whileTrue: aBlock | whileTrue self whileTrue: [] ]
Abstract Classes • Abstract classes are normally derived by the generalization of its subclasses. • They provide a common behavior for their subclasses. • The methods implemented by an abstract class are normally composite. The basic methods should be provided by the subclasses. • The main advantages of abstract classes are: • Provide a common protocol for the subclasses. • Avoid several implementations of the same methods. • Allow new subclasses to be derived without having to reimplement all the functionality. • The methods implemented in an abstract class are reused by its subclasses.
The Class Boolean The class Boolean is an abstract class. Class Boolean Object Methods Boolean ifTrue: trueBlock ^self ifTrue: trueBlock ifFalse: [] | ifFalse: falseBlock ^self ifTrue: [] ifFalse: falseBlock | ifFalse: falseBlock ifTrue: trueBlock ^self ifTrue: trueBlock ifFalse: falseBlock | and: aBlock ^self ifTrue: aBlock ifFalse: [ false ] | or: aBlock ^self ifTrue: [ true ] ifFalse: aBlock ]
The Class True The class True inherits the functionality of Boolean. Class True Boolean Methods True ifTrue: trueBlock ifFalse: falseBlock ^trueBlock value | not ^false | xor: aBoolean ^aBoolean not | printString ^'true' ]
The Class False The class False inherits the functionality of Boolean. Class False Boolean Methods False ifTrue: trueBlock ifFalse: falseBlock ^falseBlock value | not ^true | xor: aBoolean ^aBoolean | printString ^'false' ]
Cooperation • Cooperation requires a division of responsibilities • The class Contextcooperates with Blockand Method • Responsibilities: • Block and Method: provide the data necessary for the computation • Context: executes the computation and returns the result • Thanks to cooperation, the functionality of Context may be shared by Block and Method without change • The class Linkcooperates with List • Responsibilities: • Link: provides a recursive implementation for the basic methods of List • List: treats the special case of empty list and implements composite methods • Thanks to cooperation, some operations may be easily implemented by List, e.g., reverseDo:
Block and Method Class Block Object context argCount argLoc bytePointer Methods Block value: x ^ (self checkArgumentCount: 1) ifTrue: [ context at: argLoc put: x. context returnToBlock: bytePointer ] ] Class Method Object text message bytecodes literals stackSize temporarySize class watch Methods Method executeWith: arguments ^ ( Context new ; method: self ; temporaries: ( Array new: temporarySize) ; arguments: arguments ) returnToBlock: 1 ]
The Class Context Class Context Object linkLocation method arguments temporaries Methods Context method: m method <- m | arguments: a arguments <- a | temporaries: t temporaries <- t | at: key put: value temporaries at: key put: value | returnToBlock: bytePtr <28 self bytePtr> | blockReturn <18 self> ifFalse: [ ^ smalltalk error: 'incorrect context for block return'] ]
Removing an Element Class List Collection links Methods List remove: value (links notNil) ifTrue: [ links <- links removeValue: value ] ] Class Link Object key value nextLink Methods Link removeValue: aValue (aValue = value) ifTrue: [ ^ nextLink ] ifFalse: [ (nextLink notNil) ifTrue: [ nextLink <- nextLink removeValue: aValue ] ] ]
Doing in Reverse Class List Collection links Methods List reverseDo: aBlock (links notNil) ifTrue: [ links reverseDo: aBlock ] ] Class Link Object key value nextLink Methods Link reverseDo: aBlock (nextLink notNil) ifTrue: [ nextLink reverseDo: aBlock ]. aBlock value: value ]