PreviousObjects and Messages Tutorial (Windows & OS/2) Inheritance Tutorial (Windows & OS/2)Next"

Chapter 6: Writing a Class Program Tutorial (Windows & OS/2)

In the Objects and Messages Tutorial you learned how to create instances of a class and send messages. In this chapter you will learn how to write classes for objects of your own.

The class you will create is called BeepWindow, which is a subclass of Window. This means that it inherits all the basic behavior included in the class library Window class.

An instance of BeepWindow is a window with a single pushbutton. When you push the button, it sounds a beep.

The first part of the discussion looks at the way a class program is built up, using relatively little syntax which is different from ANSI 85 COBOL. It is followed by sections in which you animate through code which progressively builds up the BeepWindow class, starting from an empty window with no behavior to the full BeepWindow example with a button and behavior to process user-events.

This tutorial consists of the following sessions:

  1. Structure of a Class Program

  2. Creating a Window

  3. Adding a Button

  4. Adding Event Processing

Time to complete: 45 minutes.

6.1 Structure of a Class

This tutorial starts with a look at the overall structure of a class program, using the Class Browser to examine the structure of BeepWindow.

>>To start the Browser for BeepWindow

  1. Start the icon labeled Writing a Class in the COBOL Workbench Tutorials group.

    This starts the on-line tutorial Writing a Class.

  2. Select the hotspot labeled Browse BeepWindow (you will need to double-click or press Enter on OS/2), below the heading Using This Tutorial.

    This starts the Browser and loads the BeepWindow class.

The class program consists of a set of nested programs. Nesting programs is a concept which was introduced to COBOL in the ANSI85 standard.

The sections below examine the following elements of the class program:

6.1.1 Identifying a Class

Each class program starts with a class-id identifier and finishes with an end class clause. These bracket the outermost level of the nesting. The BeepWindow class looks like this:

 class-id. BeepWindow 
            data is protected
            inherits from Window.
 ... 
 end class BeepWindow.

The inherits from phrase identifies BeepWindow's superclass, Window. The data is protected phrase enables any subclasses of BeepWindow to inherit BeepWindow data. If this clause is omitted, or replaced by data is private, subclasses of BeepWindow cannot access inherited data directly. Inheritance of data is explained in more detail in the Inheritance Tutorial.

>>To see this code

  1. Select BeepWindow in the Browser class/program selector.

  2. Select BeepWindow in the method/section selector.

    This displays the class-id code in the text edit pane.

  3. Select end class BeepWindow in the method/section selector.

    This displays the end class code in the text edit pane.

6.1.2 Identifying Classes Used by a Program

The class-controlw paragraph identifies the executable code files which implement classes used by the program. The superclass, the class itself, and every class which will be invoked from the class must be identified in class-control.

>>To see the class-control paragraph for BeepWindow

The class-control paragraph looks like this:

 class-control.
     Window is class "window"
     BeepWindow is class "beepwin1"
     ...
     .

The class clause serves two purposes:

6.1.3 Declaring Class Program Data

All the data in the data division of the class program is class program data. You can use all the same sections in a class program data division that you can in any ANSI '85 COBOL program.

All class program data can be accessed from the Procedure Division of the class program and from all class and instance methods.

>>To see the class data declarations paragraph for BeepWindow

BeepWindow uses class Working-Storage to store initialization data for instances of BeepWindow. This is quite a common use for class Working-Storage.


Note: This use of Working-Storage is a Micro Focus feature, and not currently part of the proposed ANSI standard for OO COBOL.

6.1.4 The Class Object Program

The class object program defines the data and methods for the class object. It is nested within the class program, immediately following the class program data division (if there is one). It looks like this:

   class-object.
   object-storage section. 
  * class data .  
   ...
  * class methods
   end class-object.

The Object-Storage Section defines the class object data. Class object data is not the same as class program data.

The class object data can only be accessed from the class methods. It can also be inherited for direct access by subclasses (this depends on the contents of the Class-Id paragraph).

6.1.5 Writing a Class Method

Each class method is a nested program. The code below shows an outline for a "new" method for BeepWindow.

 method-id. "new". 
 data division.
