The purpose of this paper is to describe how creating custom SAS objects and using Object Orientated Programming enhances programmer productivity, reduces defects and enhances code reuse. The paper describes how a online multi-user data entry application was developed entirely using SAS OOP. The techniques used apply to any SAS/AF application, not just those using FRAME entries.
I presented this paper at the SAS Users Group Australia – SUGA 97 Conference back in 1997. Version 6 features were still fairly new and OO was the new concept.
Pleasal Enterprises Pty Ltd
The major focus of this paper is our experiences in using Object Orientated Programming (OOP) principles to develop several new Online Transaction Processing (OLTP) screens for an existing small database application called COGS. This COGS application is used to maintain the table of cost centres for Taconet Billing, which is Telstra’s internal computer chargeback application. While COGS has been in production for over five years with only very minor changes, recently we had a requirement to add nearly twenty new data files and their corresponding screens to it.
The initial thought was to make copies of the original data maintenance screen and modify each of the copies for the new screens. The disadvantage with this approach was that all the screens would have had a high percentage of common code with only minor (but incompatible) changes between them. As this would have been a major maintenance problem it was decided to investigate some of the new OOP development features in SAS.
OOP and SAS
Object Orientated Programming is a technique for writing programs where the emphasis is on the data in the program and the operations performed on it. Applications are designed by grouping data into common categories upon which actions are performed. These data categories are called classes and the actions are called methods. An object is a data element derived from a class.
This approach requires both the development tools that support OOP and a new way of thinking by application programmers.
With the release of SAS 6.07 and 6.08 a number of new features were added to support OOP principles. Some of these features are: FRAME Entries, SCL lists, Method blocks and Classes.
FRAME entries provide SAS/AF with a new approach to designing Graphical User Interfaces (GUIs) such as menu driven interactive windowing applications. FRAME entries are the successor of PROGRAM entries and are in many ways similar as they enable you to design, build and run an interactive application controlled by Screen Control Language (SCL). As well as this FRAME entries use OOP to create applications that are graphically oriented.
FRAME entries come with predefined classes called widgets, which are objects that display information or accept user input. Some of these are: Icons, Push buttons and Check boxes.
Development of FRAME entries requires a vector graphics display device, such as found on a PC or graphics terminal. Because we did not have this capability in our mainframe environment we were unable to develop FRAME entries. Instead all of our screens were developed using PROGRAM entries.
SCL lists are like arrays in that they are ordered collections of data. Unlike arrays, SCL lists are more flexible in their use. Some of the differences are:
· SCL lists can contain both numeric and character data. Arrays can only contain one or the other.
· SCL lists can be created, grown, shrunk and destroyed as needed. Arrays must be defined to a certain size in advance.
· SCL lists can contain other SCL lists. Arrays are limited to containing only numeric or character data.
· SCL lists are maintained entirely in memory and are not associated with disk or screen data. Arrays are defined by combining several variables of dataset or screen data.
The flexibility of SCL lists allows them to be used in a variety of ways that previously would have been impossible because of the restrictions imposed by arrays.
Method blocks are Screen Control Language (SCL) program modules that can either be called within the same program or by other programs. This is similar to the Link statement except that method blocks can be in an external program.
Method blocks can have a list of parameters for passing values between routines.
Classes are templates that are used to create object instances. The class definition contains the characteristics of the object and the actions the object can perform.
New classes can be defined by adding to an existing class. This new subclass inherits all the functionality of the existing class, as well as having the additional functions.
Classes are reusable across many applications.
The design of an Object Orientated application is similar to that of structured programming, however the emphasise is on the data rather than the program.
The initial analysis of the existing COGS application was performed by using a print out of the program for the existing data maintenance screen. This analysis highlighted areas of common code, which were listed out into a hierarchy of high to low level functionality. The remaining code was reassessed as to where the high level functions would be called from, providing a common template for all the screens.
The common functions that had been arranged into a hierarchy were then grouped into common data categories. The result was:
Each of these categories were then defined as an object class with each class inheriting the functionality of the lower class. This is best likened to an onion where each layer encloses the next, which in turn encloses the layer beneath it.
The application only communicates with the outer most layer, which then interfaces only with the next lower layer. This “hides” the lower levels from the application so that changes, such as bug fixes, can be implemented transparently.
Classes are created by combining SCL lists and methods using the BUILD command.
The resulting program structure contained four types of programs and four classes. These were: Application Program, Display Program, List Program, Select Program, Database Class, Dataset Class, Libref Class and Object Class.
This class was derived directly from SASHELP.FSP.OBJECT.CLASS, which is the parent of all SAS/AF classes. The Object class provided debugging and error handling. Because all the other classes inherit its methods these functions did not have to be written into each module.
One of the debugging functions was that the _INIT_ and _TERM_ methods were overridden to write to the log every time an object is initialised or terminated.
An example of error handling was that Instance variables were defined to hold a return code and error message. The value of these was unique for each error condition across the entire application. This meant that the application did not need to determine what the error was, if there was an error all that needs to be done is get and display the message variable.
This class was derived from the Object class and provides dataset allocation and deallocation.
This class was derived from the Libref class and provides access to SAS datasets within the library. All access to the datasets were via this class. This had the advantage that the under lying data structure could be changed without affecting the application. This could be useful in developing client/server applications. The client could be initially developed using a class catalog that uses local data. When the server was ready the class could be switched across without changing the client.
This class implemented methods to replace most of the SCL dataset functions, such as FETCHOBS, UPDATE, VARNAME, GETVARC and PUTVARC. Also, there were methods to support record locking and concurrent access.
This class was derived from the Dataset class and is responsible for providing all the high level data maintenance functions.
Since all access to the database was via the Database class, the PUTVARx methods from the Dataset class were overridden to provide data validation. This meant that only valid data could be written to the database. In addition as the validation was part of the class, all modules using the class had consistent validation rules.
To save the application having to match several data files, the FETCH method was overridden to automatically derive all the matching observations based on the foreign keys in the main dataset. These derived variables were read-only to avoid possible update conflicts. As far as the application was concerned they could be obtained using a GETVARx method like any other variable.
Each of the datasets within the database had a Display program that performed all data maintenance functions. Each of these programs was based on the template from the earlier analysis. The code in these programs was kept to a minimum and class methods used wherever possible.
The List program could be called to show a selection list of observations to process either from the PMENU item or by a function key. Any of the key variables could be listed in this way.
Similarly the Select program could also be called to set a selection criteria before showing the list.
Each of the datasets within the database had a List program that enabled record selection from a list based on the current selection criteria. Each of these programs was based on a common template. Again there was minimal code in these programs.
The Select program could be called to set a selection criteria for the list either from the PMENU item or by a function key.
Once an observation had been selected, control returned to the Display program for processing.
There was also a common Select program that set the current selection criteria for the List program. This program used an extend table to display the variables in the dataset and once the values had been entered, a WHERE clause was applied to the dataset and control passed to the List program.
Currently only exact match conditions are supported, however changing the class to provide additional comparisons would automatically update all modules that used this class.
The Application module was reduced to object initialisation and termination.
For example to maintain the Project Acronym dataset the code would be:
No further code would be necessary as the classes would provide all the functionality.
Everyone does documentation. Don’t they?
Somehow documentation is always tacked on to the end of a project plan with the same conviction that we tell our mothers that we still eat our greens after leaving home. Despite the best of intentions, it just never happens.
To avoid this there were no specifications written for this development, instead the program documentation was written first and then given to the programmer as “the spec”. This had the advantage that the documentation was written! This also saved having to write a specification of how the program should work and then write documentation of how it actually does work. Obviously the documentation had to be updated as work progressed and functionality changed.
Having designed the application we commenced construction.
As there were two programmers assigned to the development, it was decided that one would work on the Classes, while the other defined the program screens. While at first glance this would seem to be impossible since the screens depended on the functions within the classes, this was achieved by defining a dummy class catalog. This dummy class catalog contained all the necessary methods, but initially only returned constant values. As working classes became available they replaced the dummy definitions. This allowed the screens to be developed in parallel to the class definitions.
This approach would also be useful in other situations such as developing client/server applications. The client could be developed using a class catalog that uses local data. When the server component is ready the dummy class is replaced with the full function class. Provided the methods are the same the change from local to remote data should be transparent to the client application.
Testing of the application was a continuous process in that new functions were tested as they were added to the classes. This enabled us to identify and correct problems quickly. One of the advantages of OOP was that when we corrected a class, all the modules using that class received the benefit of the correction.
We found the first Display and List programs took some time to develop, however once having established our templates, the programs for the remaining screens could easily be developed in a couple of hours.
By creating our own custom objects and using Object Orientated Programming, we found that programmer productivity was enhanced, defects were reduced, as well as enabling us to reuse code. These techniques apply to any SAS/AF application, not just those using FRAME entries.
SAS Screen Control Language Reference, Version 6
SAS Screen Control Language Usage, Version 6
SAS/AF Software: Frame Entry, Usage and Reference, Version 6
SAS Technical Report P-216, SAS/AF Software, SAS/FSP Software, SAS Screen Control Language: Changes and Enhancements, Release 6.07
Concepts and common terminology are explained in SAS/AF Software: Frame Entry, Chapter 1 • Introducing the FRAME Entry.
Creating and modifying classes is documented in SAS/AF Software: Frame Entry, Chapter 10 • The Class Entry.
Also, examples of creating classes are contained in Chapter 7 • Creating Customized Classes and Methods Creating Two Subclasses of the Object Class.
The Object class is the parent of all SAS/AF objects. All instance variables and methods are documented in SAS/AF Software: Frame Entry, Chapter 14 • The Object Class.
About the author
Paul Shipley has worked in IT for 13 years, mostly at Telstra Corporation Limited. Since learning SAS over ten years ago he has used it in a variety of roles in user support and application development. Currently Paul is the Technical Leader for Taconet Billing, Telstra’s internal computer chargeback application.
When not cutting code Paul can be found instructing aerobics classes or working out in the gym.