|Powered by QM on a Rpi server|
KnowledgeBase 00031: Object Oriented Programming
This article was originally published as a Tip of the Week.
What's it all about?
Object oriented programming is not a replacement for the environment that multivalue developers have used for many years. Rather, it is an additional tool in the developer's toolbox that can simplify some development tasks.
Consider an application that needs to make a network connection to another system, hold a conversation over this connection, and then close it. Traditionally, we might develop this as a subroutine or, more likely, a set of related subroutines with a common block to pass data between them. There is nothing wrong with this approach.
Now consider what happens when we find that the application needs to hold conversations over two network connections at the same time. We would need to adapt the subroutines to pass in a connection number or some other way to reference which connection we are accessing. It is likely that inside the subroutines, data concerning each connection would become a set of dimensioned arrays in a common block with an element for each connection.
With object oriented programming, the connection becomes an object, each instance of which maintains its own internal data without needing us to create correctly sized dimensioned arrays.
The difference between the two approaches can be summarised by looking at the way in which the two language elements work. A subroutine is a piece of executable code that runs against supplied data. An object is a set of data that has processing operations built into it.
Strictly, an object is an individual instance of set of data and associated processing operations defined by a class module.
A class module is introduced by the CLASS statement instead of the PROGRAM, SUBROUTINE or FUNCTION statement as used in other module types. The class module is a container for definitions of the persistent data associated with the object and the subroutines and functions that run against that data.
Persistent data items are defined as either private or public. Private data persists between references to the object but can only be accessed from within it. Public data persists in the same way but can be accessed from outside the object.
Public functions and subroutines within the object are broadly equivalent to what in a more general description of OO programming would be considered as properties and methods. Public functions return data to the caller. Public subroutines perform some operation, typically using data passed in. Both may perform other operations within the object.
Referencing an Object
An object is created with a statement of the form
OBJ = OBJECT("classname")where classname is the name of a catalogued class module. From this point forwards the OBJ variable is used to access this particular instance of the class. The OBJECT() function can take additional data that will be passed into the newly created object.
Public data items, functions and subroutines are referenced by statements that use the -> operator. As a simple example, consider the network connection referenced earlier. The following somewhat simplified sequence might be used to send a message and read a response.
SESSION = OBJECT("HTML.GET", IP.ADDR) SESSION->SEND(MESSAGE) RESPONSE = SESSION->RECV() SESSION->DISCONNECTA practical application would include error checking. It would also be possible to combine the send and receive steps into a single message pair method.
A key point about this very simple example is that there is no reason why the QM process could not have several connections open simultaneously. The developer does not need to take any special action.
Constructors and Destructors
The example above passed the IP address of the remote system into the OBJECT() function. How does this work?
When an object is created ("instantiated"), the system looks for a public subroutine named CREATE.OBJECT and, if this exists, it is executed, passing in any additional arguments in the OBJECT() call.
The object remains in existance until the object variable, SESSION in the above example, is destroyed. At this point, the system looks for a public subroutine named DESTROY.OBJECT and, if it exists, executes it. Note that this will happen however the object is destroyed, even as part of an abort in the program to which the object variable belongs. In the networking example, the destructor subroutine might include statements to close the connection in a tidy manner such as sending a termination message to the remote system.
A key point about object oriented programming is that objects can inherit other objects, including all of their public data, functions and subroutines. This allows a developer to treat an object as a black box that performs some task without knowing how it achieves it and then include this into some higher level element of the application. The network example referenced above could have higher level layers that manage a communications protocol on top of the simple send/receive provided by the lower level item.
Tell Me More
A more thorough discussion of QM's object oriented programming capabilities can be found in the QMBasic section of the QM Reference Manual. There is also a simple practical example, INDEX.CLS, in the BP file of the QMSYS account. This provides the ability to walk through an index record by record.