Startseite < Informatik < Algorithmen Datenstrukturen / Software-Engineering / Programmiersprachen < Compiler Interpreter < Setty Tinyray < Tinyray-Scanner Tinyray-Parser / Tinyray-Language < Tinyray-LanguageKit Tinyray-Visitables [ DRUCK , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 ] Tinyray-Visitors < Tinyray-PrettyPrint Tinyray-DotCode Tinyray-PovRayCode Tinyray-GlutCCode Tinyray-VRMLCode > > Tinyray-Raytracer / Tinyray-Wallpapers > > / Java C/C++ POV-Ray LaTeX > / Künstliche Intelligenz > Schach Privates / Inhalt >
Tinyray-Visitors
Ein Visitor kann Visitable-Knoten besuchen (Besuchermuster).
Einführung Allgemeines zum Visitor
Ein Visitor implementiert für jeden besuchbaren Knotentyp (Knotenklasse) eine visit-Methode, die zur Ausführung kommt, wenn der dazugehörige Knoten besucht wird. Dies klingt ein wenig danach, als ob sich die Katze selbst in den Schwanz beißen würde, doch genau so geht man bei der aspektorientierten Programmierung vor. Der Code, der eigentlich im besuchbaren Knoten stehen sollte, steht jetzt im Visitor. So wird ein Aspekt ausgelagert und kann je nach Bedarf erweitert oder ausgetauscht werden, ohne die Knoten anfassen zu müssen — Ideal für die Softwareentwicklung im Team.
Dialog zwischen Chef und Mitarbeiterin
Chef:
  1. Wir benötigen einen Farbwertprüfer für ein Tinyray-Programm!
    Klara, schaffst Du das?
Klara:
  1. Klar Chef, ich schreib einfach einen generischen Visitor für die Knoten des Tinyray-Syntaxbaumes (ColorCheckVisitor siehe unten).
Chef:
  1. Hört sich kompliziert an, hoffentlich machst du uns nichts kaputt.
Klara:
  1. Nein Chef, gerade weil ich einen Visitor implementiere, brauchst du keine Angst um bestehende Datenstrukturen zu haben. Ich werde eine neue Klasse anlegen, die sich ColorCheckVisitor nennt. Bearbeiten werde ich nur die ColorCheckVisitor-Klasse, sonst nichts!

    Die Farbwertprüfung ist nur ein weiterer Aspekt neben den schon implementierten Aspekten wie unser Tinyray-PrettyPrint oder unser Tinyray-PovRayCode.
Chef:
  1. Stimmt, da hat es ja auch funktioniert. Prima, ich liebe es, mit Profis zu tun zu haben — Danke Klara!
Implementierung Implementierung der Visitor-Schnittstelle
Im Folgenden sehen Sie die Schnittstelle eines generischen Visitors.
Visitor.java
package tinyray.language;

/**
 * Das Interface Visitor kennzeichnet einen Besucher, der alle besuchbaren
 * Knoten des Syntaxbaumes (Visitables) besuchen kann. Für jeden einzelnen
 * Knoten muss eine eigene visit-Methode implementiert werden.
 * @param <R> Der generische Rückgabetyp.
 * @param <A> Der generische Argumenttyp.
 */

public interface Visitor<R, A> {

