Heiner Kücker

Algorithmische Symmetrie in Java

Home

Java-Seite

   Weiterentwicklung_Java

     NullPointer

     Algorithmische
     Symmetrie

     Finder_Feature

     Multiple_Interface
     Implementationen

     Proxy_Methoden

     Speicher_Klassen

   WebCam_Demo

   JSP WorkFlow
   PageFlow FlowControl
   Page Flow Engine
   Control_and_Command

   JSP_Spreadsheet

   Domain Parser

   Codegenerator_für
   hierarchische
   Datenstrukturen

   Expression_Engine
   Formula_Parser

   State Transition Engine

   AspectJ

   Java_Explorer

   DBF_Library

   Kalender_Applet

   SetGetGen

   BeanSetGet

   CheckPackage

   LineNumbers

   GradDms

   Excel-Export

   StringTokenizer

   JspDoc

   JspCheck

   JSP-Schulung
   Java Server Pages
   Struts

   AsciiTabellenLayouter

   StringSerial

   Silbentrennung

   JDBC_Schlüssel-
   Generierung

   bidirektional/
   unidirektional
   gelinkte Liste

   Java_Sitemap
   Generator

   XmlBuilder

   RangeMap

Alaska-XBase++-Seite

Projekte

Philosophien
Techniken


Konzepte

Sudoku

Kontakt /
Impressum


Links

SiteMap





Letzte Aktualisierung:
24.08.2002

Algorithmische Symmetrie

In fast allen Programmen gibt es Code-Teile, in denen Ressourcen angefordert werden und wieder abgegeben werden müssen. Typisch sind auch Codings die aus Vorbereitung, eigentlicher Abarbeitung und Endebehandlung bestehen.

Zum Beispiel muss eine Datei vor der Bearbeitung geöffnet werden und nach der Bearbeitung wieder geschlossen werden. Die Bearbeitung ist nur zwischen Öffnen und Schliessen möglich.

Ähnliches gilt für JDBC-Connections.

Auch Schleifen brauchen meist eine Anfangs- und Endbehandlung und haben einen Code-Körper.

Dafür ist ein Sprachmittel, welches die Einhaltung der algorithmischen Symmetrie per Compiler-Prüfung absichert, wünschenswert.

Anforderung/Öffnen/Initialisieren
{
   Benutzung
}
Rückgabe/Schliessen/Rücksetzen
Das Sprachelement könnte folgendermassen aussehen:
File myFile = new File("xxx");     | File myFile = new File("xxx");
                                   |
begin myFile                       | myFile.open();
{                                  | {
   myFile.read();                  |    myFile.read();
   myFile.write("yyy");            |    myFile.write("yyy");
}                                  | }
end myFile                         | myFile.close();
Sinnvoll ist die Kombination des begin-end-Blocks mit einem try-finally-Block, um zu sichern, daß die Anweisungen des end-Blockes auch beim Werfen einer Exception ausgeführt werden. Man könnte auch sagen, die algorithmische Symmetrie über den begin-end-Block ist eine Erweiterung der try-finally-Konstruktion um Anfangsbehandlung und Code-Wiederverwendung.
File myFile = new File("xxx");     | File myFile = new File("xxx");
                                   |
begin myFile                       | myFile.open();
                                   | try{
                                   |
   myFile.read();                  |    myFile.read();
   myFile.write("yyy");            |    myFile.write("yyy");
                                   |
end myFile                         | }catch(Exception e){
                                   |   Log.exception(e);
                                   | }finally{
                                   |   myFile.close();
                                   | }
Die begin- und end-Blöcke sollten im Source-Code der entsprechenden Klasse deklariert werden.
Dabei muß der Scope beachtet werden. Wenn die begin- und end-Blöcke Methoden sind, können sie die Variablen in der benutzenden Methode nicht sehen. Entweder das kontrollierte Objekt wird als Referenz übergeben oder der Code wird per include eingefügt.

Da die Bearbeitung nur im Block erlaubt ist, ist ein Sprachmittel zum Erlauben der Benutzung nur im Block sinnvoll.

public blocked void write(String parStr){
  ...
}
Das zusätzliche Schlüsselwort "blocked" sorgt dafür, daß die Methode nur innerhalb eines Blockes auf das entsprechende Objekt verwendet werden darf.

