Modeling Objects

In object-oriented programming, objects are modeled to real-world objects. A real-world object has actions related to it and characteristics of its own.

Take a ball, for example. A ball can be acted on—rolled, tossed, thrown, bounced, caught. But it also has its own physical characteristics—size, shape, composition, weight, color, speed, position. An accurate data model of a real ball would define not only the physical characteristics but all related actions and characteristics in one package:

Figure 4-2. A Ball Object

In object-oriented programming, objects are the basic building blocks—the fundamental units of data.

There are many kinds of objects; for example, character strings, collections, and input and output streams. An object—such as a character string—always consists of two parts: the possible actions or operations related to it, and its characteristics or variables. A variable has a variable name, and an associated data value that can change over time. These actions and characteristics are so closely associated that they cannot be separated:

Figure 4-3. Ball Object with Variable Names and Values

To access an object's data, you must always specify an action. For example, suppose the object is the number 5. Its actions might include addition, subtraction, multiplication, and division. Each of these actions is an interface to the object's data. The data is said to be encapsulated because the only way to access it is through one of these surrounding actions. The encapsulated internal characteristics of an object are its variables. Variables are associated with an object and exist for the lifetime of that object:

Figure 4-4. Encapsulated 5 Object

How Objects Interact

The actions defined by an object are its only interface to other objects. Actions form a kind of "wall" that encapsulates the object, and shields its internal information from outside objects. This shielding is called information hiding. Information hiding protects an object's data from corruption by outside objects, and also protects outside objects from relying on another object's private data, which can change without warning.

One object can act upon another (or cause it to act) only by calling that object's actions. Actions are invoked by sending messages. messages. Objects respond to these messages by invoking methods that perform an action, return data, or both. A message to an object must specify:

So the message format looks like this:

object~action(parameters)

Assume that the object is the string !iH. Sending it a message to use its REVERSE action:

"!iH"~reverse

returns the string object Hi!.

Methods

Sending a message to an object results in performing some action; that is, it results in running some underlying code. The action-generating code is called a method. When you send a message to an object, you specify its method name in the message. Method names are character strings like REVERSE. In the preceding example, sending the reverse message to the !iH object causes it to run the REVERSE method. Most objects are capable of more than one action, and so have a number of available methods.

The classes Rexx provides include their own predefined methods. The Message class, for example, has the COMPLETED, INIT, NOTIFY, RESULT, SEND, and START methods. When you create your own classes, you can write new methods for them in Rexx code. Much of the object programming in Rexx is writing the code for the methods you create.

Polymorphism

Rexx lets you send the same message to objects that are different:

"!iH"~reverse   /* Reverses the characters "!iH" to form "Hi!"  */
pen~reverse     /* Reverses the direction of a plotter pen      */
ball~reverse    /* Reverses the direction of a moving ball      */

As long as each object has its own REVERSE method, REVERSE runs even if the programming implementation is different for each object. This ability to hide different functions behind a common interface is called polymorphism. As a result of information hiding, each object in the previous example knows only its own version of REVERSE. And even though the objects are different, each reverses itself as dictated by its own code.

Although the !iH object's REVERSE code is different from the plotter pen's, the method name can be the same because Rexx keeps track of the methods each object owns. The ability to reuse the same method name so that one message can initiate more than one function is another feature of polymorphism. You do not need to have several message names like REVERSE_STRING, REVERSE_PEN, REVERSE_BALL. This keeps method-naming schemes simple and makes complex programs easy to follow and modify.

The ability to hide the various implementations of a method while leaving the interface the same illustrates polymorphism at its lowest level. On a higher level, polymorphism permits extensive code reuse.

Polymorpism involves a form of contract between two objects. One object will send a message to another object expecting a particular result. Different types of objects can implement different versions of this message as long as it fulfills the expectations of the the invoking object. For example, the LOOP instruction has a form called OVER. Loop OVER will iterate over a number of elements provided by an object. For example,

myarray = .array~of("Rick", "David", "Mark")
loop name over myarray
   say name
end

Will display the strings "Rick", "David", and "Mark", in that sequence. The LOOP OVER instruction will work with Arrays, Lists, stem variables, streams, etc. The LOOP instruction itself does not know anything about Arrays or Lists or stems or streams. The LOOP instruction specifies a contract. LOOP will send the target object the message MAKEARRAY and expects the target object to return an Array object that is used for the LOOP iteration. Any object can participate in LOOP iteration by implementing this contract. Objects that do implement the MAKEARRAY contract are polymorphic with the LOOP instruction.

Classes and Instances

In Rexx, objects are organized into classes. Classes are like templates; they define the methods and variables that a group of similar objects have in common and store them in one place.

If you write a program to manipulate some screen icons, for example, you might create an Icon class. All Icon objects will share the actions and characteristics defined by the class:

Figure 4-5. A Simple Class

All the icon objects will use common methods like DRAW or ERASE. They might will common variables like position, color, or size. What makes each icon object different from one another is the data assigned to its variables. For the Windows System icon, it might be position="20,20" while for the Shredder it is "20,30" and for Information it is "20,40"

Figure 4-6. Icon Class

Objects that belong to a class are called instances of that class. As instances of the Icon class, the Windows System icon, Shredder icon, and Information icon acquire the methods and variables of the class. Instances behave as if they each had their own methods and variables of the same name. All instances, however, have their own unique properties—the data associated with the variables. Everything else can be stored at the class level.

Figure 4-7. Instances of the Icon Class

If you must update or change a particular method, you only have to change it at one place, at the class level. This single update is then acquired by every new instance that uses the method.

A class that can create instances of an object is called an object class. The Icon class is an object class you can use to create other objects with similar properties, such as an application icon or a drives icon.

An object class is like a factory for producing instances of the objects.

Data Abstraction

The ability to create new, high-level data types and organize them into a meaningful class structure is called data abstraction. Data abstraction is at the core of object-oriented programming. Once you model objects with real-world properties from the basic data types, you can continue creating, assembling, and combining them into increasingly complex objects. Then you can use these objects as if they were part of the original programming language.

Subclasses, Superclasses, and Inheritance

When you write your first object-oriented program, you do not have to begin your real-world modeling from scratch. Rexx provides predefined classes and methods. From there you can create additional classes of your own, according to your needs.

Rexx classes are hierarchical. The original class is called a base class or a superclass. The derived class is called a subclass. Subclasses inherit methods and data from one or more superclasses. A subclass can introduce new methods and data, and can override methods from the superclass.

Figure 4-8. Superclass and Subclasses

You can add a class to an existing superclass. For example, you might add the Icon class to the Screen-Object superclass:

Figure 4-9. The Screen-Object Superclass

In this way, the subclass inherits additional methods from the superclass. A class can have more than one superclass, for example, subclass Bitmap might have the superclasses Screen-Object and Art-Object. Acquiring methods and variables from more than one superclass is known as multiple inheritance:

Figure 4-10. Multiple Inheritance