    /**
     * Besucht einen besuchbaren Tinyray-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param tinyray Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Tinyray tinyray, A argument);

    /**
     * Besucht einen besuchbaren Camera-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param camera Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Camera camera, A argument);

    /**
     * Besucht einen besuchbaren Background-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param background Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Background background, A argument);

    /**
     * Besucht einen besuchbaren Ambience-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param ambience Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Ambience ambience, A argument);

    /**
     * Besucht einen besuchbaren Fog-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param fog Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Fog fog, A argument);

    /**
     * Besucht einen besuchbaren Defaults-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param defaults Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Defaults defaults, A argument);

    /**
     * Besucht einen besuchbaren Sun-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param sun Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Sun sun, A argument);

    /**
     * Besucht einen besuchbaren Bounding-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param bounding Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Bounding bounding, A argument);

    /**
     * Besucht einen besuchbaren Triangle-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param triangle Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Triangle triangle, A argument);

    /**
     * Besucht einen besuchbaren Sphere-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param sphere Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Sphere sphere, A argument);

    /**
     * Besucht einen besuchbaren Plane-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param plane Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Plane plane, A argument);

    /**
     * Besucht einen besuchbaren Parameters-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param parameters Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Parameters parameters, A argument);

    /**
     * Besucht einen besuchbaren Vector-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param vector Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Vector vector, A argument);

    /**
     * Besucht einen besuchbaren Real-Knoten
     * mit einer bestimmten Zusatzinformation.
     * @param real Der besuchbare Knoten.
     * @param argument Die Zusatzinformation.
     * @return Das Ergebnis des Besuches.
     */

    R visit(Real real, A argument);
}
Beispiel Beispiel eines Visitors
Wie implementiert man nun einen Visitor?
… könnte sich so manch' Einer fragen. Deshalb sei hier ein kleines Beispiel angeführt, welches dazu verwendet werden kann, die Korrektheit der in einem Tinyray-Programm verwendeten Farbwerte nachzuweisen; daher auch der Name ColorCheckVisitor.





Die Korrektheit der Farbwerte eines Tinyray-Programms definiert sich rekursiv wie folgt:
  1. Ein Tinyray-Programm ist korrekt bezüglich seiner Farbwerte (farbkorrekt), wenn alle Unterkomponenten korrekte Farbvektoren enthalten. Die Camera-Komponente ist stets farbkorrekt, da sie keinen einzigen Farbvektor enthält.
  2. Ein Farbvektor ist korrekt, wenn die jeweiligen Real-Komponenten farbkorrekt sind.
  3. Eine Real-Komponente ist farbkorrekt, wenn ihr Wert zwischen 0.0 und 1.0 einschließlich der Grenzen liegt.




Die folgende Implementierung der Farbwertprüfung (ColorCheckVisitor) richtet sich genau nach der eben angegebenen Definition.
ColorCheckVisitor.java
package tinyray.examples;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Iterator;

import tinyray.frontend.ParseException;
import tinyray.frontend.Parser;
import tinyray.frontend.Scanner;
import tinyray.language.Ambience;
import tinyray.language.Background;
import tinyray.language.Bounding;
import tinyray.language.Camera;
import tinyray.language.Defaults;
import tinyray.language.Fog;
import tinyray.language.LanguageKit;
import tinyray.language.Parameters;
import tinyray.language.Plane;
import tinyray.language.Real;
import tinyray.language.Sphere;
import tinyray.language.Sun;
import tinyray.language.Tinyray;
import tinyray.language.Triangle;
import tinyray.language.Vector;
import tinyray.language.Visitable;
import tinyray.language.Visitor;


/**
 * Die Klasse ColorCheckVisitor stellt nur ein Beispiel eines
 * Visitors dar. Der Besuch eines Knotens ermittelt, ob die darin
 * erreichbaren Farbvektoren wertemäßig korrekt belegt sind.
 *
 * Ein Farbvektor mit den Werten [0.0, 1.2, -0.2] wäre unkorrekt,
 * da 1.2 > 1.0 und -0.2 < 0.0.
 *
 * Ob der gesamte Tinyray-Baum nun unkorrekte Farbvektoren enthält,
 * kann mit dem vorliegenden ColorCheckVisitor geprüft werden.
 */

public class ColorCheckVisitor implements Visitor<Boolean, Void> {

    public Boolean visit(Tinyray tinyray, Void nothing) {
        boolean result = true;
        result &= tinyray.camera.accept(this, null);
        result &= tinyray.background.accept(this, null);
        result &= tinyray.ambience.accept(this, null);
        result &= tinyray.fog.accept(this, null);
        Iterator<Sun> suns = tinyray.suns.listIterator();
        while (suns.hasNext()) {
            result &= suns.next().accept(this, null);
        }
        Iterator<Visitable> visitables = tinyray.visitables.listIterator();
        while (visitables.hasNext()) {
            result &= visitables.next().accept(this, null);
        }
        return result;
    }

