Introduction to Relational Object Programming (RRP)

Relational Object Programming, Four "Concerns"

Main Driver / Controller Object

Shared Workspace Object

Rule Object(s)

Task Object(s)

 

Main Driver / Controller Object

  • Locate and sequence application objects, required and optional. All of the pieces and parts of the application can be assembled after the inbound datastream has been scanned for anomalies, specific values, and in-stream directives. For auditing and regulatory purposes, only the programs that are actually needed are loaded and then executed.
  • Implement the global data vector between every object. Parent-child relationships do not exist because Busines Rules and application features are assembled based on need. Traditional software design mandates 100% assembly of every program in the application for every session. RRP design does not assume that every component, feature version, and business rules will be present. The global datavector is a shared connection from workspace to every object that IS present during a session.
  • Execute objects in the specified sequence. The controller does not evaluate which objects are to be executed. The optimal solution is to move evaluation logic into a Rule or Task object. RRP models maintain an absence of conditional execution logic in the controller.

Shared Workspace Object

A simple application may use a single datavector that contains all of the NUM and CHAR values from a single observation to be shared across datasets and procedures. This is the easy way to simplify complex Macros.

A global datavector is the location for arrays and RDBMS observations aggregated from any number of local and/or remote sources. Shared data is automatically piped into every object that is coded to designate a specific need for shared data. When data is accessible on a global basis, there is no need to pass parameters between programs.

Rule Object(s)

Evaluates data in Shared Workspace or in a SAS dataset. This component can set/reset a flag in Shared Workspace, modify rows or columns in the dataset, or enable/disable a specific software component.

Task Object(s)

Every remaining sofware feature, functionality, etc.

 


COMMUNICATIONS, INDEPENDENCE

Repository Relationship Programming (RRP) defines an intermediate relationship between each object. The idea is to physically separate objects from each, using a logical connection instead.

Communications Independence

Prohibit most implementations of dot-notation where the parent object invokes a child object..

objectName.methodName();

Prohibit the use of events with parameters.

_sendEvent('event name', arg1, arg2, arg3);

Prohibit the use of parameters between programs.

objectName.methodName(arg1, arg2, arg3);

Prohibit most implementations where the parent object makes a direct exchange of data with a child object.

varName=objectName.varName;

objectName.varName=varName

Physical Independence

Remove IMPORT, _NEW_, and INCLUDE statements.

Remove hardcoded program names, operating system paths, and macro parameters.

 

"..flexibility over the long haul, with attendant reduction of cost and containment of technical risk, requires more than the mere *form* of modularity -- and it also requires a match between responsibility and control. It's easy to do something once, or even to do it for a while, but that it's much harder to establish a process that does that same thing reliably for weeks or months or years at a time."

Peter Coffee, Ziff Davis

 


OBJECT INDEPENDENCE

Create a list of every object (a.k.a. SAS program) that is used in the application. Create a dataset listing of SAS objects by reading the specified SAS Catalogs with Proc SQL.

  • This dataset contains 50% of the information necessary to create automatic flowcharts.

 

The following object source code demonstrates how to assemble a SAS application without IMPORT or INCLUDE statements

class standardIncludeTemplate;                                                 
public list application / (sendEvent='N');

runInterface: method;
dcl num dset;
dcl char arg1 arg2 arg3 arg4;
dcl object thisProgram;

submit continue;
proc sql;
create table work.objects as
select *
from sashelp.vcatalg
where libname='MONTURA' and
memname='R4_LINKLIST' and
objtype='CLASS';
quit;
endsubmit;

dset=open('work.objects', 'i');
do while (fetch(dset)=0);
arg1=getvarc(dset, varnum(dset, 'libname'));
arg2=getvarc(dset, varnum(dset, 'memname'));
arg3=getvarc(dset, varnum(dset, 'objname'));
arg4=getvarc(dset, varnum(dset, 'objtype'));

thisProgram=instance(loadclass(arg1||'.'||arg2||'.'||arg3||'.'||arg4));
inserto(application, thisProgram, -1);
end;
close(dset);
endmethod;
endclass;

THE RELATIONSHIP CONDUCTOR

Applications are composed of a variable number of objects, per session. Two objects that currently execute in sequence can be separated with one (or more) new rule objects at the click of a mouse. The solution is to implement something that can make data available to every object, regardless of execution sequence.

With this approach, there is no need to pass positional and keyword parameters between programs. The SCL list is the perfect conductor because it can be loaded with numeric and character data elements at the same time.

Most applications need the following types of data channels.

  1. Primary data channel(s) for single-iteration data
  2. Secondary channel(s) for session data.
  3. A primary channel for messages that control session go/stop status.
  4. Secondary channel(s) for messages related to business-rules and other toggles
class universalDataVector;
public list sku / (sendEvent='N', description='Data channel'); public list storeCount / (sendEvent='N', description='Data channel); public list messages / (sendEvent='N', description='Execution control channel'); public list rules / (sendEvent='N', description='Business rule toggles');
endclass;

COMMUNICATION CHANNELS

The implementation of Karl Weick's intermediate relationship is the list attribute named sku. It works just like a local attribute (variable) within the current object. This example shows how to read one row from a SAS dataset and insert every variable, character and numeric, into a single global vector.

Each data element is named with the column name in the SAS dataset.

class item;                                                            
public list sku / (sendEvent='N');
public list activeMethods / (sendEvent='N'); public list messages / (sendEvent='N');

runInterface: method;
dcl num xMethod;
do xMethod=1 to listlen(activeMethods) while (listlen(messages)=0);
call send(_self_, getitemc(activeMethods, xMethod));
end;
endmethod;

interface1: method;
submit continue;
proc SQL undo_policy=none inobs=1;
create table work.temp as
select * from sysdata.inventory
where sku_status=0;
quit;
endsubmit;
endmethod;

interface2: method;
dcl num dset i;

dset=open('work.temp', 'i');
fetch(dset);

do i=1 to attrn(dset, 'NVARS');
if vartype(dset, i)='N'
then insertn(sku, getvarn(dset, i), -1, varname(dset, i));
else insertc(sku, getvarc(dset, i), -1, varname(dset, i));
end;
close(dset);
endmethod;
endclass;

The next object to execute simply reads data populated by the ITEMS object from the SKU attribute. The following source code shows how to fetech a single data element from the global data vector.

class storeplanning;                                           
public list sku / (sendEvent='N');
public list storeCount / (sendEvent='N');
public list activeMethods / (sendEvent='N');

runInterface: method; dcl num xMethod;
do xMethod=1 to listlen(activeMethods) while (listlen(messages)=0);
call send(_self_, getitemc(activeMethods, xMethod));
end;
endmethod;

interface1: method;
dcl char this_sku=getnitemc(sku, 'sku_id');

submit continue;
proc SQL undo_policy=none;
create table work.temp as
select count(distinct store_id) as count, alpha_id
from sysdata.storeplanning
where sku_id='&this_sku'
group by alpha_id;
quit ;
endsubmit;
endmethod;

interface2: method / (description='Store Count by Geo-Alpha');
dcl char arg1;
dcl num dset arg2;

dset=open('work.temp');
do while (fetch(dset)=0);
arg1=getvarc(dset, varnum(dset, 'alpha_id'));
arg2=getvarn(dset, varnum(dset, 'count'));

insertn(storeCount, arg2, -1, arg1);
end;
close(dset);
endmethod;
endclass;

THE CONNECTION

A system utility automatically links universalDataVector to every object in the application.
Data is omnipresent within the session.

Read the technical specification for complete details: Repository Relationship Programming.