Object-oriented PL/I 
Author Message
 Object-oriented PL/I

  I've received several requests to post Peter Elderon's OO-PL/I GUIDE
presentation, so here it is. Peter, I'm sure, would like to have feedback
from this group as to the questions he poses in the paper. Enjoy.
------------------------------------------------------------------------

  INTRODUCTION
  ____________

  A class consist of objects and the operations (also known as methods) that
  may be performed on such objects.  Classes offer the following features:

  o   data encapsulation

      Classes "hide" the data that represent an object (also commonly known as
      an "instance") of a class and expose only the methods that can be applied
      to it.

  o   inheritance

      Classes also allow new, more specialized, classes to be developed from
      existing classes.

  o   polymorphism

      When you define a subclass, you may also override the methods defined in
      your parent classes.  Then when applying a method to an object of a class
      (or one of its subclasses), which actual method is invoked will depend on
      the actual class to which the object belongs and whether that class over-
      rode the parent class's method.

  In some object-oriented languages, the cost of method invocation is kept to a
  minimum but at the expense of what is sometimes called "fragile super-
  classes":  changes to a class require all users and subclasses of that class
  to be recompiled.

  In PL/I, class users and subclasses need not be recompiled if

  1.  new methods are added to a class

  2.  new instance variables are added to a class

  This is what you would expect from a math library, for instance.

  PL/I also clearly separates the client's view of a class from the implement-
  er's by defining a class via:

  1.  a statement that defines the client's view:

      o   the class's parentage

      o   the operations that may be performed on its objects (these operations
          are also known as "instance methods")

  2.  a package that defines the implementation:

      o   what parent class methods have been overridden

      o   the implementation of its instance methods

      o   the implementation of the overridden methods

      o   its "private" instance data

  THE CLIENT'S VIEW OF A CLASS
  ____________________________

  The statement that defines the client's view of a class is the DEFINE CLASS
  statement, which would typically be in an include file.  It has the syntax:

      DEFINE
        CLASS
          <class-name>
          INHERITS ( <parent-class-name> )
          METHODS ( <declaration-commalist> ) ;

  The INHERITS clause names this class's parent.  Every PL/I class is a sub-
  class of the PL/I-provided Object class (except, of course, the Object class
  itself).  Note also that a class may have only one parent; like Smalltalk and
  Java, object-oriented PL/I supports only single inheritance.

  The METHODS clause names the operations permitted on objects of this class.
  These methods are the only way a client can manipulate objects of this class.
  There is no public or protected data: all data is encapsulated by methods.

  For example, the following statement defines a "student" class

      define
        class
          student
          inherits( object )
          methods
            (
              setUp entry( char(64) varying,
                            char(6) )

             ,get_Id entry() returns( char(6) )

             ,get_Name entry() returns( char(64) varying )

             ,print entry()
            );

  CLASS INSTANCES
  _______________

  The DEFINE CLASS statement shares some important similarities with the DEFINE
  STRUCTURE statement.

  The name appearing in the specification of a HANDLE attribute may be the name
  specified in a DEFINE STRUCTURE statement or in a DEFINE CLASS statement.  A
  variable declared as a HANDLE for a class is also known as an instance of
  that class.

  Just as the only access to the elements in a defined structure is via the =>
  operator applied to a handle to that structure, the only access to the
  methods in a defined class is is via the => operator applied to a handle to
  that class.

  For instance, given the student class in the example above, the following
  statements declare an instance of that class and invoke the setup method for
  that class.

       dcl  s  handle student;

       call s=>setUp( 'Z. Dobson', '030507' );

  The example above is incomplete since before you apply a method to an object,
  the object must be instantiated.  The primary way to instantiate an object is
  via the NEW type function applied to the name of that class.  So the example
  above could be completed to look like:

       dcl  s  handle student;

       s = new(: student :);
       call s=>setUp( 'Z. Dobson', '030507' );

  So just as for defined structures, storage for defined classes is obtained
  via the new type function.

  If an instance of a class is obtained via the new function, it should be
  destroyed by applying to it the delete method from the PL/I object class.  So
  the student instance obtained above could be destroyed via

       call s=>delete();

  THE IMPLEMENTER'S VIEW OF A CLASS
  _________________________________

  A PL/I package implements a class by specifying the name of the class in the
  OWNS clause of the PACKAGE statement.  For instance, the following statement
  indicates that this package implements the student class:

      student:
        package
        owns( student );

  A package that owns a class must contain a DEFINE CLASS statement for that
  class.

  A package that owns a class must also define any private data for a class via
  the DEFINE INSTANCE statement:

      DEFINE
        INSTANCE
          <class-name>
          ( <declaration-commalist> ) ;

  The DEFINE INSTANCE statement is valid only inside a package that owns the
  class whose data that it defines.

  The declaration commalist in the define instance statement must specify must
  specify either a scalar or a structure or an array thereof.

  QUESTION: should a list of scalars and/or structures be allowed??

  A package that owns a class must implement all the methods in that class by
  containing a level-one procedure for each method defined by the class.  Note
  that these procedures are not exported from the package; they cannot be
  invoked from outside or inside the package except via a handle.

  As indicated earlier, PL/I allows new methods and variables to be added to a
  parent class without "damaging" any of is subclasses or clients.  This
  implies that classes must be created at run-time.  The compiler generates all
  the code to do this.  The actual creation of the class occurs in the PL/I
  package that owns the class.

  The code generated by the compiler produces several external names built
  using the name of the class:

  o   <class-name>NewClass

  o   <class-name>ClassData

  o   <class-name>CClassData

      This name is generated only if the PL/I object class is the SOM object
      class

  Each method implemented by a class receives one undeclared byvalue parameter,
  which can be referenced by the built-in function SELF.  SELF is a handle for
  the class being implemented and can be used to access instance data as well
  as to invoke methods.  For instance, the following code implements the
  student class:

      student:
        package
        owns( student );

        %include class;
        %include student;

        dcl self builtin;

        define
          instance
            student
             ( 1 student_data
                ,2 student_id      char(6)
                ,2 student_name    char(64) varying
             )
          ;

        setUp: proc( name, id );

          dcl name char(64) varying;
          dcl id   char(6);

          self=>student_id = id;
          self=>student_name = name;
        end;

        get_Id: proc returns( char(6) );

          return( self=>student_id );
        end;

        get_Name: proc returns( char(64) varying );

          return( self=>student_name );
        end;

        print: proc;

          put skip edit( ' id', ': ',
                         self=>student_id ) (a(12),a,a );
          put skip edit( ' name', ': ',
                         self=>student_name ) (a(12),a,a );
        end;

      end student;

  A package that owns a class may also override methods in its parents'
  classes.  The DEFINE OVERRIDES statement indicates which parent methods a
  class will override:

      DEFINE
        OVERRIDES
          <class-name>
          ( <method-name-commalist> ) ;

  The DEFINE OVERRIDES statement is valid only inside a package that owns the
  class named in it.  This package must also contain a level-one procedure for
  each method overridden by the class.

  A method may often want to override a parent method in order to perform some
  special processing before or after the normal processing performed by the
  parent method.  In order to be able to do this, it must be possible to
  specify that the method that should be applied to self is that defined by its
  parent.  The PARENT built-in function provides this ability.

  For instance, if a subclass of the student class overrode the student print
  method because it wanted to print additional data belonging to the subclass,
  the first thing the subclass print method might want to do is invoke the
  student print method as in the following example:

       print: proc;
         call parent=>print();
         /* use self to print additional data */
       end;

  THE PL/I OBJECT CLASS
  _____________________

  Every class has the object class as its root class.  The object class defines
  a set of common methods that can be applied to all class instances.

  The object class define statement is

          define
            class
              object
              methods
                (
                   delete entry()
                   /* used to destroy an instance of a class   */
                   /* it will invoke the uninit method         */

                ,  init entry()
                   /* invoked when an instance is created      */
                   /* the class object version does nothing    */
                   /* but exist in order to be overridden      */

                ,  uninit entry()
                   /* invoked when an instance is destroyed    */
                   /* the class object version does nothing    */
                   /* but exist in order to be overridden      */

                );

  CONFORMANCE
  ___________

  Only a handle may be assigned only to another handle But the source in such
  an assignment should be a handle of the target handle's class or one of its
  subclasses

  For example, given the student class with two derived classes, undergrad and
  graduate, consider

           dcl s handle student;
           dcl u handle undergrad;
           dcl g handle graduate;

           ...
           s = u;  /* valid */
           u = s;  /* valid */

           ...
           s = g;  /* valid */
           u = s;  /* invalid */

  A new condition CONFORMANCE, which can be disabled or enabled like
  SUBSCRIPTRANGE, will be raised when enabled if such an invalid assignment is
  attempted.

  CONSTRUCTORS
  ____________

  QUESTION: Should constructors be supported??

  In examples used above, the student class has a method, setUp, that can be
  used to initialize its instance data.  However, the student class could be
  defined with a constructor that would be invoked to perform this initializa-
  tion.

  If a method list for a class contains a method with the same name as the
  class name, that method is called a constructor.  The constructor will be
  invoked when the new function is invoked, and if the constructor requires
  parameters, the new function must be invoked with the necessary additional
  arguments.  For instance, the student class above could be defined via

      define
        class
          student
          inherits( object )
          methods
            (
              student entry( char(64) varying,
                             char(6) )

             ,get_Id entry() returns( char(6) )

             ,get_Name entry() returns( char(64) varying )

             ,print entry()
            );

  The student class would then be instantiated via

      dcl s handle student;
      s = new(: student, 'Z. Dobson', '030507' :);

  Constructor methods cannot be invoked directly except a constructor method
  itself may invoke via the parent builtin the constructor for its parent
  class.

  AN EXAMPLE WITH INHERITANCE AND POLYMORPHISM
  ____________________________________________

  Using the student class above, two different subclasses could be defined for
  it via the statements:

      define
        class
          undergrad
          inherits( student )
          methods
            (
              setUp_Undergrad entry( char(64) varying,
                                     char(6),
                                     char(8) )
            );

      define
        class
          graduate
          inherits( student )
          methods
            (
              setUp_Grad entry( char(64) varying,
                                char(6),
                                char(8) )
            );

  The implementation of the undergrad class could be

      undergrad: package owns( undergrad );

        %include class;
        %include student;
        %include undergrad;

        dcl self builtin;
        dcl parent builtin;

        define
          instance
            undergrad
             (
                underGradDate char(8)
             )
          ;

        define
          overrides
            undergrad
             (
                print
             )
          ;

        setUp_Undergrad: proc( name, id, graddate );

          dcl name char(64) varying;
          dcl id   char(6);
          dcl gradDate char(8);

          call self=>setup( name, id );
          self=>UnderGradDate = graddate;
        end;

        print: proc;

          call parent=>print;
          put skip edit( ' grad date', ': ', self=>undergradDate )
                       ( a(12), a, a );
        end;

      end undergrad;

  While the implementation of the graduate class could be

      graduate: package owns(graduate);

        %include class;
        %include student;
        %include graduate;

        dcl self builtin;
        dcl parent builtin;

        define
          instance
            graduate
             (
                gradDegree char(8)
             )
          ;

        define
          overrides
            graduate
             (
                print
             )
          ;

        setup_Grad: proc( name, id, degree );

          dcl name char(64) varying;
          dcl id   char(6);
          dcl degree char(8);

          call self=>setup( name, id );
          self=>GradDegree = degree;
        end;

        print: proc;

          call parent=>print;
          put skip edit( ' degree', ': ', self=>gradDegree )
                       ( a(12), a, a );
        end;

      end graduate;

  The following code is client code using these classes.  It obtains an
  instance of the undergrad class, initializes it and saves the handle for that
  instance in an array of student handles.  It then obtains an instance of the
  graduate class, initializes it and saves the handle for that instance in the
  same array of student handles.  Finally it loops through that array and
  prints the data for each student. The print function is polymorphic since the
  two subclasses have each overridden it: when applied to a student handle it

  will call the print method for the undergrad or graduate class in accordance
  with the class to which the handle actually points.

  So the output of the following program would be, thanks to polymorphism:

         id          : 030507
         name        : Z. Dobson
         grad data   : 19550611

         id          : 111317
         name        : L. Jim
         degree      : Ph. D.

      registrar: proc;

        %include class;
        %include student;
        %include undergrad;
        %include graduate;

        dcl s(20) handle student;
        dcl u     handle undergrad;
        dcl g     handle graduate;
        dcl c     fixed bin(31) init(0);
        dcl jx    fixed bin(31);

        /* add a sample undergrad student */
        c = c + 1;
        u = new(: undergrad :);
        call u=>setUp_Undergrad( 'Z. Dobson', '030507', '19550611' );
        s(c) = u;

        /* add a sample graduate student */
        c = c + 1;
        g = new(: graduate :);
        call g=>setUp_Graduate( 'L. Jim', '111317', 'Ph. D.' );
        s(c) = g;

        /* print out all the students */
        do jx = 1 to c;
          call s(jx)=>print();
          put skip;
        end;
      end;
----------------------------------------------------------------------
Dave Jones



Mon, 15 Nov 1999 03:00:00 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. ANNOUNCEMENT: Object-Oriented Systems - new object-oriented journal

2. ANNOUNCEMENT: Object-Oriented Systems - new object-oriented journal

3. OBJECT-ORIENTED PROGRAMMING IN PL/I -- R. Smedley

4. object oriented programming in pl/i

5. OBJECT-ORIENTED PROGRAMMING IN PL/I -- R. Smedley

6. Compare object-oriented and function-oriented ....

7. Action-oriented vs Object-oriented

8. Object Structures - Building Object-Oriented Software Components with Eiffel- By Jacob Gore

9. The use of the Factory Paragraph versus Class-Object in Object Oriented (OO) COBOL

10. 3rd Conference on Object-Oriented Technologies and Systems (COOTS) - Student Grant Application

11. Is Visual Basic Object Oriented?

12. object-oriented vs. functional

 

 
Powered by phpBB® Forum Software