Programme und untergeordnete Routinen

Dieses Dokument behandelt die Objekttypen, die als Routinen, d.h. als untergeordnete Programme aufgerufen werden können.

Anmerkung:
Obwohl sie ebenfalls von anderen Objekten aufgerufen werden, sind Helproutinen und Maps (Masken) genau genommen keine Routinen. Sie werden deshalb in getrennten Dokumenten beschrieben; siehe Abschnitt Helproutine und Map.

Folgende Themen werden behandelt:


Modulare Anwendungsstruktur

Eine typische Natural-Anwendung besteht nicht aus einem einzigen großen Programm, sondern ist in mehrere Objekt-Module aufgeteilt. Jedes dieser Objekte stellt eine funktionale Einheit von überschaubarer Größe dar, und jedes Objekt ist mit den anderen Objekten der Anwendung auf eine klar definierte Weise verbunden. Dadurch ergibt sich eine übersichtlich strukturierte Anwendung, was die Entwicklung und spätere Wartung erheblich erleichtert und beschleunigt.

Ein Hauptprogramm, das ausgeführt wird, kann andere Programme, Subprogramme, Subroutinen, Helproutinen und Maps aufrufen. Diese Objekte können ihrerseits wiederum andere Objekte aufrufen (eine Subroutine kann beispielsweise eine andere Subroutine aufrufen). Dadurch kann die objekt-orientierte Struktur einer Anwendung äußerst komplex und vielschichtig werden.

Mehrere Stufen (Levels) aufgerufener Objekte

Ein aufgerufenes Objekt ist jeweils eine Stufe (Level) unter dem Objekt, von dem es aufgerufen wurde; d.h. bei jedem Aufruf eines untergeordneten Objekts erhöht sich die Stufennummer um 1.

Ein Programm, das selbständig ausgeführt wird, wird auf Stufe 1 eingeordnet; Subprogramme, Subroutinen, Maps oder Helproutinen, die direkt von diesem Hauptprogramm aufgerufen werden, sind auf Stufe 2; ruft eine dieser Subroutinen ihrerseits eine andere Subroutine auf, so ist letztere auf Stufe 3.

Wird aus einem anderen Objekt über ein FETCH-Statement ein Programm aufgerufen, so wird es als Hauptprogramm eingestuft und auf Stufe 1 eingeordnet. Ein Programm, das mit FETCH RETURN aufgerufen wird, wird dagegen als untergeordnetes Programm eingestuft und ist eine Stufe unter dem Objekt, von dem es aufgerufen wurde.

Die folgende Abbildung enthält ein Beispiel für mehrere Stufen aufgerufener Objekte und zeigt, wie diese Stufen gezählt werden:

Beispiel für mehrere Stufen (Levels)

Sie können die Systemvariable *LEVEL benutzen (siehe Systemvariablen-Dokumentation), um die Level-Nummer des Objekts zu erfahren, das gerade ausgeführt wird.

Verarbeitungsfluss beim Aufruf eines untergeordneten Programms

Wenn ein CALLNAT-, PERFORM- oder FETCH RETURN-Statement oder ein Function Call, das eine untergeordnete Routine — ein Subprogramm, eine externe Subroutine, ein Programm bzw. eine Function — aufruft, ausgeführt wird, wird die Ausführung des aufrufenden Objekts unterbrochen, und die Ausführung der untergeordneten Routine beginnt.

Die Ausführung der untergeordneten Routine wird solange fortgesetzt, bis entweder ihr END-Statement erreicht ist oder die Verarbeitung der untergeordneten Routine durch die Ausführung eines ESCAPE ROUTINE- oder ESCAPE MODULE-Statements gestoppt wird.

In beiden Fällen wird die Verarbeitung des aufrufenden Objekts mit dem nächsten Statement nach dem CALLNAT-, PERFORM- bzw. FETCH RETURN-Statement, mit dem die untergeordnete Routine aufgerufen wurde, fortgesetzt.

Im Falle eines Function Call wird die Verarbeitung des aufrufenden Objekts mit dem Statement fortgesetzt, das den Function Call enthält.

Beispiel:

Verarbeitungsablauf beim Aufruf eines Unterprogramms

Programm

Ein Programm kann selbständig ausgeführt — und getestet — werden.

  • Um ein Source-Programm zu katalogisieren (kompilieren) und anschließend auszuführen, verwenden Sie das Systemkommando RUN.

  • Um ein Programm auszuführen, das bereits als katalogisiertes Objekt existiert, verwenden Sie das Systemkommando EXECUTE.