* any data required by the method. 
  ...
 linkage section. 
 01 aBeepWindow            object reference.
 01 aParent                object reference.
 
 procedure division using aParent returning aBeepWindow.
* code to create and initialize a BeepWindow object.
 exit method.
 end method "new".


Note: BeepWindow does not actually define any class methods; it inherits its implementation of the "new" method from its superclass. This section is included to illustrate how you create a class method.

As with the class program itself, you can declare different types of data in the Data Division of the method. The DATA DIVISION header itself is optional. Data declared here is only accessible to the code in this method. The data division can contain any of the following sections:

The Procedure Division contains the code for the method. You terminate processing of the method with an EXIT METHOD statement. This returns processing to the program which invoked the method.

6.1.6 The Object Program

The object program defines the data and methods for instances of the class. It is nested within the class program. It looks like this:

   object.
   object-storage section. 
  * instance data for the object.  
   ...
  * Instance methods
   end object.

The only Data Division section that has any meaning in an object program is the Object-Storage Section. You can create other data sections, but the run-time behavior if you try to access the data in these sections is undefined.

Any data you declare in the Object-Storage Section is accessible to all the instance methods, and may be inherited by instances of subclasses of the class.

There is no Procedure Division in an object program. You write the initialization code for your object as a method. BeepWindow uses method "initialize". Initialize is invoked by the "new" method which creates BeepWindow objects.

>>To see the object program and data declarations paragraph for BeepWindow

6.1.7 Instance Methods

Instance methods are nested programs of the object program. The code below is an outline of the "initialize" method of beepwin1.cbl.

 method-id. "initialize".
 working-storage section.
...
 linkage section.
    01 lnkParent                      object reference.
 procedure division using lnkParent.
 ...
 exit method.
 end method "initialize".

Instance methods are nested inside the object program. Writing an instance method is exactly like writing a class method, with the only difference being the scope of data which the instance method can access.

The instance method can access data:

>>To see the "initialize" method for BeepWindow

The code below summarizes the structure of an Object COBOL class, and recaps the material covered so far in this tutorial.

class-id. BeepWindow inherits  from Window.
                               *> Identification and inheritance

 class-control.                *> Class control paragraph
                               *> names the files 
 Beepwindow is class "beepwin" *> containing the   
 Window is class "window"      *> executables for each
                               *> class.
 .                             *> Period terminates paragraph.

 data division.                *> Data division header is 
                               *> optional. 
 ...
 working-storage section.      
 ...
 procedure division.           *> procedure division is 
                               *> optional. You can  
                               *> use it for class 
                               *> initialization.  
 exit program.                 *> Terminates procedure division
                               *> division. 

     class-object              *> Defines the start of the class
                               *> object. 
     object-storage section.   *> Defines class object data
     ...

     ...
 
         method-id. "new".     *> Start of class method "new". 
         ...
         end method "new".     *> End of class method "new".
    end class-object.          *> End of the class object

    object.                    *> Start of the code 
                               *> defining behavior 
                               *> of instances  
                               *> of the class. 
    object-storage section.    *> Defines instance data. 
     ...
         method-id. "sayHello".*> Start of instance 
                               *> method "sayHello"
         ...
         end method "sayHello".*> End of instance method. 
    end object.                *> End of code for 
                               *> instances.  

 end class BeepWindow. 

This completes the summary of class structure. In the next section you will animate through the code which creates a window on your desktop.

6.2 Creating a Window

This section takes you through the code which creates a BeepWindow and displays it on your desktop. You will use Animator V2 to step through the code so that you can see exactly what it does.

