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.