    public Boolean visit(Camera camera, Void nothing) {
        return true; // Die Kamera enthält keine Farbvektoren!
    }

    public Boolean visit(Background background, Void nothing) {
        return background.color.accept(this, null);
    }

    public Boolean visit(Ambience ambience, Void nothing) {
        return ambience.color.accept(this, null);
    }

    public Boolean visit(Fog fog, Void nothing) {
        return fog.color.accept(this, null);
    }

    public Boolean visit(Defaults defaults, Void nothing) {
        return defaults.parameters.accept(this, null);
    }

    public Boolean visit(Sun sun, Void nothing) {
        return sun.color.accept(this, null);
    }

    public Boolean visit(Bounding bounding, Void nothing) {
        boolean result = true;
        Iterator<Visitable> visitables = bounding.visitables.listIterator();
        while (visitables.hasNext()) {
            result &= visitables.next().accept(this, null);
        }
        return result;
    }

    public Boolean visit(Triangle triangle, Void nothing) {
        return triangle.parameters.accept(this, null);
    }

    public Boolean visit(Sphere sphere, Void nothing) {
        return sphere.parameters.accept(this, null);
    }

    public Boolean visit(Plane plane, Void nothing) {
        return plane.parameters.accept(this, null);
    }

    public Boolean visit(Parameters parameters, Void nothing) {
        boolean result = true;
        if (parameters.visitables.size() > 0) { // diffuse color
            result &= parameters.visitables.get(0).accept(this, null);
        }
        if (parameters.visitables.size() > 1) { // ambient color
            result &= parameters.visitables.get(1).accept(this, null);
        }
        return result;
    }

    public Boolean visit(Vector vector, Void nothing) {
        return vector.x.accept(this, null)
            && vector.y.accept(this, null)
            && vector.z.accept(this, null);
    }

    public Boolean visit(Real real, Void nothing) {
        return (real.value >= 0.0) && (real.value <= 1.0);
    }


    /* Hauptprogramm */

    public static void main(String[] arguments) {

        if (arguments.length > 0) {

            try {

                // Scanner, Parser, Tinyray wie immer!
                Scanner scanner =
                    new Scanner(new FileInputStream(arguments[0]));
                Parser<Visitable, Tinyray> parser =
                    new Parser<Visitable, Tinyray>(scanner, new LanguageKit());
                Tinyray tinyray = parser.parse();

                // Verwendung des ColorCheckVisitors
                boolean okay = tinyray.accept(new ColorCheckVisitor(), null);

                if (okay) {
                    System.out.println("Colors okay!");
                } else {
                    System.out.println("Colors NOT okay!");
                }

            } catch (ParseException e) {
                System.err.println(e.getMessage());
            } catch (FileNotFoundException e) {
                System.err.println(e.getMessage());
            }
        } else {
            System.err.println("Tinyray-Programm-Datei angeben.");
        }
    }
}
Das „Hallo Welt”-Programm
hello-world.tinyray
// Hallo Welt!

