5.5 Two Subclasses for the Account Class
This chapter continues from the previous chapter in
demonstrating how one creates classes and subclasses in
Smalltalk. In this chapter we will create two special subclasses
of Account, known as Checking and Savings. We will
continue to inherit the capabilities of Account, but will
tailor the two kinds of objects to better manage particular
kinds of accounts.
5.5.1 The Savings class
We 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. We create the class Savings as a subclass of
Account:
| 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 first define
a method for allocating a new account--we need to make sure that the
interest field starts at 0.
| !Savings methodsFor: 'initialization'!
init
interest := 0.
^ super init
! !
|
Recall that the parent took care of the new message, and
created a new object of the appropriate size. After creation,
the parent also sent an init message to the new
object. As a subclass of Account, the new object will
receive the init message first; it sets up its own
instance variable, and then passes the init message up the
chain to let its parent take care of its part of the
initialization.
With our new Savings account created, we can define
two methods for dealing specially with such an account:
| !Savings methodsFor: 'interest'!
interest: amount
interest := interest + amount.
self deposit: amount
!
clearInterest
| oldinterest |
oldinterest := interest.
interest := 0.
^oldinterest
! !
|
The first method says that we add the amount to our
running total of interest. The line self deposit: amount
tells Smalltalk to send ourselves a message, in this case
deposit: amount . This then causes Smalltalk to look up
the method for deposit: , which it finds in our parent,
Account. Executing this method then updates our overall
balance.(23)
One may wonder why we don't just replace this with the
simpler balance := balance + amount . The answer lies
in one of the philosophies of object-oriented languages in general,
and Smalltalk in particular. Our goal is to encode a
technique for doing something once only, and then re-using
that technique when needed. If we had directly encoded
balance := balance + amount here, there would have been
two places that knew how to update the balance from a
deposit. This may seem like a useless difference. But consider
if later we decided to start counting the number of
deposits made. If we had encoded
balance := balance + amount in each place that needed to
update the balance, we would have to hunt each of them down in
order to update the count of deposits. By sending self
the message deposit: , we need only update this method
once; each sender of this message would then automatically get the correct
up-to-date technique for updating the balance.
The second method, clearInterest , is simpler. We
create a temporary variable oldinterest to hold the current
amount of interest. We then zero out our interest to
start the year afresh. Finally, we return the old interest
as our result, so that our year-end accountant can see how
much we made.(24)
5.5.2 The Checking class
Our second subclass of Account represents a checking
account. We will keep track of two facets:
-
What check number we are on
-
How many checks we have left in our checkbook
We will define this as another subclass of Account:
| Account subclass: #Checking
instanceVariableNames: 'checknum checksleft'
classVariableNames: ''
poolDictionaries: ''
category: nil !
|
We have two instance variables, but we really only need to
initialize one of them--if there are no checks left, the current
check number can't matter. Remember, our parent class
Account will send us the init message. We don't need our
own class-specific new function, since our parent's will
provide everything we need.
| !Checking methodsFor: 'Initialization'!
init
checksleft := 0.
^super init
! !
|
As in Savings, we inherit most of abilities from our superclass,
Account. For initialization, we leave checknum
alone, but set the number of checks in our checkbook to
zero. We finish by letting our parent class do its own
initialization.
5.5.3 Writing checks
We will finish this chapter by adding a method for
spending money through our checkbook. The mechanics of taking
a message and updating variables should be familiar:
| !Checking methodsFor: 'spending'!
newChecks: number count: checkcount
checknum := number.
checksleft := checkcount
!
writeCheck: amount
| num |
num := checknum.
checknum := checknum + 1.
checksleft := checksleft - 1.
self spend: amount.
^ num
! !
|
newChecks: fills our checkbook with checks. We record
what check number we're starting with, and update the count
of the number of checks in the checkbook.
writeCheck: merely notes the next check number, then
bumps up the check number, and down the check count. The
message self spend: amount resends the message
spend: to our own object. This causes its method to be looked
up by Smalltalk. The method is then found in our parent class,
Account, and our balance is then updated to reflect our
spending.
You can try the following examples:
| Smalltalk at: #c put: (Checking new) !
c printNl !
c deposit: 250 !
c printNl !
c newChecks: 100 count: 50 !
c printNl !
(c writeCheck: 32) printNl !
c printNl !
|
For amusement, you might want to add a printOn: message to
the checking class so you can see the checking-specific
information.
In this chapter, you have seen how to create subclasses
of your own classes. You have added new methods, and inherited
methods from the parent classes. These techniques provide
the majority of the structure for building solutions to
problems. In the following chapters we will be filling in
details on further language mechanisms and types, and providing
details on how to debug software written in Smalltalk.
|