![]() |
CS 535 Object-Oriented Programming Fall Semester, 2003 Abstract Classes, Inheritance & Testing |
|
---|---|---|
© 2003, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 21-Oct-03 |
Abstract Classes
Abstract class – a class not used to create instances of itself
Concrete class – a class that we do create direct instances of
Why Abstract Classes
Defining an Abstract Class
Some languages have special syntax for abstract classes
public abstract class NoObjects { public void aFunction() { System.out.println( "Hi Mom" ); } public abstract void subClassMustImplement( int foo ); }
Smalltalk does not have special syntax for abstract classes
Collection>>do: aBlock self subclassResponsibility
What does self subclassResponsibility do?
Inform reader
How to Prohibit Instances of Abstract Class
Documentation is normally enough
Implement new so it throws an exception
Stream class>>new "Provide an error notification that Streams are not created using this message." self error: ('Streams are created with on: and with:')
How do subclass objects get created?
PositionableStream is a subclass of Stream
PositionableStream class>>on: aCollection ^super new on: aCollection “Throws and Exception”Use basicNew
PositionableStream class>>on: aCollection ^self basicNew on: aCollection
basicNew
Some Collection Classes
![]()
(N) indicates number of methods defined the class
Bold indicates concrete classes
Abstract Classes and Data
Abstract classes commonly do not have instance variables
How can they implement methods?
Identify a core set of abstract operations
Implement other methods using core methods
Collection Class
Collection does not have any instance variables
Collection implements
Collection>>detect: aBlock ifNone: exceptionBlock self do: [:each | (aBlock value: each) ifTrue: [^each]]. ^exceptionBlock value
Collection>>do: aBlock self subclassResponsibility
Abstract Classes, Types and Hinges
Tagging (declaring) a variable to be an Abstract class instance
SomeClass>>foo: aCollection ^aCollection fold: [:a :b | a max: b].
public class SomeClass { public int foo(Collection a) { blah} }
public class Resticted { public int foo(Array a) { blah} }
Abstract Classes and Hiding
Smalltalk VM on startup informs Filename of the correct concrete class for the current platform
‘foo’ asFilename Filename named: ‘foo’
Create an instance of the correct concrete Filename class
Platform Independence Aside
End of line Characters
Mac, PC and Unix have different end of line characters
When you read a file:
Abstract Classes and Hiding
![]()
String is an abstract class
String new
Strings Continued
| a | a :=String new. a class. "returns ByteString"
| b | b :=(String with: (Character value: 3585)) "3585 is Thai character". b class "returns TwoByteString"
| c | c := String with: $a. c class. “returns ByteString” c at: 1 put: (Character value: 3585). c class “returns TwoByteString”
To learn about character encodings read:
http://www.joelonsoftware.com/articles/Unicode.html
become: Smalltalk Magic
| c | c := String with: $a. c class. “returns ByteString” c at: 1 put: (Character value: 3585). c class “returns TwoByteString”
How did c change class?
a become: b
String Transformation without become?
Use composition
String has instance variable that holds real string
String forwards messages to the real string
String can replace the real string with a different object
Smalltalk.Core defineClass: #String superclass: #{Core.CharacterArray} indexedType: #none private: false instanceVariableNames: 'realString' classInstanceVariableNames: '' imports: '' category: 'Collections-Text'
size ^realString size
at: anInteger ^realString at: anInteger
at: anInteger put: aCharacter aCharacter value > 256 ifTrue: [realString := realString atTwoByteString]. realString at: anInteger put: aCharacter.
Inheritance
What should I use as a super class?
Common Mistakes
Engine Subclass of Car
![]()
Using a has-a relation for inheritance
Car subclass of Engine
![]()
“I need access to engine methods in the car class and now I have it.”
Roles Verses Classes
2.11 Be sure the abstractions you model are classes and not simply the roles objects play
![]()
BinarySearchTree>>initialize
left := LeftNode new.
right := RightNode new.
![]()
BinarySearchTree>>initialize
left := Node new.
right := Node new.
![]()
initialize mother := Mother new. father := Father new. etc.
initialize mother := Person new. father := Person new. etc.
What to Test
Everything that could possibly break
How often do accessor methods have errors?
Node>>value ^value
How many errors did your WordStream>>next method have?
Some Guidelines
Test values
Common Things that Programs Handle Incorrectly
Adapted with permission from “A Short Catalog of Test Ideas” by Brian Marick, http://www.testing.com/writings.html
Strings
Test using empty String
Collections
Test using:
Do we need to test all methods?
Character>>isWordSeparator ^self isSeparator | (#($, $. $; $' $" $? $!) includes: self)
Character>>dollarValue self isAlphabetic ifFalse:[^0]. ^self asLowercase asInteger - $a asInteger + 1
String>>dollarValue ^self inject: 0 into: [:subTotal :next | subTotal + next dollarValue]
String>>words ^self runsFailing: [:each | each isWordSeparator]
String>>dollarWords ^self words select: [:each | each dollarValue = 100]Can we just test String>>dollarWords?
If String>>dollarWords works correctly then all the other methods are highly likely to also work correctly
Benefit
Can we just test
String>>dollarValue String>>words
If these methods work what could be wrong with String>>dollarWords?
Copyright ©, All rights reserved.
2003 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 21-Oct-03    Next