15.1Defining A Model
It is a good software engineering practice to clearly separate the domain from its views. This is a common practice which allows one to change the rendering or even the rendering framework without having to deal with the internal aspects of the model. Thus, we will begin by presenting a simple model for a todo list that contains todo items as shown by Figure 97.
ToDoItem Class. A todo item is characterized by a title, a due date and a status which indicates whether the item is done.
Object subclass: #ToDoItem
instanceVariableNames: 'title due done'
classVariableNames: ''
poolDictionaries: ''
category: 'ToDo-Model'
It has accessor methods for the instance variables title
, due
and done
.
ToDoitem>>title
^ title
ToDoitem>>title: aString
title := aString
ToDoItem>>due
^ due
ToDoItem>>due: aDate
due := aDate asDate
ToDoItem>>done
^ done
ToDoItem>>done: aBoolean
done := aBoolean
We specify the default values when a new todo item is created by defining a method initialize
as follows:
ToDoItem>>initialize
self title: 'ToDo Item'.
self due: Date tomorrow.
self done: false.
A word about initialize
and new
. Squeak/Pharo is the only Smalltalk dialect that performs automatic object initialization. This greatly simplifies the definition of classes. If you have defined an initialize
method, it will be automatically called when you send the message new
to your classes. In addition, the method initialize
is defined in the class Object
so you can (and are encouraged) to invoke potential initialize
methods of your superclasses using super initialize
in your own initialize
method. If you want to write code that is portable between dialects, you should redefine the method new
in all your root classes (subclasses of Object
) as shown below and you should not invoke initialize
via a super call in your root classes.
ToDoItem class>>new
^ self basicNew initialize
In this book we follow this convention and this is why we have not added super initialize
in the methods ToDoItem>>initialize
and ToDoList>>initialize
.
We also add two testing methods to our todo item:
ToDoItem>>isDone
^ self done
ToDoItem>>isOverdue
^ self isDone not and: [ Date today > self due ]
ToDoList Class. We now create a class that will hold a list of todo items. The instance variables will contain a title and a list of items. In addition, we define a class variable Default
that will refer to a singleton of our class.
Object subclass: #ToDoList
instanceVariableNames: 'title items'
classVariableNames: 'Default'
poolDictionaries: ''
category: 'ToDo-Model'
You should next add the associated accessor methods title
, title:
, items
and items:
.
The instance variable items
is initialized with an OrderedCollection
in the initialize
method:
ToDoList>>initialize
self items: OrderedCollection new
We define two methods to add and remove items.
ToDoList>>add: aTodoItem
self items add: aTodoItem
ToDoList>>remove: aTodoItem
^ self items remove: aTodoItem
Now we define the class-side method default
that implements a lazy initialization of the singleton, initializes it with some examples and returns it. The class-side method reset
will reset the singleton if necessary.
ToDoList class>>default
^ Default ifNil: [ Default := self new ]
ToDoList class>>reset
Default := nil
Finally, we define a method to add some todo items to our application so that we have some items to work with.
ToDoList class>>initializeExamples
"self initializeExamples"
self default
title: 'Seaside ToDo';
add: (ToDoItem new
title: 'Finish todo app chapter';
due: '11/15/2007' asDate;
done: false);
add: (ToDoItem new
title: 'Annotate first chapter';
due: '04/21/2008' asDate;
done: true);
add: (ToDoItem new
title: 'Watch out for UNIX Millenium bug';
due: '01/19/2038' asDate;
done: false)
Now evaluate this method (by selecting the self initializeExamples
text and selecting do it
from the context menu). This will populate our model with some default todo items.
Now we are ready to define our seaside application using this model.