Tinyray {
    Sphere {
        [0, 0, 0]; // Wo liegt die Kugel?
        2.0        // Welchen Radius besitzt die Kugel?
    }
    Sun {
        [1, 1, 1]; // In welcher Richtung befindet sich die Sonne?
        [1, 1, 1]  // Welche Farbe hat die Sonne?
    }
}
… enthält natürlich nur korrekte Farbwerte.
java ColorCheckVisitor hello-world.tinyray
Colors okay!
Doch beim folgenden Tinyray-Programm sieht es ganz anders aus. Unter den vielen Farbvektoren hat sich ein schwarzes Schaf eingeschlichen!
badcolor.tinyray
Tinyray {
    Camera { [0, 17, 4.5]; [-0.22, 0, -2]; [0.2, 0, 1] }
    Ambience { [0.4, 0.4, 0] }
    Sun { [0, 0, 1]; [0.4, 0.3, 0.0] }
    Sun { [0, 17, 20]; [0.9, 0.9, 0.9] }
    Fog { [0.6, 0.6, 0.6]; 0.035 }

    Bounding {
        Defaults { [0.8, 0.8, 0.8]; [0.2, 0.2, 0]; 0.1; 50.0; 0.6 }
        Sphere { [+5.0000, +0.0000, 0]; 0.3; [0.8, 0.6500, 0] }
        Sphere { [+4.8296, +1.2941, 0]; 0.3; [0.8, 0.5853, 0] }
        Sphere { [+4.3301, +2.4999, 0]; 0.3; [0.8, 0.5250, 0] }
        Sphere { [+3.5355, +3.5355, 0]; 0.3; [0.8, 0.4732, 0] }
        Sphere { [+2.4999, +4.3301, 0]; 0.3; [0.8, 0.4335, 0] }
        Sphere { [+1.2941, +4.8296, 0]; 0.3; [0.8, 0.4085, 0] }
        Sphere { [+0.0000, +5.0000, 0]; 0.3;
            [0.8, 0.4000, -0.2] // Wo ist das schwarze Schaf?
        }
        Sphere { [-1.2941, +4.8296, 0]; 0.3; [0.8, 0.4085, 0] }
        Sphere { [-2.4999, +4.3301, 0]; 0.3; [0.8, 0.4335, 0] }
        Sphere { [-3.5355, +3.5355, 0]; 0.3; [0.8, 0.4732, 0] }
        Sphere { [-4.3301, +2.4999, 0]; 0.3; [0.8, 0.5250, 0] }
        Sphere { [-4.8296, +1.2941, 0]; 0.3; [0.8, 0.5853, 0] }
        Sphere { [-5.0000, +0.0000, 0]; 0.3; [0.8, 0.6500, 0] }
        Sphere { [-4.8296, -1.2941, 0]; 0.3; [0.8, 0.7147, 0] }
        Sphere { [-4.3301, -2.4999, 0]; 0.3; [0.8, 0.7750, 0] }
        Sphere { [-3.5355, -3.5355, 0]; 0.3; [0.8, 0.8268, 0] }
        Sphere { [-2.4999, -4.3301, 0]; 0.3; [0.8, 0.8665, 0] }
        Sphere { [-1.2941, -4.8296, 0]; 0.3; [0.8, 0.8914, 0] }
        Sphere { [+0.0000, -5.0000, 0]; 0.3; [0.8, 0.9000, 0] }
        Sphere { [+1.2941, -4.8296, 0]; 0.3; [0.8, 0.8914, 0] }
        Sphere { [+2.4999, -4.3301, 0]; 0.3; [0.8, 0.8665, 0] }
        Sphere { [+3.5355, -3.5355, 0]; 0.3; [0.8, 0.8268, 0] }
        Sphere { [+4.3301, -2.4999, 0]; 0.3; [0.8, 0.7750, 0] }
        Sphere { [+4.8296, -1.2941, 0]; 0.3; [0.8, 0.7147, 0] }
    }

    Defaults { [0.27, 0.26, 0.2]; [0.2, 0.15, 0.1]; 0.4; 600.0; 0.7 }
    Plane { [-1, 1, -1.2]; [1, -1, -1]; [-1, -1, -1] }
}
Gleich mal testen, ob der ColorCheckVisitor in dem Tinyray-Programm badcolor.tinyray das besagte schwarze Schaf erkennt!
java ColorCheckVisitor badcolor.tinyray
Colors NOT okay!
Ja, das schwarze Schaf wurde entdeckt!

Leider kann man daraus nicht erkennen, wo sich nun genau im Tinyray-Programm die unkorrekte Farbangabe verbirgt bzw. wie die unkorrekte Farbangabe lautet.
Diese Klasse dahingehend zu erweitern, sei Ihrem Ehrgeiz überlassen.