[ << ] | [ < ] | [ Up ] | [ > ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following notes introduce OO using the Smalltalk language. After programming in LISP, you may be very happy with Smalltak- often the code has no brackets!
Until 5 years ago, I would have said that Smalltalk is was an historical language (something to be studied by ancient scholars). Now, maybe not (see all the interest in Seaside and GNU Smalltalk).
Anyway, we don’t study it to program in it. Rather, we use it to define an abstract spec of what an OO language should look like. From that, we will then audit GOLD- an OO language written in 100 lines of LISP.
Smalltalk was developed at Xerox Parc in the 1970s by a team that included Alan Kay, Dan Ingalls, Adele Goldberg, Ted Kaehler, Scott Wallace, and others. Alan Kay is famous for saying
The best way to predict the future is to invent it.
He wasn’t kidding. Smalltalk is famous for creating the bitmapped multi-windowed mouse-driven environment that inspired Steve Jobs to create the first Macintosh. Up until the iPhone/iPad, the Smalltalk interfaces were the dominant influence on our vision of a computer. Consider the following images from the Sept 1977 Scientific American Article, "Microelectronics and the Personal Computer" pp. 230-244, by Alan Kay. Note the definition of some core ideas:
"Windows," display frames within the larger display screen, enable the user to organize and edit information at several layers of refinement. Once the windows are created they overlap on the screen like sheets of paper. Images with various degrees of symbolic content can be display simultaneously.
Lets take a moment to review the seven main buzzwords of OO:
Classes make instances and, usually, distinction is as follows:
(By the way, here’s something really cool. Everything in Smalltalk is an instance, including classes:
Instances have attributes, operations, identity, and associations.
! Account methods ! inTheRed ^balance < 0 !!
Instances can use inheritance, classification, encapsulation and polymorpishm:
Person (has "dob", does "walk") Male (does "jump") OldMale (does "dance", and calls jump a little) YoungMale (does "dance", and calls jump a lot) Female (does "dance" and "jump")
(Aside: do Males have a "dob"? How do "OldMale"s jump?)
! Account class methods ! bigAccount ^self new minBalance: 1000000 ! smallAccount ^self new maxBalance: 20000 !!
For example, you could be modeled as a network of associations between instances of the class "Body", "Arm", and "Leg":
Note classic newbie OO mistake: associations is not inheritance;
So it is not true that "Arm" isa "Body". Rather, "Body" uses "Arm".
For another example, "Fraction" contains two integers: the "Numerator" and the "Denominator". "Fraction" is not a sub-class of "Integer"- but it does use Integers.
Lesson: a working OO system is a conversation between networks of objects.
Let’s say that we’ve got an Account
class:
Object subclass: #Account instanceVariableNames: 'balance' classVariableNames: '' poolDictionaries: '' category: nil ! !Account methods ! spend: amount balance := balance - amount ! deposit: amount balance := balance + amount ! init balance := 0 !!
This last init
method
initializes the hidden variable "balance" (actually called an instance variable ) to zero, which makes sense for an account balances.
We can create the Savings
class as a subclass of Account
. It holds money, just like an
Account
, but has an additional property that we will model: it is paid interest based on its balance.
Account subclass: #Savings instanceVariableNames: 'interest' classVariableNames: '' poolDictionaries: '' category: nil !
The instance variable "interest" will accumulate interest paid. Thus, in
addition to the "spend:" and "deposit:" messages which we inherit from
our parent, Account
, we will need to define a method to add in interest
deposits, and a way to clear the interest variable (which we would do
yearly, after we have paid taxes). We need to define a method for
allocating a new account - we need to make sure that the interest field
starts at 0.
!Savings methods ! init interest := 0. ^super init !!
Can you guess what super init
does? Its the sub-class method calling the parent.
It is like saying "do my own initializations for my own variables (interest
) then
go away and do whatever you need to do to initialize my inherited properties.
While this is a trivial example, such definitions-via-extension are very useful when dealing with (say) complex interface classes where super-classes have some intricate initialization sequence. Subclasses need not understand that initialization sequence, all they need to do is to call daddy and ask him to run that complex code.
One of our challenges for GOLD is to handle such super-class calls.
As an aside (and this is a concept we will return too in later lectures) is that one thing you want to with initialization control is handle singletons; i.e. classes for which there should only be one instance, and provide a global point of access to it.
Why singletons? Well, often, a system only needs to create one instance of a class, and that instance will be accessed throughout the program. Examples would include objects needed for logging, communication, database access, etc. You could pass such an instance from method to method, or assign it to each object in the system. However, this adds a lot of unnecessary complexity.
Singletons are most appropriate for services that do not change their nature based on their invocation context. A singleton can be created at startup based on context or be reset for larger scale state transitions
Simply put: singletons make it easy to compose a program from different components. Singletons do not create unnatural interdependencies made by passing a context object around, where every piece of code will bind to everything in the context object. Singletons stand alone.
Examples:
How to do it in Smalltalk?
Step 1: add a ClassVariable to a class.
Account subclass: #Database instanceVariableNames: '' classVariableNames: 'CurrentDatabaseConnection' poolDictionaries: '' category: nil !!
Step 2: change the new method that only returns the same instance, over and over again.
!Database class methods! new CurrentDatabaseConnection isNil ifTrue:[ CurrentDatabaseConnection := super new]. ^CurrentDatabaseConnection !!
That’s it! Now, whenever we create a "new" instance of this class, we just use the same one over and over again.
Note that there is a much much much more elegant way to create Singletons (see the uber-elegant way Io does it, on p72-74- which we’ll explore in later lectures).
In the above example, we could specialize instance creation by writing a new "new" method, that runs just for one exceptional class (DataBase’s singleton method).
That’s OO- everything is exceptional. Polymorphism means that general procedures that can specialized for particular objects without mucking up the centralized controller.
To illustate that point, we take a look at how procedural control can be defined in an OO system.
Before doing that, we have to know about "Blocks" which is Smalltalk’s version of lambda bodies.
|x y z| x := [3 > 7]. y := [:a | a > 7]. z := [:a :b | a > b]. x value. "Returns true" y value: 10 "Returns false" z value: 10 value: 20 "Returns false"
All control in Smalltalk is based on blocks. The message
3 > 7 ifTrue: [Transcript printOn: 'yes'].
The way this works is that:
3 > 7
evaluates to some subclass of Boolean
:
true
is the single instance of the class True and false
is the single
instance of the class False.
ifTrue:
with the argument [Transcript printOn: 'yes']
is handled by either a method of the class True or False.
The code for those methods is shown below:
! True methods ! ifTrue: trueBlock ^trueBlock value "We are true -- evaluate trueBlock" !! ! False methods ! ifTrue: trueBlock ^nil "We are false -- do nothing" !!
Note that this is an example of pure OO: the object-oriented message system is being enlisted to replace procedural if-then-else.
Using Blocks, trick, we can also implement for-loops. Here’s the equivalent of LISP’s dotimes
macro:
! Number methods ! to: stop do: aBlock | i | i := self. [i <= stop] whileTrue: [ "whileTrue is a built-in" aBlock value: i. i := i + 1 ] ^stop !!
For example:
1 to: 5 do: [:i| Transcript printOn: i]
And here the equivalent of LISP’s dolist macro:
! SequenceableCollection methods ! do: aBlock 1 to: (self size) do: [:i | aBlock value: (self at: i)] !!
For example:
'this is a string' do: [:char| Transcript printOn: char]
Now consider the advantages of such distributed control structures:
Which means, extending the language to bury away common processing constructs inside higher-level commands is much easier than in "C".
So there is a whole industry in building domain-specific languages. For example, here’s a state machine language, written in Smalltalk. Each machine contains states (e.g. #start, #run) which are connected by transitions (e.g. if happiness < 2 then go to #run).
! Factory methods ! worksIn: factory factory machine: (self new: 'party boy'); go: #start to: #run then: [The machine talks: 0]; go: #run to: #oddHaHa then: [The machine talks: 1]; go: #run to: #even then: [The machine talks: 2]; go: #oddHaHa to: #oddHaHa then: [The machine drinks; talks: 3]; go: #oddHaHa to: #run if: [The machine happiness < 2 ] then: [The machine talks: 4; talks: 'Too drunk to stay awake.']; go: #even to: #run; go: #run to: #endF if: [App random next > 0.9] then: [The machine talks: 5; talks: 'that is all folks'. The yourself oo. The state oo. The next oo. The machine oo. The factory oo]. !!
For more on this language, see the agent-ready code base.
For more on domain-specific languages see my notes on that topic.
[ << ] | [ < ] | [ Up ] | [ > ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated on April 19, 2011 using texi2html 5.0.