Polymorphism
From Real Software Documentation
Aim
In this lesson, we start learning to use polymorphism, which lets us take advantage of similarities between objects to simplify code.
How To Use This Lesson
You my find that you need to go back over this lesson a few times. Some of this may seem strange or even counterintuitive the first time through.
What is Polymorphism?
Polymorphism, broadly speaking, is the ability to treat similar things in similar ways. Here’s how it works in Real Studio (minus one or two details we will learn about in later lessons):
- As we saw in the last lesson, all objects must have a class;
- Classes are placed in a tree-shaped class hierarchy (each class having one superclass and as many subclasses as you wish);
- Classes inherit all the methods and properties of their superclass;
- Each class is its own type, but classes are also of the type of their superclass, the superclass of their superclass, the superclass of that, and all other superclasses reaching to the root class of the whole class hierarchy, the object class.
- You can treat an object as an instance of any of its types. This means, for example, that you can treat all the controls in a window as RectControl objects, because all controls are subclasses of the RectControl object. You could move all of them around, for example, without needing to write different code to handle each different type.
It is this last capability that is polymorphism: we can put shared behavior into a shared superclass, and write code that deals only with shared features of objects, ignoring their differences.
Properly used, polymorphism will let us reduce the amount of code we must write and debug, and to reduce the amount of work required to maintain and extend a program. However, polymorphism is a subtle feature, and learning to use it well requires both understanding and experience. Much of what we will be doing in this course is to get you on the road to using object-oriented programming effectively, and much of that comes down to using polymorphism effectively.
It will take us several lessons to cover all of Real Studio’s object-oriented programming (OOP) features. In this lesson, we will learn about class hierarchies, and about using events to extend a superclass. We will also be modifying the word counter from previous lessons so it can work with different sources of text without further modification.
An Example of a Class Hierarchy
Here is a diagram of part of the built-in Real Studio class hierarchy:
A couple of examples from this diagram: Object is a superclass for all the other classes; Control is a subclass of Object, and a superclass for Line, RectControl, PushButton and TextField.
Sometimes, it might be necessary to distinguish the immediate superclass of a class: RectControl is the immediate superclass of PushButton, for example. On other occasions, the immediate superclass of a class will just be called its superclass. Exactly which meaning of superclass is intended should be obvious from context.
Our Class Hierarchy
As another example, here is the class hierarchy we’re going to create in this lesson:The Polymorphic Word Counter
Let’s get to a working example:
- Open the project from the last lesson.
Remember how we wrote the word counting code to get its data from an Abstract Data Type? We’re going to modify our project now so that data source is an object of a particular class. We will then make the one word counter method able to get its data from any subclass of that class.
- Open the CharacterSource class.
We’re going to change it so it doesn’t do anything. Seriously.
Inheritance, Polymorphism, and Abstract Classes
We’re going to turn CharacterSource into an Abstract base class. Its role will be to define an interface for an Abstract Data Type. We will rely on subclasses to provide the actual behavior wanted. You won’t ever create a CharacterSource object as such (because it wouldn’t do anything). You’ll create an object of one of CharacterSource’s subclasses, but our CountWords method will treat that object as a CharacterSource and not need to know what the object actually is.
- Add four Event Definitions to the class.
Do this by clicking the Add Event Definition button once for each event definition. This opens an declaration area with the same options as you have for creating a new method. Name the following four Event Definitions as follows:
| Event Name | Return Type |
|---|---|
| BeginEvent | (none) |
| CurrentCharEvent | String |
| FinishedEvent | Boolean |
| LengthEvent | Integer |
Events
Event Definitions provide a means for code defined in the superclass to call code defined in the subclass, without having to know anything about the subclass. What we are going to do now is to modify the methods in this class to do nothing but call the events we just defined; we are then going to create two subclasses, one to return text from the Clipboard, and another to return text from a file.
- Delete the ClipBoardSource property from CharacterSource.
You can select each property in the Code Editor browser area and then right+click (Command-click on Macintosh) and select Delete from the contextual menu.
- Modify the methods in CharacterSource to read:
- Switch to the Project Editor and click on the Add Class button. Name the new class ClipboardSource and enter CharacterSource as its Super.
- Double-click on the class to open its Code Editor. Expand the Event Handlers in the pane on the left.
We see the four events we just defined here automatically because this class is a subclass of CharacterSource.
- Add the property we just removed from CharacterSource: ClipBoardSource As Clipboard
- Enter the following code into the four Event handlers.
BeginEvent:
//Post: CurrentPosition == 0
CurrentPosition = 0
ClipBoardSource = New Clipboard
CurrentCharEvent
//This method is not called more often than the length of the clipBoard text
//Post: each time this method is called, the characters on the clipboard text are returned in order
CurrentPosition = CurrentPosition + 1
Return Mid (ClipBoardSource.Text, CurrentPosition, 1)
//Post: return true if the last call to CurrentChar returned the last character of the clipBoard text, else return false
Return (CurrentPosition > Length)
LengthEvent
//Post: the length of the text on the clipBoard is returned
Return Len (ClipBoardSource.text)
- Modify Window1 to look like this:
- Change the name of the top PushButton to “CountClipButton” and Caption to “Count Clipboard”.
Now, we need to modify CountWords to work with a CharacterSource given to it as an argument. Change its declaration so that it is passed one parameter, “Source as CharacterSource”.
//Post: Word array contains all of the words in Source;
// Count array contains corresponding count of those words in Source
Dim InWord As Boolean
Dim c, StringBuffer As String
Source.Begin // c Invariant, starting *before* first character
InWord = False //Inword Invariant
StringBuffer = "" //StringBuffer Invariant
While Not Source.Finished
//Invariants:
// c: c is the next unprocessed character in Source
// StringBuffer: StringBuffer holds the string of characters that will be the next word to be added to our list; after it is added, or between words, this string is empty
// Inword: InWord is true if the counter character of Source.text was IsWordCharacter
//Note: Other than StringBuffer, we only need to act on word boundaries
c = Source.CurrentChar //c invariant
if InWord then
if not IsWordCharacter (c) or Source.Finished then //Word End; emit word
AddWord StringBuffer //StringBuffer Invariant
StringBuffer = "" //StringBuffer Invariant
InWord = false //InWord invariant
else
StringBuffer =StringBuffer + c
end if
else //not InWord
If IsWordCharacter (c) then //WordBegin; start buffering
InWord = true //InWord invariant
StringBuffer = StringBuffer + c
if Source.Finished then
AddWord StringBuffer
end if
end if
end if
Wend
- Set the Action event handler of the Count Clipboard button to:
Dim counter As integer
Dim Source As ClipBoardSource
Source = New ClipBoardSource
CountWords(Source)
For counter = 0 to UBound(Word)
Word_List.AddRow str(Count(counter))
Word_List.cell(counter,1) = Word(counter)
Next
- Put a breakpoint at the top of the Action event handler we just created, put something short (a few characters will do) on the Clipboard, run the program, and step through the process of executing the code in the CharacterSource and the ClipboardSource classes.
You’ll see that the methods that CountWords calls from its Source argument go first to the methods in CharacterSource, then down to the event handlers in the Clipboard-Source subclass. Functions return their results in the opposite order: back to the CharacterSource function that called the event handler, then out to CountWords where the original method was called.
Classes vs. Objects
It is important to be clear on terminology here: the class is the definition of what an object does; the object is the actual usable thing we create when our program runs. The object contains data and does the things the class defines. We say the object is an instance of the class.
An object is always an instance of more than one class: in this case, Source is an instance of both ClipboardSource and CharacterSource. Within the button’s Action event handler, we treat it as a ClipboardSource, because we need to create a fully functional, non-abstract class that actually does things. However, CountWords has a CharacterSource variable as its argument. When we call CountWords, its Source argument ends up pointing at the same object we just created (remember our discussion about how object variables don’t contain an object, but only refer to them?).
Polymorphism in Action
This is polymorphism in action: we could pass CountWords any class that has CharacterSource as its superclass, without further modifying the CountWords method. Any details required to make it work with a particular source of data will be provided in the event handlers of the particular CharacterSource subclass in question.
This means we can debug CountWords in isolation, and also reduce the amount of code we need to write to count words coming from a new source of data. We will see that right now in the code to implement the Count File…button’s behavior.
More on Events
Events are the most important extension mechanism in REAL Studio. The events you define and intercept in classes are the same mechanism as the event handlers we’ve been writing for controls. You could create your own, specialized button class, for example, by creating a new class, setting its Super to PushButton, and then providing code in its Action or other event handlers. You can also define new events in your new class that will be available to any further subclasses, and to any instance of that button that you drag from your project window into a Window you create.
Events as an extension mechanism are unique to REAL Studio. Other object-oriented programming languages use a very different extension mechanism called method overriding. Method overriding is also available in REAL Studio, and we will learn about it in later lessons. Don’t look for much advice on how to use events, or when to use events instead of method overriding. Apart from what we will develop in this course, and what you may find on the internet by other REAL Studio programmers or in the few books on REAL Studio, there isn’t any.
The File Counter
Now, we’re going to create the code to make the Count File…button work. As usual, if you need to, look up the information on how to handle files in Real Studio, in the built-in reference.
- In the Project Editor, create a new class. Name it FileSource, and set its Super to CharacterSource.
- Open the class’s Code Editor and add the following properties:
| Name | Data Type |
|---|---|
| CurrentPosition | Integer |
| TheStream | BinaryStream |
- Add the following code to the Event Handlers:
BeginEventCurrentPosition = 0
TheStream.position = 0
CurrentCharEventCurrentPosition = CurrentPosition + 1
Return Chr(TheStream.ReadByte)
FinishedEvent
Return CurrentPosition> Length
LengthEvent
Return TheStream.Length - Add the following method to the FileSource class:
- Add the following code to the Action event handler for the Count File… button:
- Choose Project ↠ Add ↠ File Type Set.
You will see a new FileTypes icon in your Project tab. Double-click on it to see the File Type Sets Editor:
- Choose text/plain from the Add Common File Type menu in the File Type Sets toolbar.
It adds the entry for generic text files to the File Type Sets panel. Note that its MacType is “TEXT” and its MacCreator is a string of question marks, meaning that any creator is fine. By default, it adds a number of common extensions for text files, including “.txt” and “.text” and you can add to this list.
A Minor Issue
We wrote our own means of detecting the end of file in the code above (keeping a CurrentPosition count, and comparing it to the file length). The BinaryStream provides both a Position property and an EOF property, but the way they are implemented doesn’t suit our purpose. EOF is True before we’ve read the last character, and Position will be equal to the length of the file both before and after reading the last character. So we can’t write a proper While loop based on either of these properties. For example, if we wrote:
we wouldn’t return the last character in the file. If we wrote:
we would have the same problem. Other ways of using these built-in mechanisms can be devised to read the last character, but without making them fairly complicated, they will fail if we try to use a zero-length CharacterSource (try it!).
This isn’t a huge issue, though; we just wrote our own mechanism.
//and copy the results into Word_List
Dim Counter As Integer
Dim Source As FileSource
Dim f As FolderItem
Dim s As BinaryStream
Source = new FileSource
//Request file
f=GetOpenFolderItem("text/plain")
If f<>Nil then
s=BinaryStream.Open(f,False)
Source.SetFile s
CountWords(Source)
For Counter = 0 to UBound(Word)
Word_List.AddRow str(Count(counter))
Word_List.cell(counter,1) = Word(counter)
Next
End if
Notice how we added an extra method to our class. Objects of type CharacterSource are guaranteed to support four methods (Begin, CurrentChar, Finished and Length), but objects of type FileSource support an extra method (SetFile). Notice how the button knows that it is working with a FileSource, so it can set the file, but CountWords doesn’t know about this extra method, and doesn’t need to.
File Types
We need to tell REAL Studio what types of files it should be trying to open. You should read Chapter 9 of the User’s Guide for more information about File Types.
Try It
That’s it. You might like to trace through reading a (very short) file.
Review
We’ve seen the basics of implementing a class hierarchy, and of employing polymorphism. You should go back and re-read the introductory material discussing the features we used in this program. Focus on how the class hierarchy and the events mechanism enable us to treat two different classes the same. Notice how this is an extension of the idea of the Abstract Data Type.
Object-Oriented Design
A major theme of this course is how to design applications as cleanly separated parts that let us develop and debug parts of complex programs quite separately. We saw how methods and particularly Abstract Data Types help us to do this. Inheritance and Polymorphism take this idea further: you can not only implement different ADTs separately, but you can develop and debug behavior shared between objects that implement the same ADT once. Good object-oriented design will often have a lot of its code placed toward the top of class hierarchies, which means that a lot of the code in the program is generalized and shared.
Achieving this sort of efficiency of design is as much art as science. It takes years of programming experience, and a lot of looking at well-written object-oriented code to develop good object-oriented design skills.
Explanations/References
Events are discussed in Chapter 5 and in the Adding Event Definitions section of Chapter 9 of the REAL Studio User’s Guide.
Unfortunately, the REAL Studio User’s Guide only discusses polymorphism using the other mechanism (‘virtual methods’, which we’ll cover later). But note that if you read the Adding New Events section of Chapter 9, what it is describing is polymorphism.
We’re deliberately taking this slowly, so a really good understanding of these issues will have to wait until we’ve done some more lessons.
One of the important design principles we want to adhere to is that superclasses should need to know nothing about their subclasses; by defining an event and calling it, a superclass is defining a way to communicate with its subclass, whatever it may be, while being suitably ignorant about the details of that class.
If you read Chapter 9, it may help to understand that the Virtual Methods mechanism described there is another way to have communication occur between subclass and superclass (but in that case, you put the subclass in control). We’ve started with events, because you need those to subclass controls, and we’ll just stick with events for a while, since we want to keep things simple.