>>To see the code to create a window

  1. Select Check from the COBOL menu.

    The Browser starts the COBOL Checker to create executable code.

  2. Once checking is complete, select Animate from the COBOL menu.

    There is a pause while Animator V2 starts and loads in BeepWindow. Once Animator V2 has started, the line below tag B005 appears highlighted and ready for execution.

    This line (invoke EventManager...) is inside the class program Procedure Division. The Procedure Division is generally used to hold any class initialization code. Most classes do not require any special initialization, so they have no class Procedure Division.

    In beepwin1.cbl we are using the Procedure Division to create and initialize an instance of the EventManager (see below), and to create an instance of the BeepWindow class. In a real application, this code would probably be found in the object or program responsible for starting and controlling the user-interface. It is bundled into the Procedure Division here so that we only need a single program to display a BeepWindow instance.

  3. Step the line of code.

    This creates an instance of the EventManager class. EventManager is the class responsible for handling all GUI events.

  4. Step the lines below tag B006.

    These lines send the following messages to the EventManager instance:

    >
    "autoCoordinates" Setting auto coordinates ensures that the window sizes and positions you set are portable across environments.
    "baseLinePositioning" Base line positioning helps you to line up text labels with fields. BeepWindow does not actually use any fields, but this code makes it easier if you want to add some later.
    "initialize" You must initialize the EventManager instance before starting to create GUI objects, but after setting "autoCoordinates" or "baseLinePositioning".

  5. Step the line after tag B007 (invoke self "new"...).

    This sends the "new" message to self. Sending a message to self invokes a method inside the current object; in this case the BeepWindow class. Self is an object reference which always points to the object in which it is found. You don't need to declare self; it is created automatically for you.

    BeepWindow doesn't have its own "new" method. It is inherited from Window (part of the supplied class library). Because the class library classes are all compiled to .gnt code you don't see the "new" method executing. The "new" method of Window creates an instance, and then sends it the "initialize" message.

    BeepWindow does have an "initialize" instance method, so when "new" sends the message "initialize", this is where the execution transfers.

    Control is now below tag B011.

  6. Step the line below tag B011 (invoke super "initialize"...).

    This sends the message "initialize" to this object, but tells the OO RTS to start looking for the method in the superclass code (which is in the Window class). Instances of Window have initialization code which must be executed. Although this object is an instance of BeepWindow, it inherits all the behavior of instances of Window, so it must execute all the initialization code for a Window instance.

    The parameter oo-desktop is also passed to the "initialize" method. oo-desktop is an external data item initialized by the EventManager which refers to the Windows or OS/2 desktop. It is being passed to the Window instance as a parent.

    All graphical objects must have a parent, which is usually either the Windows or OS/2 desktop, or another window. Don't confuse the parent of a graphical object with its superclass. An object inherits its behavior from its superclass. The parent of a graphical object owns that object at run-time. If the parent is destroyed, all its child objects are destroyed with it.

  7. Step the lines below tag B012.

    The CharacterArray class is part of the class library. Instances of this class are objects which contain strings. The message "withLengthValue" creates an instance of CharacterArray containing a string of the specified length and value. You pass this instance as a parameter to the "setTitle" method.

    This sets the window title. The "setTitle" method is inherited from the Window class.

  8. Step the line below tag B013.

    This destroys the object which we have now finished with, deallocating its memory and freeing up system resources.

  9. Step the lines below tag B014.

    These initialize parameters to set the window size and position. The dimensions are nominally in character sizes. This window will be approximately 35 x 8 text characters in size.

    This method of calculating position and size has the advantage of being easily portable across environments; the COBOL GUI mechanisms are responsible for identifying the environment and calculating the approximate character size.

    The statement invoke self "setRectangle"... sets the size and position of the window. This is another inherited method.

  10. Step the exit method statement.

    This returns control to the Procedure Division of BeepWindow.

  11. Step the line invoke aWindow "create".

    The processing so far has created and initialized an Object COBOL BeepWindow object, held in object reference aWindow. When you send it the "create" message, it creates the GUI object that will display on your desktop.

    As a programmer your communication with the GUI object, which is the one you see and interact with on the desktop, is through the intermediary of the Object COBOL object. Most of the time you can treat the two as synonymous; the only reason you need to be aware of the split is to remember to send the "create" message to instantiate the GUI object.

  12. Step the line invoke aWindow "show".

    This displays the BeepWindow on the desktop. You can resize and move it, and also drop down the system menu. However, we have not yet added the application code to process events.

    This includes the Close event. If you select Close from the System menu, nothing happens. You will learn how to add processing for events later in this tutorial.

  13. Select Exit from File on Animator V2 to quit. This also closes the Beep window.

    This concludes this session on creating a window. In the next session you will add a single button to this window.