Für den Fall, daß die Operationen auf dem blockierten Objekt in Methoden zusammengefasst werden, benötigt man ein Sprachmittel zum Delegieren der Benutzungs-Erlaubnis in Methoden.

public void writeln(blocked File parFile,String parStr){
  parFile.write(parStr); // blocked method
  parFile.write("\n");   // blocked method
}
Meine Überlegung ist, das Blockieren an den Parameter und nicht an die Methode zu binden. Der Compiler darf die Verwendung nur in einem Block auf das entsprechende Parameter-Objekt erlauben.

Eine andere Möglichkeit ist die Verwendung eines Macro-Prozessors. Der Macro- Prozessor arbeitet nach folgendendem Prinzip:

Definition des Macros:

macro WRITE <name>, <outStrExpression>
<name>.write(<outStrExpression>)
macroend
Verwendung im Quelltext:
WRITE outFile , strOut ;
Auflösung durch Macro-Prozessor:
outFile.write( strOut ) ;

Vorteile

Die Includierung der Macros durch einen Pre-Prozessor erlaubt Code-Bausteine, die Struktur-Elemente (typisch die Brackets { und } ) enthalten. Das können try- catch-finally-Blöcke, if-, for-, while- und do-while Strukturen sowie Blöcke für beschränkte Variablensichtbarkeit sein. Das ist mit Methoden oder Objekten nicht möglich.

Nachteile

Einige Fehler werden erst vom Java-Compiler gefunden. Hier kann durch bessere Prüfungen im Pre-Prozessor etwas entlastet werden.
Erst eine Integration des Pre-Prozesors in den Java-Compiler erlaubt die richtige Zuordnung der Zeilen-Nummern bei Syntax- und Laufzeitfehlern. Wünschenswert ist eine Zeilenangabe Source-Zeile:Macro-Zeile.

notwendige Erweiterungen

Im jetzigen MacroProcessor-Prototyp ist noch keine Validierung eingebaut. Die Kontrolle auf die Verwendung bestimmter Makros nur innerhalb von anderen Makro-Klammern könnte mit Techniken analog zur Tag-Validierung von JSP-Tags erfolgen. Es bietet sich die passive Validierung über bestimmte Schalter, Ausdrücke oder reguläre Ausdrücke beziehungsweise aktiv über spezielle Validierungs-Klassen, die vom Preprozessor geladen und mit einer Referenz auf den Source-Kontext gestartet werden.

Zur Schachtelung von Makros ist ein Mechanismus zum Bereitstellen der Schachtelungstiefe sinnvoll. Dadurch können zum Beispiel Laufvariablen unterschieden werden ( i<depth> -> i0, i1, i2 ...).

Probleme

Die Bereitstellung dieses Features verhindert nicht den unmittelbaren Zugriff auf die in den Makros verwendeten API-Funktionalitäten. Man bräuchte also noch eine Möglichkeit zum Abschalten (Kapseln) von API-Methoden. Das passt zur OOP-Kapselung und erweitert diese.

Macro-Processor Prototyp

Hier der in einem sehr frühen Entwicklungsstdium befindliche Pre-Prozessor für die Macro-Ersetzung. Je nach Zeit werde ich diesen weiterentwickeln.

Download der Quelldateien AlgoSymm.zip

Installation:

Entpacken in Verzeichnis Ihrer Wahl (z.B. G:\algosymm)

Start mit run.bat .


Hier der Thread auf de.comp.lang.java zu diesem Thema

Achtung: Erweiterungen und Fixes stelle ich ohne Historie und ohne Ankündigung hier bereit.
Deshalb am besten immer die letzte Version runterladen.

Lizenzbedingungen:

Die Programme, Quelltexte und Dokumentationen können ohne irgendwelche Bedingungen kostenlos verwendet werden.
Sie sind Freeware und Open Source. Für Fehler und Folgen wird keinerlei Haftung übernommen.

Hinweise zur Fehlerbeseitigung und Verbesserung sind mir willkommen.

Ich freue mich auch über Feedback bezüglich der erfolgreichen Verwendung meiner Sourcen.

Bei Fragen helfe ich gern mit Hinweisen oder zusätzlicher Dokumentation, falls ich dafür Zeit habe.