Ein Programm kann auch von einem anderen Objekt mit einem FETCH- oder FETCH RETURN-Statement aufgerufen werden. Das aufrufende Objekt kann ein anderes Programm, eine Subroutine, ein Subprogramm, eine Function, eine Helproutine oder eine Verarbeitungsregel in einer Map sein.

  • Wenn ein Programm mit FETCH RETURN-Statement aufgerufen wird, wird die Ausführung des aufrufenden Objekts unterbrochen — nicht beendet —, und das aufgerufene Programm wird als untergeordnetes Programm aktiviert. Wenn die Ausführung des aufgerufenen Programms beendet ist, wird das aufrufende Objekt reaktiviert und seine Ausführung mit dem nächsten Statement nach dem FETCH RETURN-Statement fortgesetzt.

  • Wenn ein Programm mit FETCH aufgerufen wird, wird die Ausführung des aufrufenden Objekts beendet, und das aufgerufene Programm wird als Hauptprogramm aktiviert. Das aufrufende Objekt wird nach beendeter Ausführung des aufgerufenen Programms nicht reaktiviert.

Die folgenden Themen werden nachfolgend erörtert:

Mit FETCH RETURN aufgerufenes Programm

Mit FETCH RETURN aufgerufenes Programm

Ein mit FETCH RETURN aufgerufenes Programm kann auf die vom aufrufenden Objekt benutzte Global Data Area (GDA) zugreifen.

Darüber hinaus kann jedes Programm seine eigene Local Data Area (LDA) haben, in der die nur in diesem Programm verwendeten Felder definiert sind. Außerdem kann ein Programm auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.

Ein mit FETCH RETURN aufgerufenes Programm kann jedoch keine eigene Global Data Area (GDA) haben.

Mit FETCH aufgerufenes Programm

Mit FETCH aufgerufenes Programm

Ein mit FETCH als Hauptprogramm aufgerufenes Programm verwendet in der Regel seine eigene Global Data Area (wie in der obigen Abbildung gezeigt). Es könnte allerdings auch dieselbe Global Data Area verwenden wie das aufrufende Objekt.

Anmerkung:
Ein Source-Programm kann auch mit einem RUN-Statement aufgerufen werden; siehe RUN-Statement im der Statements-Dokumentation.

Subroutine

In der Regel wird eine Subroutine verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt wird.

Die Statements, aus denen eine Subroutine besteht, müssen innerhalb eines DEFINE SUBROUTINE ... END−SUBROUTINE-Statement-Blocks definiert werden.

Eine Subroutine wird mit einem PERFORM-Statement aufgerufen.

Eine Subroutine kann eine interne Subroutine ("inline subroutine") oder eine externe Subroutine sein:

  • Interne Subroutine
    Eine interne Subroutine wird innerhalb des Objekts, welches das sie aufrufende PERFORM-Statement enthält, definiert.

  • Externe Subroutine
    Eine externe Subroutine wird als separates Objekt — vom Typ Subroutine — außerhalb des Objektes, welches sie aufruft, definiert.

Falls Sie einen Code-Block haben, der innerhalb eines Objekts mehrmals ausgeführt werden soll, ist es sinnvoll, eine interne Subroutine zu verwenden. Sie müssen diesen Block dann nur einmal innerhalb eines DEFINE SUBROUTINE-Statement-Blocks kodieren, und rufen ihn dann mit mehreren PERFORM-Statements auf.

DieseThemen werden nachfolgend erörtert:

Interne Subroutine

Interne Subroutine

Eine interne Subroutine kann in einem Objekt vom Typ Programm, Function, Subprogramm, Subroutine oder Helproutine enthalten sein.

Daten, die einer internen Subroutine zur Verfügung stehen

Eine interne Subroutine hat Zugriff auf alle Datenfelder innerhalb des Objekts, in dem sie enthalten ist.

Externe Subroutine

Externe Subroutine

Eine externe Subroutine — also ein Objekt des Typs Subroutine — kann nicht selbständig ausgeführt werden. Sie muss von einem anderen Objekt aufgerufen werden. Das aufrufende Objekt kann ein Programm, eine Function, ein Subprogramm, eine Subroutine, eine Helproutine oder eine Verarbeitungsregel in einer Map sein.

Daten, die einer externen Subroutine zur Verfügung stehen

Eine externe Subroutine kann auf die Global Data Area (GDA) zugreifen, die vom aufrufenden Objekt benutzt wird.

Darüber hinaus können mit dem PERFORM-Statement Parameter von dem aufrufenden Objekt an die externe Subroutine übergeben werden. Diese Parameter müssen entweder im DEFINE DATA PARAMETER-Statement der Subroutine oder in einer von der Subroutine benutzen Parameter Data Area (PDA) definiert werden.

Außerdem kann eine externe Subroutine eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die nur innerhalb der Subroutine verwendet werden sollen. Eine externe Subroutine kann jedoch keine eigene Global Data Area (GDA) haben.

Außerdem kann eine externe Subroutine auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.

Subprogramm

In der Regel wird ein Subprogramm verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt werden.

Ein Subprogramm kann nicht selbständig ausgeführt werden. Es muss von einem anderen Objekt aufgerufen werden. Das aufrufende Objekt kann ein Programm, eine Function, ein Subprogramm, eine Subroutine oder eine Helproutine sein.

Ein Subprogramm wird mit einem CALLNAT-Statement aufgerufen.

Wenn das CALLNAT-Statement ausgeführt wird, wird die Ausführung des aufrufenden Objekts unterbrochen und das Subprogramm ausgeführt. Nach der Ausführung des Subprogramms wird die Ausführung des aufrufenden Objekts mit dem nächsten Statement nach dem CALLNAT-Statement fortgesetzt.

Daten, die einem Subprogramm zur Verfügung stehen

Mit dem CALLNAT-Statement können Parameter von dem aufrufenden Objekt an das Subprogramm übergeben werden. Diese Parameter sind die einzigen Daten, die dem Subprogramm vom aufrufenden Objekt zur Verfügung stehen. Sie müssen entweder im DEFINE DATA PARAMETER-Statement des Subprogramms oder in einer vom Subprogramm benutzen Parameter Data Area (PDA) definiert werden.

Verfügbare Daten für Subprogramm

Außerdem kann ein Subprogramm eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die innerhalb des Subprogramms verwendet werden sollen.

Wenn ein Subprogramm seinerseits eine Subroutine oder Helproutine aufruft, kann es eine eigene Global Data Area (GDA) haben und diese gemeinsam mit der Subroutine bzw. Helproutine nutzen.

Außerdem kann ein Subprogramm auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.

Function

In der Regel wird eine Function verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt werden.

Im Unterschied zu den in Natural eingebauten Systemfunktionen dient eine Function dazu, benutzerdefinierte Funktionalität zur Verfügung zu stellen.

Die Function liefert einen Ergebniswert zurück, der von dem aufrufenden Objekt verwendet wird. Der Ergebniswert wird anhand der Daten errechnet, die der Function zur Verfügung stehen.

Ein Objekt des Typs Function enthält ein DEFINE FUNCTION-Statement für die Definition einer einzelnen Funktion und das zugehörige END-Statement.

Die Function selbst wird durch einen Function Call aufgerufen.

Daten, die einer Function zur Verfügung stehen

Mit dem Function Call können Parameter vom aufrufenden Objekt an die Function übergeben werden. Diese Parameter sind die einzigen Daten, die der Function vom aufrufenden Objekt zur Verfügung stehen. Sie müssen im DEFINE FUNCTION-Statement definiert werden.

Zusätzlich kann eine Function eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die innerhalb der Function verwendet werden sollen. Eine Function kann jedoch keine eigene Global Data Area (GDA) haben.

Außerdem kann eine Function auf anwendungsunabhängige Variablen (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.

Falls erforderlich, können Sie Ergebnis- und Parameterlayouts für das Objekt definieren, das die Function aufruft. Dazu dient das DEFINE PROTOTYPE-Statement.

Daten, die einer Function zur Verfügung stehen

Weitere Informationen siehe Function Call.

Vergleich zwischen externer Subroutine, Subprogramm und Function

In diesem Abschnitt werden die Merkmale der externen Subroutine, des Subprogramms und der Function zusammengefasst und miteinander verglichen.

Folgende Merkmale sind bei allen vorhanden:

  • Der Programmcode, der die Logik der Routine bildet, befindet sich in einem separaten Objekt, das in einer Natural-Library gespeichert wird.

  • Parameter werden in dem Objekt mittels eines DEFINE DATA PARAMETER-Statement definiert.

Die Unterschiede zwischen externer Subroutine, Subprogramm und Function werden in der folgenden Tabelle dargestellt:

Betrachtungsgegenstand Externe Subroutine Subprogramm Function
Maximale Länge des Namens 32 Zeichen 8 Zeichen 32 Zeichen
Verwendung einer Global Data Area (GDA) Nutzt eine GDA gemeinsam mit dem Aufrufer. Legt eine Instanz einer GDA an. Eine GDA ist nicht zulässig.
Prüfung von Format/Länge der übergebenen Paramter gegen die Definition im aufgerufen Objekt bei der Kompilierung Prüfung erfolgt nur, wenn die Compiler Option PCHECK auf ON gesetzt ist. Prüfung erfolgt nur, wenn die Compiler Option PCHECK auf ON gesetzt ist. Prüfung erfolgt nur, wenn zur Kompilierungszeit ein katalogisiertes Function-Objekt existiert.
Aufruf durch Aufruf durch PERFORM-Statement Aufruf durch CALLNAT-Statement Aufruf durch einen Function Call

Ein Function Call kann in Statements anstelle von schreibgeschützten Operanden benutzt werden. Ein Function Call kann auch als Statement benutzt werden.

Bestimmung des aufzurufenden Objekts zur Kompilierungs-/Laufzeit Bestimmung zur Kompilierungszeit Bestimmung zur Kompilierungs- bzw. Ausführungszeit, je nach dem Operanden, der beim CALLNAT-Statement verwendet wird. Bestimmung zur Kompilierungs- bzw. Ausführungszeit, je nach dem Operanden, der beim Function Call verwendet wird.
Nutzung eines Ergebniswertes in einem Statement Ein Ergebniswert muss einem Parameter zugewiesen werden, der als Operand in einem Statement verwendet werden soll. Ein Ergebniswert muss einem Parameter zugewiesen werden, der als Operand in einem Statement verwendet werden soll. Das Ergebnis eines Function Call wird als Operand in dem Statement verwendet, das den Function Call enthält.

In den nachfolgenden Beispielen erfolgt ein Vergleich zwischen einem Function Call und dem Aufruf eines Subprogramms.

Beispiel für einen Function Call

Das folgende Beispiel zeigt ein Programm, das eine Function aufruft, und die Definition der Function, die mit einem DEFINE FUNCTION erstellt wird.

Programm, das die Function aufruft

** Example 'FUNCAX01': Calling a function  (Program)                    
************************************************************************
*                                                                       
WRITE 'Function call' F#ADD(< 2,3 >)  /* Function call.                   
                                      /* No temporary variables needed. 
*                                                                       
END 

Definition der Function F#ADD

** Example 'FUNCAX02': Calling a function  (Function)                   
************************************************************************
DEFINE FUNCTION F#ADD                                                    
  RETURNS #RESULT (I4)                                                  
  DEFINE DATA PARAMETER                                                 
    1 #SUMMAND1 (I4) BY VALUE                                                    
    1 #SUMMAND2 (I4) BY VALUE                                                     
  END-DEFINE                                                            
  /*                                                                    
  #RESULT := #SUMMAND1 + #SUMMAND2                                      
  /*                                                                    
END-FUNCTION                                                            
*                                                                       
END 

Beispiel für den Aufruf eines Subprogramms

Um die Funktionalität, die im obgigen Beispiel für den Function Call gezeigt wird, stattdessen mittels eines Subprogramm-Aufrufs zu implementieren, müssen Sie temporäre Variablen angeben.

Programm, das das Subprogramm aufruft

Das folgende Beispiel zeigt ein Programm, das ein Subprogramm unter Verwendung einer temporären Variablen aufruft.

** Example 'FUNCAX03': Calling a subprogram  (Program)                  
************************************************************************
DEFINE DATA LOCAL                                                       
  1 #RESULT (I4) INIT <0>                                               
END-DEFINE                                                              
*                                                                       
CALLNAT 'FUNCAX04' #RESULT 2 3    /* Result is stored in #RESULT.     
*                                                                       
WRITE '=' #RESULT                 /* Print out the result of the        
                                  /* subprogram.                        
*                                                                       
END 

Aufgerufenes Subprogramm FUNCAX04

** Example 'FUNCAX04': Calling a subprogram  (Subprogram)               
************************************************************************
DEFINE DATA PARAMETER                                                   
  1 #RESULT   (I4) BY VALUE RESULT                                      
  1 #SUMMAND1 (I4) BY VALUE                                             
  1 #SUMMAND2 (I4) BY VALUE                                             
END-DEFINE                                                              
*                                                                       
#RESULT := #SUMMAND1 + #SUMMAND2                                        
*                                                                       
END