A class describes a data structure in terms of its variables and its algorithms. This section shows how a very simple data structure is implemented as a class and it shows how this data structure can be extended by a subclass.
First we discuss the data-type like aspects of classes.
Object subclass: #Person instanceVariableNames: 'name givenName dateOfBirth' classVariableNames: '' poolDictionaries: '' category: 'Tutorial'
This data type has three fields - instance variables - to store a persons name, given name and date of birth. The instance variables name and givenName shall take string values, the value of instance variable dateOfBirth is assumed to be a Date. These details are not declared and cannot be checked by the Smalltalk compiler.
The structure of the class is depicted by this diagram:
The title gives the name of the class, the part below the title contains the data fields - the variables - of the class.
A variable of type Person is created with:
| instanceOfPerson | instanceOfPerson := Person new. instanceOfPerson
To examine the object that you create with this piece of code, you should evaluate it with 'inspect it'. You will get an inspector view that you can use to read (and to modify) the values of the instance variables of the object.
new is a message to a class that creates
a new instance of the class. When sent to class Person,
this message allocates memory for a new Person and answers this
new person. In some programming languages,
The class method new initializes all instance variables of a new instance with the value nil.
To access the fields of the data structure, we need access functions.
setName: aString name := aString.
getName ^name.
setGivenName: aString givenName := aString.
getGivenName ^givenName.
setDateOfBirth: aDate dateOfBirth := aDate.
getDateOfBirth ^dateOfBirth.
With these access methods, we can now create a record that describes a person:
| instanceOfPerson | instanceOfPerson := Person new. instanceOfPerson setName: 'Churchill'; setGivenName: 'Winston'; setDateOfBirth: (Date newDay: 30 month: 11 year: 1874). instanceOfPerson
When you evaluate this with 'inspect it', you can see that all instance variables have now values. Select the instance variable dateOfBirth and inspect it: You see that a date is in turn an object with instance variables.
To create a record of type 'Person' - an instance of class 'Person', as we prefer to say it in the terminology of Smalltalk, we can define an instance creation method:
name: familyName givenName: givenName dateOfBirth: aDate ^self new setName: familyName; setGivenName: givenName; setDateOfBirth: aDate.
This method is defined on the class side of the protocol. With this class method, we can create an initialized instance with one single statement:
| instanceOfPerson | instanceOfPerson := Person name: 'Churchill'; givenName: 'Winston'; dateOfBirth: (Date newDay: 30 month: 11 year: 1874). instanceOfPerson
One may think that a class method is essentially a convenient shorthand notation for instance creation. But in fact it is more than that: A class method creates and initializes an instance.
The use of individually defined instance creation methods facilitates the creation of fully initialized instances. The creation of incompletely initialized instances is thus easily avoidable. The definition and use of individually crafted instance creation methods is therefore highly recommended. Interesting examples of individually defined instance creation methods can be found in the class protocol of some view classes. The interested reader should look at:
Among these methods, on:list:selected:changeSelected:menu:keystroke: is the most general one, it allows the sender to provide default values for five instance variables. The other methods provide default values for instance variables that mentioned in the method selector. Note that the shorter methods call the most general method to actually create an instance, this call makes the relatiionship between a specialized method and the most general method explicit.
Among these methods, on:getState:action:label:menu: is the most general one, it allows the sender to provide default values for five instance variables. The other methods provide default values for instance variables that mentioned in the method selector.
Often, we have to keep additional data for certain kinds of persons. For an employee, we want additionally to know the kind of work he does, his salary and the department where he passes the working days.
An employee is of course a person, but one in a specific role for which additional information is available. To store that additional information, it is desirable to extend the definition of class Person. This is possible, it is even easy: We define a subclass, that adds additional instance variables and additional methods to its superclass:
Person subclass: #Employee instanceVariableNames: 'kindOfWork salary department dateOfHirement' classVariableNames: '' poolDictionaries: '' category: 'Tutorial'
The diagram of the class shows that the instance variables of the superclass are inherited:
The class Employee inherits also all instance methods and all class methods from its superclass.
To complete the definition of class Employee, we have to add some instance methods:
kindOfWork: aString kindOfWork := aString.
kindOfWork ^kindOfWork.
salary: anAmount salary := anAmount.
salary ^salary.
department: aString department := aString.
departement ^department.
dateOfHirement: aDate dateOfHirement := aDate.
dateOfHirement ^dateOfHirement.