6.2.1 Adding a Button

In this section we are going to add some code to the BeepWindow class to add a button to it. The new code is in beepwin2.cbl.

>>To load beepwin2.cbl into the Class Browser

  1. Select BeepWindow in the class/program selector.

  2. Select Remove from the File menu.

  3. Select Open from File and load beepwin2.cbl from the Open File dialog box.

If you select BEEPWIN2.CBL in the file selector and BeepWindow in the method/section selector, you will see that the class clause for BeepWindow in the class-control paragraph now refers to beepwin2. You will now animate through the new code.

>>To animate the new code

  1. Select Check from the COBOL menu.

    The Browser starts the COBOL Checker to create executable code.

  2. Once checking is complete, select Animate from the COBOL menu.

    The Class Browser displays a dialog box asking you if you want to save changes. Although you have not made any changes to the source code, by loading a different program you have changed the Browser's project file.

  3. Push the No button so that the project file stays in its original state.

    There is a pause while Animator V2 starts and loads in BeepWindow. Once Animator V2 has started, the line below tag B005 appears highlighted and ready for execution.

  4. Move the text cursor to the line below tag B007 (invoke self "new").

  5. Select Run to cursor from Execute.

    We are not going to step through any of the code you executed in the previous section.

  6. Step the invoke statement.

    This takes Animator V2 to the Procedure Division of the "initialize" method. The line perform windowInitialize is highlighted ready for execution. In this version of BeepWindow, the "initialize" method has been split into separate sections for the window initialization code and the add button code, to make it easier to read.

  7. Select Run through from Execute.

    This executes all the window initialization code.

  8. Step the line perform buttonAdd.

    Execution passes to the line below tag B020.

  9. Step the line invoke PushButton "new"....

    This creates an instance of the PushButton class, with self (which is this instance of BeepWindow) as a parent. This means that the PushButton is positioned relative to this BeepWindow, and is displayed when BeepWindow is visible. It will also be destroyed when the BeepWindow is destroyed.

  10. Step the lines below tag B021.

    These label the button.

  11. Step the line below tag B022.

    This finalizes the string object which is no longer needed.

  12. Step the lines below tag B023.

    This positions the button in the lower left-hand corner of its parent window. Coordinates are always relative to the top left-hand corner of the parent.

  13. Step up to and including the exit method from the "initialize" method.

    Animator V2 returns to the line invoke aWindow "create".

  14. Step this line and the one below.

    When you send the "create" message to an Object COBOL GUI object, it is automatically passed on to all the object's children - in this case the pushbutton.

    The "show" message displays the window and the pushbutton. You can push the button, but there is no code to translate the event into action.

  15. Select Exit from the File menu to quit Animator V2 and close down the Beeper window.

You have now added a pushbutton to the window. In the next session, you will add behavior to the window so that it can process user events.

6.2.2 Adding Event Processing

So far you have created a window, and added a pushbutton to it. You can move and resize the window, and you can push the button. However, neither object responds to any event which requires processing by the application.

Every time the end-user of a GUI application does something (for example, moves the mouse, presses a key, pushes a button), the operating system sends an event to the application. The event tells the application that something has happened.

To make an application respond to events, you need to register the events you are interested in, and specify a piece of code to be executed when that event occurs. To register an event against an object, you send it the "setEventTo" message, providing the following parameters:

Events are represented by numeric codes. There is a complete list of the possible events for objects and their codes in the copyfile p2cevent.cpy. This is the simplest type of event processing. You can handle events in a slightly different way, by translating the physical events which occur at the interface to the logical events which are expected by the controlling application. The tutorial GUI Programming demonstrates this type of event handling.

Once you have registered all the events with the different GUI objects in your application, you can start event processing. This is handled for you by an instance of the EventManager class. Once you send the EventManager the "run" message, it will poll for events. When one of the events you have registered occurs, it invokes the method registered for that event.

You stop EventManager event polling by sending it the "terminate" message. This also closes down all the GUI objects created by your application.

