Designing Objects

All of the code presented in this chapter is available at http://guzdial.cc.gatech.edu/st/clock.st.

  • Design Process
  • Clock Example
  • Subclassing: AlarmClock Example
  • Reuse: VCR Example
  • Reuse: AppointmentBook Example

    Objects Are Different

    My Experience with the Pluggable WebServer (PWS) (http://www.cc.gatech.edu/fac/mark.guzdial/squeak/pws)


    The Object-Oriented Design Process

  • Not a linear process

    Object-Oriented Analysis Stage

    Throughout all of analysis, what you're really doing is figuring out what you're doing.

    I use two kinds of activities in my object-oriented analyses.

  • The first one is simply brainstorming.
  • Second one is CRC Card Analysis

    Brainstorming

  • Try to write down all the objects ( NOUNS !)that you can think of that relate to the domain in which you are working.
  • Filtering
  • Final filter of your brainstormed candidate objects: Which of these objects are really ones that you want to be dealing with?

    CRC Cards

  • Ward Cunningham (also of WikiWikiWeb!) and Kent Beck
  • Typically, CRC cards are common 3x5 index cards.

    (I've created a Squeak window definition that you can use to play with cards on the Squeak desktop (http://guzdial.cc.gatech.edu/st/CRCWindow.st).)

  • A CRC Card has three fields:

    ./blankcrc.gif


    Using CRC Cards


    Benefits of CRC Card Analysis

  • The CRC cards can help you check that you've covered all your responsibilities, that you understand the interactions between the orjects, and that the responsibilities make sense.

  • The cards form a useful record of your early design thoughts.

  • You can play with your cards in a group!

    Object Oriented Design Stage

    Goal: A description detailed enough to code from.


    Class Diagrams

    Not UML, Coad.

    Tools: Playground http://www.oi.com and BOOST http://www.cc.gatech.edu/gvu/edtech/BOOST/home.html


    Class Diagrams

    Here's an example Coad class diagram using Joe the Box as an example.

  • Each class becomes a rectangle in this notation: Name, attributes, services.
  • The double bar around the rectangle indicates that these are concrete classes. A concrete class is one that you will actually create instances of. An abstract class is one that you create only to define functionality that you will inherit in other classes. You never create instances of an abstract class.
  • The lines between the class boxes indicate relationships. The symbol that looks like a logical AND indicates a generalization-specialization or IsA relationship.
  • We say that the relationship between NamedBox and Name would be whole-part or HasA.
  • Lines for "just talking"

    ./boxdiagram.gif


    First Design: Clock

  • So what do you think goes into a clock?

    First Design: Clock

    What goes into a clock?


    First Design: Clock

    Instance variables

    Methods


    Mistakes We Just Made


    Brainstorming a Clock


    Scenarios for CRC Cards for a Clock


    Ticker Scenario in CRC Cards

  • Control begins with the Ticker.

    ./SecondsTickerAtStart.gif


    Ticker Scenario in CRC Cards

  • Now the Clock enters the picture. The Clock must increment the representation of Time , so that is a collaborator.

    ./TickerAndClock.gif


    Ticker Scenario in CRC Cards

  • Now Time is informed that a second has gone by, so it must increment its seconds representation, which may in turn trigger the representations for minutes and hours.

    ./TickerClockTime.gif


    Display Time Scenario in CRC Cards


    Design of a Clock

  • Clock : The Clock has to be able to set the displayFormat (and thus know it, too) and return time in a given format. It needs to be able to respond to a nextSecond , and pass that on to Time. It needs to know about Time.

    It does not actually collaborate with the SecondsTimer, so the Clock doesn't really need to know about that object. I found, however, that it becomes useful for the Clock to know its timer when you think about starting and stopping the Clock. Starting and stopping the clock is really about asking the timer to stop firing.


    Design of a Clock

  • SecondsTimer :The SecondsTimer has to know its clock , in order to be able to tell it when a second has passed. The SecondsTimer has to be able to turn on and off (start and stop). It is probably going to use some kind of external process to generate the timing signals, so it will need to know its process .

    Design of a Clock

  • Time : Time must be able to track the hours, minutes, and seconds. It must be able to increment the number of seconds, and have that addition flow into the other units, too. It really doesn't need to know about any other objects.


    Design of a Clock

    ./clock1.gif

  • Delegating current time

    Alternatives

  • How else could we have done this?
  • SecondsTimer updates Time?
  • Time handles formatting?

    Programming the Clock

     
    Object subclass: #Clock
    	instanceVariableNames: 'time timer displayFormat '
    	classVariableNames: ''
    	poolDictionaries: ''
    	category: 'ClockWorks'!
     

     
    Object subclass: #SecondsTimer
    	instanceVariableNames: 'clock process '
    	classVariableNames: ''
    	poolDictionaries: ''
    	category: 'ClockWorks'!

    Now we can be language dependent: Time is given.


    Programming the SecondsTimer

    Interesting part: The timing process

     
    SecondsTimer methodsFor: 'time management' stamp: 'mjg 9/27/1998 14:12'!
    
    startTicking
    	process := [[true] whileTrue: [(Delay forSeconds: 1) wait. clock nextSecond.]] newProcess.
    	process priority: (Processor userBackgroundPriority).
    	process resume.! !
     

     
    SecondsTimer methodsFor: 'time management' stamp: 'mjg 9/26/1998 17:04'!
    
    stopTicking
    	process terminate.! ! 


    Programming the Clock

    Starting and stopping the Timer

     
    Clock methodsFor: 'time management' stamp: 'mjg 9/27/1998 14:21'!
    
    start
    	timer isNil ifFalse: [timer stopTicking. "Stop one if already existing."].
    	timer _ SecondsTimer new.
    	timer clock: self.
    	timer startTicking.
     !
    

    Clock methodsFor: 'time management' stamp: 'mjg 9/27/1998 14:37'! stop timer isNil ifFalse: [timer stopTicking]. timer _ nil. !


    Programming the Clock

    Setting the Time

     
    Clock methodsFor: 'time management' stamp: 'mjg 9/26/1998 16:56'!
    
    setTime: aString
    	time _ Time readFrom: (ReadStream on: aString).
     !
    
     

    Programming the Clock

    Incrementing Seconds

     
    Clock methodsFor: 'time management' stamp: 'mjg 9/27/1998 14:10'!
    
    nextSecond
    	time _ time addTime: (Time fromSeconds: 1)
     ! !
     


    Programming the Clock

    Display Format

     
    Clock methodsFor: 'time management' stamp: 'mjg 9/26/1998 16:57'!
    
    displayFormat: aType
    	"aType should be '24' or '12'"
    	displayFormat _ aType
     !
    
     

     
    Clock methodsFor: 'time management' stamp: 'mjg 9/26/1998 17:22'!
    
    display
    	"Display the time in a given format"
    	| hours minutes seconds |
    	hours _ time hours printString.
    	minutes _ time minutes printString.
    	(minutes size < 2) ifTrue: [minutes _ '0',minutes]. "Must be two digits"
    	seconds _ time seconds printString.
    	(seconds size < 2) ifTrue: [seconds _ '0',seconds]. 
    	(displayFormat = '12')
    	ifTrue: [(hours asNumber > 12) 
    		ifTrue: [^((hours asNumber - 12) printString),':',minutes,':',
    			seconds,' pm'].
    		(hours asNumber < 12)
    		ifTrue: [^hours,':',minutes,':',seconds,' am']
    		ifFalse: ["Exactly 12 must be printed as pm"
    			^hours,':',minutes ,':',seconds,' pm']]
    	ifFalse: ["24-hour time is the default if no displayFormat is set"
    		^hours,':',minutes,':',seconds].! !
     


    Trying out the clock

  •  
    
    cl := Clock new.
    cl displayFormat: '12'.
    cl setTime: '2:05 pm'.
    cl start.
     
  • Transcript show: cl display .
  • cl stop .

    Specializing Clock as an AlarmClock

  • What should be in an AlarmClock?

  • Well?


    Specializing Clock as an AlarmClock

    ./AlarmClockCRC.gif


    Specializing Clock as an AlarmClock

  • Make the alarm behavior a general block

    ./alarmClock.gif


    Didn't that gag?

  • A block exists in Smalltalk only!
  • Alarm should actually be defined as a class now, make it a Smalltalk-specific thingie as last as possible.

    Programming the AlarmClock

     
    Clock subclass: #AlarmClock
    	instanceVariableNames: 'alarmTime alarmBlock '
    	classVariableNames: ''
    	poolDictionaries: ''
    	category: 'ClockWorks'!
     

    Programming the AlarmClock

    Handling Alarm Time

     
    AlarmClock methodsFor: 'time management' stamp: 'mjg 9/27/1998 13:58'!
    
    nextSecond
    	super nextSecond.
    	(time = alarmTime) ifTrue: [alarmBlock value].
     !
    

    AlarmClock methodsFor: 'time management' stamp: 'mjg 9/27/1998 13:58'! setAlarmTime: aString alarmTime _ Time readFrom: (ReadStream on: aString). !


    Testing the AlarmClock

  •  
    
    cl _ AlarmClock new.
    cl setTime: '2:04 pm'.
    cl alarmBlock: [3 timesRepeat: [Smalltalk beep. Transcript show: 'ALARM!']].
    cl setAlarmTime: '2:06 pm'.
    cl start 

  • cl stop .


    Reuse in a VCR

    Scenario: Starting and stopping a recording

    ./VCRCRC.gif


    The Power of Aggregation

  • Real power of O-O
  • Look at where everything is, yet little knows about anything else.

    ./vcr.gif


    Side Trip: Good Design

    Characteristics of a Good Object-Oriented Design


    Reuse in an AppointmentBook

    What are the new objects in an AppointmentBook?


    Reuse in an AppointmentBook

    Filtering and Designing

    ./APPOINTMENTS.jpg


    Programming the AppointmentBook

     
    AppointmentBook methodsFor: 'initialization' stamp: 'mjg 9/27/1998 14:07'!
    
    initialize
    	appointments _ OrderedCollection new.
     !
    
     
  • OrderedCollection: Like an extendable Array (or Java Vector )
  • There are lots of collections: Bags (no ordering, duplications allowed), Sets (no ordering, no duplicates), SortedCollections , etc.
  • Each is optimized for its characteristics

    Programming the AppointmentBook

     
    AppointmentBook methodsFor: 'appointments' stamp: 'mjg 9/27/1998 14:29'!
    
    makeAppointment: aDescription for: aDate at: aTime
    	| a |
    	a _ Appointment new.
    	a description: aDescription.
    	a date: (Date readFrom: (ReadStream on: aDate)).
    	a alarm: aTime.
    	appointments add: a.
     !
    
     
  • Date class understands how to manipulate dates and answer questions about dates (e.g., "What day of the week was July 4, 1776?" (Date readFrom: (ReadStream on: 'July 4, 1776')) weekday => Thursday )

    Programming the AppointmentBook

     
    AppointmentBook methodsFor: 'appointment alarms' stamp: 'mjg 9/27/1998 14:31'!
    
    onToday
    	(appointments select: [:each | each date = Date today]) do: [:each | each on].
     ! 

     
    AppointmentBook methodsFor: 'appointment alarms' stamp: 'mjg 9/27/1998 14:36'!
    
    allOff
    	appointments do: [:appointment | appointment alarm stop].
     ! 

  • All collections understand do: , select: , collect: , reject:
  • Note: Not quite orthogonal!

    Progamming the Appointments

     
    Appointment methodsFor: 'accessing' stamp: 'mjg 9/27/1998 14:32'!
    
    alarm: someTime
    	alarm _ AlarmClock new.
    	alarm setAlarmTime: someTime.
     !
    
     

     
    Appointment methodsFor: 'accessing' stamp: 'mjg 9/27/1998 14:18'!
    
    alarm
    	^alarm
     !
    
     

  • Skipping lots of accessors

    Programming the Appointments

     
    Appointment methodsFor: 'appointment alarms' stamp: 'mjg 9/27/1998 14:34'!
    
    on
    	"The appointment is today, so turn on alarm."
    	alarm alarmBlock: [3 timesRepeat: [Smalltalk beep.].
    		Transcript show: 'Appointment: ',description.
    		alarm stop.].
    	alarm setTime: (Time now printString).
    	alarm start.
     ! 

    Testing AppointmentBook

    We can test the AppointmentBook now.

     b _ AppointmentBook new initialize.
    b makeAppointment: 'Testing!' for: '9/27/98' at: '2:34 pm'.
    b onToday.
     

    When you're declaring the end of the day, be sure to use b allOff


    Summarizing: Implementing Models

  • Attributes -> Instance Variables, and Services -> Methods

    (At least, most of the time)

  • Gen-Spec -> Superclass-Subclass
  • Part-Whole can map several ways: