zur Homepage

Entwurf: Version 0.1

Basis dieses Entwurf sind meine Schulungsunterlagen, die auf Vorträgen fußen, erweitert um meine Praxis projiziert auf das, was in Java gängig, soweit das in RPG umsetzbar ist.

RPG programmieren mit Stil

oder geht das überhaupt?

Die folgenden Richtlinien orientieren sich am Programmierstil, wie er in modernen Programmiersprachen gepflegt wird.

Allgemeine Empfehlungen

Jede Verletzung dieser Richtlinien ist erlaubt, wenn sie die Lesbarkeit verbessert
Das oberste Ziel der Richtlinien ist Verständlichkeit und Wartbarkeit des Codes durch leichte Lesbarkeit der Quelltexte zu erreichen. Man kann nicht alles in Regeln fassen und Programmierer brauchen Flexibilität für creatives arbeiten.

Namens Konventionen

Es werden grundsätzlich englische, sprechende Namen verwendet.
Englisch ist die Sprache in internationalen Projekten. Es werden keinerlei Abkürzungen verwendet, die nicht allgemein gebräuchlich sind (html ist verwendbar), auch nicht cpy für copy, pgm für programm oder obj für object. Kurznamen sind nur dort erlaubt, wo sie erzwungen sind, also für Programme, Module und Dateinamen.
Variablennamen werden in gemischter Groß/Kleinschreibung geschrieben und bezeichnen Substantive oder Adjektive, bei zusammengesetzten Wörtern beginnt jeder Wortbestandteil mit einem Großbuchstaben. Von RPG verwendete Schlüsselwörter werden nicht verwendet, Variablennamen fangen nicht mit SQL an (wegen embedded SQL). Sonderzeichen wie # % § in Namen werden nicht verwendet (Problem im internationalen Umfeld).
Lokale Variablen in Prozeduren werden mit kurzen Namen benannt, globale Modulvariablen mit längeren Namen, Variablen Exporte sollten nur in Ausnahmefällen verwendet werden und mit dem Modulnamen als eindeutigem Präfix versehen werden Importe von Variablen zur Bindezeit sind nicht erlaubt; gegebenen Falls ist zur Laufzeit zu binden (über API QleGetExp). Globale Pseudo Typdeklarationen, auf die mit Like Bezug genommen wird, müssen in Qualified Datenstrukturen vorgenommen werden, die mit dem Namen der Copystrecke als Präfix versehen werden. Bei der Verwendung maroder SQL Precompiler sind Ausnahmen möglich (Vorsicht: Präfix SQL ist bereits reserviert).
Prozedurnamen werden in gemischter Kleinschreibung geschrieben und bezeichnen Verben mit sprechenden Erweiterungen; exportierte Prozeduren sollen, Exportnamen müssen mit dem Modulnamen als Präfix versehen werden.
Exportierte Symbole müssen eindeutig sein. Um Bindefehler auszuschließen müssen Exportnamen (Eintrag bei Schlüsselwort EXTPROC der Prototypen) mit Modulnamen als Präfix versehen werden, die eindeutigen Exportnamen sollen bevorzugt verwendet werden, weitere Pseudo qualifier als suffix oder prefix sind zu vermeiden (Ein Zugrifsmodul heißt ARTICLEund die Prozedur zum lesen ARTICLEread() und nicht readArticle() oder ähnlich).
Namen von Konstanten bezeichnen Substantive und werden in Großbuchstaben geschrieben, zusammengesetzte Begriffe werden mit _ verkettet.
Globale Konstanten können als Export oder über Copy Strecken abgebildet werden. Bei Copy Strecken sollte die Deklaration über eine Datenstruktur und Präfix mit qualified eindeutig gemacht werden.

Kommentare

Jedes Modul, jede Procedure, wird mit einem Kommentarkopf eingeleitet.
Die Prototypen exportierter Prozeduren enthalten eine Black Box Beschreibung als Kommentar. Die Beschreibung der internen Funktionalität befindet sich bei der Implementierung. Kommentare auf feinkörnigerer Ebene sind meist ein Zeichen für unzureichende Modularisierung und schlecht gewählte Namen von Variablen und Prozeduren.
Keine Randkommentare und Zeilennummern verwenden.
Echohafte Wiederholung von Anweisungen in Kommentaren ist zu vermeiden. Variablennamen werden vorrangig durch sprechende Namen beschrieben.

Layout der Quellen

Alle Deklarationen erfolgen in D Bestimmungen.
Deklarationen von exportierten Prototypen erfolgen grundsätzlich in Copystrecken, die in einer eigenen Quelldatei QRPGLEH geführt werden. Name von Modul, Quelle, Header und zugehörigen Objekten (SRVPGM oder PGM) sind gleich.
Die Programme werden einheitlich gegliedert.
Für einheitlichen Grundaufbau empfiehlt sich der Einsatz eines Generators, der aus den Prototypen der Exporte Programmrahmen generiert (GENFRAME auf der Open Source Seite www.bender-dv.de). Trennlinien innerhalb von Prozeduren und Deklarationsblöcken stören die Lesbarkeit, zur Blockbildung reicht eine Leerzeile ohne Schmuck völlig aus.
Schachtelungen und Strukturblöcke werden durch Einrückung deutlich gemacht.
Hieraus ergibt sich die bevorzugte Verwendung von "Free Format" (Ausnahme: Deklarationen und embedded SQL). Die Füllwörter EVAL und CALLP sind grundsätzlich wegzulassen, Bei Aufrufen ohne Parameter ist ein leeres Klammerpaar zu verwenden.

Modularisierung

Schichtentrennung hat oberste Priorität.
Module und werden klar nach Schichten getrennt. Ein Modul, das einen Bildschirm ausgibt, oder Druckausgaben macht, liest keine Daten und Geschäftslogik wird in separaten Modulen implementiert. Ein Serviceprogramm hat ein Modul, Procedures gehören nur gemeinsam in ein Modul, wenn sie gemeinsame Zustandsgrößen teilen (globale Variablen).
Zur Modularisierung sind SubProcedures einzusetzen.
Subroutines werden nicht verwendet. Jede Procedure umfasst maximal zwei Bildschirmseiten, weniger ist mehr. Modularisierung beginnt immer mit der Benamung, lässt sich kein treffender Name finden, ist der innere Zusammenhalt eines Moduls (hier procedure) schwach.
Die main Procedure ruft nach Parameterprüfung sofort eine lokale Procedure auf.
Die Eingangsprozedur (unbenannte) ist ein technisch notwendiger Mechanismus, mehr nicht; durch sofortige Verzweigung in eine Subprocedure stehen dort lokale Variablen zur Verfügung. Die Schnittstelle ist durch ein globales Procedure Interface zu beschreiben, ENTRY PLIST wird nicht verwendet.
Variablen Export wird nicht verwendet, lokale Deklarationen werden bevorzugt, globale Deklarationen werden für Status behaftete Zustandsgrößen benutzt.
Ausnahme von dieser Regel sind Variablen, die von embedded SQL verwendet werden, diese werden global deklariert und einheitlich mit ES_ Prefix versehen und nur als Workfelder für embedded SQL verwendet. In globalen Deklarationen werden keinerlei Feldüberlagerungen verwendet, für Extrahierungen und Konvertierungen sind immer entsprechende Subprocedures mit sprechendem Namen zu verwenden.
Parameter
Parameterübergabe by Value wird bevorzugt, bei dynamischen Aufrufen ist die übergabe als Konstante die Regel. Es wird klar unterschieden zwischen Übergabe und Rückgabe. Bei Procedures erfolgt die Rückgabe als Rückgabewert bei der return Anweisung; Rückgabe mehrere Werte erfolgt in Datenstrukturen. Verdeckte Informationsübergänge sind zu vermeiden; Procedures arbeiten mit Zustandsgrößen, Informationsübergänge werden durch Parameter abgebildet.

Programmerstellung und Binden

Dokumentierung der Programmerstellungsbefehle in der Quelle.
Programme müssen reproduzierbar gleich erstellt werden können. Ein einfacher Weg besteht darin, die Befehle zur Erstellung von Programmen und gleichartigen Objekten in der Quelle selber als Kommentar zu dokumentieren. Es bietet sich an einen Präprozessor zu verwenden, der die Befehle ausliest und die Objekte automatisch erstellt (CRTCPP auf der Open Source Seite www.bender-dv.de)
Keine Binderverzeichnisse und keine Binder Language verwenden.
Bindefehler können schwerwiegende Auswirkungen haben (Störung Transaktionslogik), deswegen ist es vorzuziehen alle Bindeangaben explizit vorzunehmen. Bei der änderung von Exporten ist grundsätzlich alles neu zu binden, was betroffen ist. Programmerstellung muss einfach und sicher sein. Bei großen Anwendungen ist Change Management Software dem Mut zum Risiko vorzuziehen.
Keine Module by Copy binden
Schwache Bindung und kleine Bindeeinheiten bevorzugen. Grundsätzlich nur by reference binden und dazu aus Modulen Serviceprogramme erzeugen. Idealerweise dynamisch zur Laufzeit binden, dazu ist ein open Source Tool verfügbar (PROCP4NAMEauf der Open Source Seite www.bender-dv.de).
Benamte Aktivierungsgruppen für Programme verwenden
Für Programme grundsätzlich Name der Quelle mit dem Entry Modul, Name des Moduls, Programmname und Name der Aktivierungsgruppe gleich nennen.
Serviceprogramme mit *CALLER oder eigener benamter Aktivierungsgruppe erstellen.
Eigene Aktivierungsgruppe (gleich Modulname) bei eigener Commitdefinition und bei Modulen ohne Zustand sinnvoll.

Deklarationen

Deklarattionen ausschließlich in D Bestimmungen
Alle Deklarationen in D Bestimmungen mit minimalem Scope. Namen mit Leerzeichen trennen, bei Datenstrukturen einrücken und Langnamen in extra Zeile nutzen.
Konstanten statt Literale verwenden.
Keine Literale im Programm verwenden, sondern stattdessen Konstanten deklarieren. Bei numerischen Werten ist es oft klarer Variablen mit Initialisierung zu verwenden, da für Konstanten nur eingeschränkt Typ Eigenschaften deklariert werden können.
Feldüberlagerungen und doppelte Deklarationen vermeiden.
Keine verdeckten Informationsübergänge, Overlay und andere Überlagerungstechniken meiden, in globalen Deklarationen strikt unterlassen. Informationsübergänge nach dem Prinzip gleicher Name gleicher Inhalt zwischen Dateipuffern und Datenfeldern durch eindeutige Präfixe oder qualified Datenstrukturen ausschließen.
Boolean Variablen statt Bezugszahlen verwenden.
Keine implizit deklarierten Bezugszahlen verwenden, statt dessen sprechend benamte Boolean Variablen deklarieren und diese über Konstanten TRUE und FALSE belegen und abfragen, statt *ON und *OFF und 0 oder 1 zu verwenden.
Rechenfelder selber deklarieren anstatt vom Compiler erstellte Zwischenfelder
Vorsicht mit geschachtelten Rechneausdrücken, statt das Rundungsverhalten durch Extender zu beeinflussen, lieber Zwischenwerte in selbst deklarierte Felder mit vorgegebener Genauigkeit stellen lassen, Literale und numerische Konstanten beim rechnen lieber durch Variablen ersetzen, oder durch Zuweisung entsprechend Casten. Keine Typumwandlungen durch Feldüberlagerung vornehmen, stattdessen Built in Functions oder eigene Procedures zum Casten verwenden.

Anweisungen

Rechenoperationen in Ausdrücken formuliern.
überflüssige Operation Codes für numerische Berechnungen nicht verwenden. Stattdessen Zuweisungen und Ausdrücke codieren, für Zeitberechnungen entsprechende Built in Functions.
Bit Frickeleien unterlassen.
Biton und Bitoff, sowie die davon abgeleiteten Konsorten sind ebenso unleserlich, wie überflüssig. Bei Flagfeldern, wie sie von manchen APIs verwendet werden, sind Hex Konstanten und das addieren von Binärzahlen vorzuziehen, soweit man dies überhaupt benötigt.
Sprunganweisungen vermeiden
Unstruktur Anweisungen, wie GOTO und CABxx sind überflüssig und durch strukturierte Elemente abzubilden. Der Gebrauch von LEAVE und ITER ist eingeschränkt verwendbar, sollte aber kontrolliert erfolgen.
Prozeduren statt Subroutinen verwenden
Subroutinen sind seit der Existenz von Prozeduren überflüssig und werden nicht mehr verwendet.
überflüssige Anweisungen weglassen.
CALLP und EVAL sind überflüssig und sind nur Synonyme für "mach es wirklich". Statt Extender und EVALR sollte man besser Zwischenfelder, Casting und transparentes Errorhandling verwenden.
Keine Fixformat Anweisungen benutzen.
Es gibt keine erforderliche Anweisung mehr, die man nicht im Free Format oder mit Built in Functions machen kann; selbst im freien Format und bei den Built in Functions gibt es schon wieder überflüssiges. Guter Programmierstil zeichnet sich durch Einfachheit und Lesbarkeit aus; alles was kryptisch ist, ist auch schlecht.

zur Homepage