You can see how this works by animating the third version of the BeepWindow class. The code is in beepwin3.cbl. Follow the steps below:

>>To animate the BeepWindow

  1. Select BeepWindow in the class/program selector.

  2. Select Remove from the File menu.

  3. Select Open from File and load beepwin3.cbl from the Open File dialog box.

  4. Select Check from the COBOL menu.

    The Browser starts the COBOL Checker to create executable code.

  5. Once checking is complete, select Animate from the COBOL menu.

    The Class Browser displays a dialog box asking you if you want to save changes. Although you have not made any changes to the source code, by loading a different program you have changed the Browser's project file.

  6. Push the No button so that the project file stays in its original state.

    There is a pause while Animator V2 starts and loads in BeepWindow. Once Animator V2 has started, the line below tag B005 appears highlighted and ready for execution.

  7. Move the text cursor to the line below tag B007 (invoke self "new"...).

  8. Select Run to cursor from the Execute menu.

    We are not going to step through any of the code you executed in the previous sections.

  9. Step the invoke statement.

    Execution moves to the "initialize" method.

  10. Move the cursor to the line perform registerEvents.

  11. Select Run to cursor from Execute.

  12. Step perform registerEvents.

    Execution moves to the line below tag B030 (move p2ce-close...).

  13. Step the next two lines (up until tag B031).

    p2ce-close is a level 78 data item from copyfile p2cevent.cpy. It contains the event code for the close event. The close event is sent to a window object when the end-user selects Close from the System menu on the window, or double-clicks on the System menu icon (the icon at the top left-hand corner of the window).

    The second line (invoke self "setEventTo"...) registers the close event on self against method "closeWindow". Since we are in the code for a BeepWindow instance, self is the window we are creating. The method "closeWindow " is also a method of self. The space before the end quote is there so that the "setEventTo" method can find the end of the string.

  14. Step the two lines below tag B031.

    This registers the clicked event against the button object. When the end-user clicks the button, the message "beepButtonPushed" is sent to self (the window).

  15. Step the exit method statement.

  16. Step the lines up until tag B008.

    The window appears on the desktop.

  17. Step the line invoke EventManager "run".

    Control now passes to the EventManager, which waits for events from the operating system. Because the EventManager is .gnt code, like the rest of the class library, you can't see it executing in Animator V2. You can't use any Animator V2 functions until EventManager relinquishes control.

  18. Push the Beep pushbutton on the Beeper window (the window may have disappeared behind the Animator window).

    This causes the clicked event on the pushbutton. The EventManager sends the clicked event to the pushbutton. The pushbutton in turn invokes method "beepButtonPushed" in BeepWindow (as set up in step 13 above).

    The execution point in Animator V2 changes to the line below tag B050 inside method "beepButtonPushed" (invoke alarm "custom").

  19. Step the invoke statement.

    This sounds the system bell. Alarm is a class library class for controlling the system bell. Because there is only one bell, you can't create instances of Alarm - all methods are implemented by the class itself.

  20. Step the exit method statement.

    Control returns to the EventManager.

  21. On the Beep window, select Close from the System menu.

    This causes the close event on the window. The EventManager invokes method "closeWindow" in BeepWindow (as set up in step 12 above). The execution point changes to the line below tag B040 (invoke anEventManager "terminate").

  22. Step the line of code.

    This tells the EventManager to stop processing events. It also destroys all the GUI objects in the application, so the Beep window disappears.

  23. You can now step through the exit method statements until Animator V2 displays the "STOP RUN encountered..." message on the status line.

  24. Select Exit from the File menu on Animator V2.

  25. Select Exit from the File menu on Browser.

    Push No when asked whether you want to save changes.

  26. Select Exit from the on-line help File menu to close down the on-line tutorial.

6.3 Summary

This concludes this tutorial on writing a class program. In this tutorial you learned:

In a later tutorial you will learn a more sophisticated form of event processing which enables you to separate more thoroughly an interface from business logic.


Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

PreviousObjects and Messages Tutorial (Windows & OS/2) Inheritance Tutorial (Windows & OS/2)Next"