XML * XML-SCHEMA * XPATH * XSL * XSL-FO * SVG * XQUERY * XPROC * ANT * DIVERSES



XSL-FO / DocBook / Konvertierung einer XML-Datei nach HTML, FO/PDF und Docbook 5.0 / Längeres Beispiel für das Inputdokument

Längeres Beispiel für das Inputdokument

Längeres Beispiel für das Inputdokument

➪ Hier finden Sie ein Beispiel, um eine XML-Datei nach HTML, FO/PDF und Docbook 5.0 zu konvertieren.


<Artikelliste>
  <Artikel titel="Objektorientierte Programmierung in JavaScript" Datum="15.04.2012" jahr="2012"
    publish="true" id="javascript1">
    <Listing id="1" Thema="Funktions - Überladung I">
      <code id="">function funcwithparam() {</code>
      <remark idcode="1">möglicher Aufruf: funcwithparam("A", new Array("1", "2", "Hallo",
        57), "B", "C"); // null als Parameter ruft Fehler hervor.</remark>
      <code id="">writelog("funcwithparam: Anzahl Parameter: " + arguments.length);</code>
      <remark idcode="1">gibt die Anzahl der übergebenen Parameter aus, ergibt bei o.a. Aufruf
        die Anzahl 4 (eines davon ist ein Array)</remark>
      <code id="">for (var y = 0; y &lt; arguments.length; y++) {</code>
      <remark idcode="1">Schleife über alle übergebenen Parameter</remark>
      <code id="">if (arguments[y].length > 0) {</code>
      <remark idcode="1">prüft, ob eins der Parameter ein Array ist. Dabei wird auch ein
        String als Array erkannt.</remark>
      <code id="">var arr = arguments[y];</code>
      <remark idcode="1">spricht diesen Parameter als Array an. </remark>
      <code id="">for (var yy = 0; yy &lt; arr.length; yy++)
        writelog(arr[yy].valueOf());</code>
      <remark idcode="1">gibt jedes Element des Arrays aus. Ein String wird in einzelne
        Characters zerlegt.</remark>
      <code id="">}</code>
      <remark idcode="1"/>
      <code id="">else writelog(arguments[y].valueOf());}}</code>
      <remark idcode="1">gibt Parameter aus, die keine Arrays sind</remark>
    </Listing>
    <Listing id="2" Thema="Typdeklaration mit this und prototype">
      <code id="1">var Person = new Function("name", "this.Nachname=name;");</code>
      <remark idcode="1">Hier wird Person als instanziierbarer Typ deklariert. Alternative
        Deklaration: "var Person = function (name) { this.Nachname=name; };" Nachname
        definiert ein öffentlich verfügbares Property des aufrufenden Objekts.</remark>
      <code id="2">Person.prototype.getName = function () { return this.Nachname; };</code>
      <remark idcode="2">getName() gibt den Inhalt von this.Nachname zurück. Hier (bewusst
        ausserhalb der Typdeklaration Mensch) wird Person über prototype eine neue
        parameterlose Function getName zugewiesen.</remark>
      <code/>
      <remark/>
      <code id="3">var m = new Person("Sagblos");</code>
      <remark idcode="3">Hier wird das erste Objekt über den Konstruktor mit dem
        Übergabeparameter "name" und dessen Wert "Sagblos" initialisiert.</remark>
      <code id="4">writelog("Die Person heisst: " + m.getName());</code>
      <remark idcode="4">Dieses Objekt m gibt über getName() den Inhalt von this.Nachname
        zurück.</remark>
      <code id="5">writelog("Direkter Zugriff auf Nachname: " + m.Nachname);</code>
      <remark idcode="5">Da Nachname über this. ein öffentlich verfügbares Property für den
        Typ Person darstellt, kann das Objekt m direkt auf Nachname zugreifen. Das heisst,
        mit m.Nachname = "Nixlos"; könnte der Wert geändert werden.</remark>
    </Listing>
    <Listing id="3" Thema="Datenkapselung">
      <code id="1">var myhiddeninteger = function () {</code>
      <remark idcode="1">deklariert myhiddeninteger als instanziierbaren Typ.</remark>
      <code id="2">var verstecktesfeld;</code>
      <remark idcode="2"> verstecktesfeld ist eine Variable, die ausschliesslich innerhalb des
        Typs angesprochen werden kann; ausserhalb (Zeile 8) jedoch nicht. </remark>
      <code id="3">this.get = function () {return verstecktesfeld;};</code>
      <remark idcode="3">deklariert eine Lesemethode, die verstecktesfeld zurückgibt.</remark>
      <code id="4">this.set = function (val) {verstecktesfeld = parseInt( val);};</code>
      <remark idcode="4">deklariert eine Schreibmethode, mit der man verstecktesfeld einen
        Integerwert zuweisen kann.</remark>
      <code id="5">};</code>
      <remark idcode="5">Ende der Typdeklaration myhiddeninteger</remark>
      <code id="6">var hhh = new myhiddeninteger();</code>
      <remark idcode="6">hhh ist eine erste Instanz von myhiddeninteger</remark>
      <code id="7">hhh.set(95);</code>
      <remark idcode="7">hhh verwendet die Schreibmethode set, um einen Integerwert auf
        verstecktesfeld zu schreiben.</remark>
      <code id="8">//writelog(hhh.verstecktesfeld);</code>
      <remark idcode="8">die Zeile ist auskommentiert, weil der direkte Zugriff des Objekts
        hhh auf verstecktesfeld nicht möglich ist und "undefined" als Rückgabe erzeugen
        würde</remark>
      <code id="9">writelog("myhidden: " + hhh.get());</code>
      <remark idcode="9">hhh verwendet die Lesemethode get, um den gesetzten Wert 95
        zurückzugeben.</remark>
    </Listing>
    <Listing id="4" Thema="Bruchrechnung">
      <code id="1">var Bruch = function () {</code>
      <remark idcode="1">Deklaration der "Klasse" Bruch</remark>
      <code id="2">this.ZAEHLER = new myhiddeninteger();</code>
      <remark idcode="1">Implementierung der Datenkapselung für ZAEHLER über separaten Typ
        myhiddeninteger</remark>
      <code id="3">this.NENNER = new myhiddeninteger();</code>
      <remark idcode="1">Implementierung der Datenkapselung für NENNER über separaten Typ
        myhiddeninteger</remark>
      <code/>
      <remark/>
      <code id="1">this.toString = function () {</code>
      <remark idcode="1">Überschreibt die toString-Methode für Bruch</remark>
      <code id="1">this.vorzeichenwechsel();</code>
      <remark idcode="1">ruft die später deklarierte Methode vorzeichenwechsel für das
        aktuelle Objekt auf</remark>
      <code id="1">if (this.NENNER.get() === 0) return "Nenner ist 0, NICHT DEFINIERT";</code>
      <remark idcode="1">Fallbehandlung für NENNER = 0</remark>
      <code id="1">else if (this.ZAEHLER.get() === 0 || this.NENNER.get() === 1) return
        this.ZAEHLER.get();</code>
      <remark idcode="1">Fallbehandlung für ZAEHLER=0 oder NENNER=1</remark>
      <code id="1">else return this.ZAEHLER.get() + " / " + this.NENNER.get();};</code>
      <remark idcode="1">alle anderen Fälle.</remark>
      <code id="1">this.vorzeichenwechsel = function () {</code>
      <remark idcode="1">Deklaration der Methode Vorzeichenwechsel für das aktuelle
        Objekt</remark>
      <code id="1">if (this.NENNER.get() &lt; 0) {</code>
      <remark idcode="1">Fallbehandlung für NENNER &lt; 0</remark>
      <code id="1">this.ZAEHLER.set(this.ZAEHLER.get() * (-1));</code>
      <remark idcode="1">Dann Vorzeichenwechsel für ZAEHLER</remark>
      <code id="1">this.NENNER.set(this.NENNER.get() * (-1));</code>
      <remark idcode="1">und Vorzeichenwechsel für ZAEHLER</remark>
      <code id="1">};};};</code>
      <remark idcode="1">Ende if, Ende Methode Vorzeichenwechsel, Ende Deklaration für
        Bruch</remark>
      <code/>
      <remark/>
      <code id="1">Bruch.prototype.kuerzen = function () {</code>
      <remark idcode="1">kuerzt zaehler und nenner des aufrufenden Objekts durch Ermittlung
        des grössten gemeinsamen Teilers. Aus didaktischen Gründen steht kuerzen ausserhalb
        von Bruch-Deklaration; sie hätte genausogut innerhalb mit this implementiert werden
        können. </remark>
      <code id="1">var z = Math.abs(this.ZAEHLER.get());</code>
      <remark idcode="1">kopiert aktuellen Absolutwert von ZAEHLER in temporären Wert
        z</remark>
      <code id="1">var n = Math.abs(this.NENNER.get());</code>
      <remark idcode="1">kopiert aktuellen Absolutwert von NENNER in temporären Wert
        n</remark>
      <code id="1">if (z &gt; 0 &amp;&amp; n &gt; 0) {</code>
      <remark idcode="1">prüft, ob temporäre Variablen z und n in jedem Fall &gt; 0 sind;
        wichtig für die folgende While-Schleife</remark>
      <code id="1">while (z !== n) {</code>
      <remark idcode="1">Die Schleife subtrahiert z und n so lange gegeneinander, bis beide
        gleich sind. Dafür müssem z und n zwingend &gt; 0 sein.</remark>
      <code id="1">if (z &gt; n) z = z - n;</code>
      <remark idcode="1">wenn z &gt; n: subtrahiere n von z.</remark>
      <code id="1">else n = n - z; }</code>
      <remark idcode="1">sonst subtrahiere z von n. Ende While-Schleife.</remark>
      <code id="1">this.ZAEHLER.set(this.ZAEHLER.get() / z);</code>
      <remark idcode="1">Der größte gemeinsame Teiler ist in z gefunden, jetzt wird der
        aktuelle ZAEHLER-Wert durch z dividiert und das Ergebnis auf ZAEHLER
        zurückgeschrieben.</remark>
      <code id="1">this.NENNER.set(this.NENNER.get() / z);</code>
      <remark idcode="1">Analog für NENNER</remark>
      <code id="1">}};</code>
      <remark idcode="1">Ende der Kürzroutine</remark>
      <code/>
      <remark/>
      <code id="1">Bruch.prototype.subtrahiere = function (b) {</code>
      <remark idcode="1">Subtrahiert ein Bruch-Objekt vom aufrufenden Objekt, liefert als
        Ergebnis ein neues Objekt zurück (ObjectFactory), kuerzt dabei alle Objekte. Die
        Methode ist ein Beispiel für diverse andere Gestaltungsmöglichkeiten.</remark>
      <code id="1">var erg = null;</code>
      <remark idcode="1">erg wird später gefüllt</remark>
      <code id="1">if ((b instanceof Bruch)) {</code>
      <remark idcode="1">prüft, ob Parameter b eine Instanz von Bruch ist</remark>
      <code id="1">b.kuerzen();</code>
      <remark idcode="1">kürzt ZAEHLER und NENNER von b</remark>
      <code id="1">this.kuerzen();</code>
      <remark idcode="1">kürzt ZAEHLER und NENNER des aufrufenden Objekts</remark>
      <code id="1">erg = new Bruch();</code>
      <remark idcode="1">weist erg den Typ Bruch zu</remark>
      <code id="1">erg.ZAEHLER.set(this.ZAEHLER.get() * b.NENNER.get() - b.ZAEHLER.get() *
        this.NENNER.get());</code>
      <remark idcode="1">setzt ZAEHLER von erg</remark>
      <code id="1">erg.NENNER.set(this.NENNER.get() * b.NENNER.get());</code>
      <remark idcode="1">setzt NENNER von erg</remark>
      <code id="1">erg.kuerzen();}</code>
      <remark idcode="1">kürzt ZAEHLER und NENNER von erg</remark>
      <code id="1">return erg;};</code>
      <remark idcode="1">gibt erg als Objekt zurück; Ende Methode subtrahiere</remark>
      <code/>
      <remark/>
      <code id="1">// Objekte instanziieren</code>
      <remark idcode="1"/>
      <code id="1">var aa = new Bruch();</code>
      <remark idcode="1">instanziiere aa als neuen Bruch</remark>
      <code id="1">var bb = new Bruch();</code>
      <remark idcode="1">instanziiere bb als neuen Bruch</remark>
      <code id="1">aa.ZAEHLER.set(-57);</code>
      <remark idcode="1">verwende ZAEHLER-Schreibmethoden für aa</remark>
      <code id="1">aa.NENNER.set(-95);</code>
      <remark idcode="1">verwende NENNER-Schreibmethoden für aa. Die negativen Vorzeichen
        werden über Vorzeichenwechsel automatisch korrigiert.</remark>
      <code id="1">bb.ZAEHLER.set(33);</code>
      <remark idcode="1">verwende ZAEHLER-Schreibmethoden für bb</remark>
      <code id="1">bb.NENNER.set(55);</code>
      <remark idcode="1">verwende NENNER-Schreibmethoden für bb</remark>
      <code id="1">var dd = aa.subtrahiere(bb);</code>
      <remark idcode="1">hier weist aa über die Methode subtrahiere dem bislang nicht
        verwendeten Objekt dd seinen Typ und dessen Ergebniswerte zu. aa, bb und dd werden
        dabei automatisch gekürzt.</remark>
      <code id="1">writelog(aa.toString() + " - " + bb.toString() + " = " +
        dd.toString());</code>
      <remark idcode="1">Aufruf des Ergebnisses: "3 / 5 - 3 / 5 = 0"</remark>
    </Listing>
    <Listing id="5" Thema="Vererbung, Polymorphie">
      <code id="">var Fahrzeug = new Function("this.fahren = function () { return \"Jedes
        Fahrzeug faehrt\"; }; this.ausgabe = function () { return \"Ich bin ein Fahrzeug\";
        }; ");</code>
      <remark idcode="1">erzeugt Fahrzeug als Typ mit den Properties fahren und
        ausgabe</remark>
      <code id="">Fahrzeug.Auto = function () { };</code>
      <remark idcode="1">erzeugt Auto als Typ, analog zu "Fahrzeug.Auto = new
        Function();"</remark>
      <code id="">Fahrzeug.Auto.prototype = new Fahrzeug();</code>
      <remark idcode="1">weist Auto den Typ Fahrzeug zu</remark>
      <code id="">Fahrzeug.Auto.prototype.fahren = function () { return "Automobil faehrt auf
        der Strasse"; };</code>
      <remark idcode="1">überschreibt das Property fahren</remark>
      <code id="">Fahrzeug.Boot = function () { };</code>
      <remark idcode="1">erzeugt Boot als Typ, analog zu "Fahrzeug.Boot = new
        Function();"</remark>
      <code id="">Fahrzeug.Boot.prototype = new Fahrzeug();</code>
      <remark idcode="1">weist Boot den Typ Fahrzeug zu: Boot "erbt" von Fahrzeug</remark>
      <code id="">Fahrzeug.Boot.prototype.fahren = function () { return "Boot faehrt auf dem
        Wasser"; };</code>
      <remark idcode="1">überschreibt die Methode fahren für Fahrzeug.Boot</remark>
      <code/>
      <remark/>
      <code id="">var f = new Fahrzeug();</code>
      <remark idcode="1">instanziiert ein Fahrzeug-Objekt </remark>
      <code id="">var aaa = new Fahrzeug.Auto();</code>
      <remark idcode="1">instanziiert ein Fahrzeug.Auto - Objekt</remark>
      <code id="">var bbb = new Fahrzeug.Boot();</code>
      <remark idcode="1">instanziiert ein Fahrzeug.Boot - Objekt</remark>
      <code id="">writelog(f.ausgabe() + "; " + f.fahren());</code>
      <remark idcode="1">Ausgabe: "Ich bin ein Fahrzeug; Jedes Fahrzeug faehrt"</remark>
      <code id="">writelog(aaa.ausgabe() + "; " + aaa.fahren());</code>
      <remark idcode="1">Ausgabe: "Ich bin ein Fahrzeug; Automobil faehrt auf der
        Strasse"</remark>
      <code id="">writelog(bbb.ausgabe() + "; " + bbb.fahren());</code>
      <remark idcode="1">Ausgabe: "Ich bin ein Fahrzeug; Boot faehrt auf dem Wasser"</remark>
      <code/>
      <remark/>
      <code id="">writelog("bbb ist ein Object: " + (bbb instanceof Object));</code>
      <remark idcode="1">ergibt true</remark>
      <code id="">writelog("bbb ist ein Fahrzeug: " + (bbb instanceof Fahrzeug));</code>
      <remark idcode="1">ergibt true</remark>
      <code id="">writelog("bbb ist ein Fahrzeug.Boot: " + (bbb instanceof
        Fahrzeug.Boot));</code>
      <remark idcode="1">ergibt true</remark>
      <code id="">writelog("bbb ist ein Fahrzeug.Auto: " + (bbb instanceof
        Fahrzeug.Auto));</code>
      <remark idcode="1">ergibt false</remark>
    </Listing>
    <Listing id="6" Thema="Reflection">
      <code id="1">for (var p in aa) {</code>
      <remark idcode="1">Schleife über alle Properties des Bruch-Objekts aa</remark>
      <code id="1">if (aa.hasOwnProperty(p)) {</code>
      <remark idcode="1">prüft auf hasOwnProperty; nur solche Properties, die innerhalb des
        Typs z.B.(?) mit this. angelegt werden, werden hier angesprochen.</remark>
      <code id="1">writelog("OwnProperty: " + p);</code>
      <remark idcode="1">gibt den Namen des Properties aus</remark>
      <code id="1">if (p === 'ZAEHLER' || p === 'NENNER') {</code>
      <remark idcode="1">wenn der Propertyname ZAHELER oder NENNER ist</remark>
      <code id="1">writelog(" Aktueller Wert: " + aa[p].get());}</code>
      <remark idcode="1">rufe dessen get-Function auf</remark>
      <code id="1">else if (p === 'toString') {</code>
      <remark idcode="1">wenn das aktuelle Property die toString-Function ist</remark>
      <code id="1">writelog("toString: " + p + "; Result: " + aa[p].call(aa, ""));}}</code>
      <remark idcode="1">rufe diese Function auf: im übertragenen Sinn sieht es so aus, als
        würde die Methode das referenzierende Objekt aufrufen.</remark>
      <code id="1">else writelog("NOT OwnProperty: " + p);}</code>
      <remark idcode="1">listet alle Properties auf, die ausserhalb der Bruch-Deklaration mit
        prototype angelegt wurden</remark>
    </Listing>
    <Listing id="7" Thema="Der Object-Konstruktor">
      <code id="1">var einmensch = new Object();</code>
      <remark idcode="5">Hier wird ein neues Objekt "einmensch" erstellt; synonym zu "var
        einmensch = {};"</remark>
      <code id="1">einmensch.vorname = "Lotte";</code>
      <remark idcode="5">Dem Objekt werden zur Laufzeit Properties zugewiesen und mit Werten
        befüllt, hier: vorname</remark>
      <code id="1">einmensch.nachname = "Rielos";</code>
      <remark idcode="5">wie vor, hier nachname</remark>
      <code id="1">einmensch.toString = function () { return this.vorname + " " +
        this.nachname };</code>
      <remark idcode="5">überschreibe das toString-Property unter Aufruf von this</remark>
      <code id="1">writelog(einmensch.toString());</code>
      <remark idcode="5">Ausgabe: "Lotte Rielos"</remark>
      <code/>
      <remark/>
      <code id="1">for (var einmprop in einmensch){</code>
      <remark idcode="5">Auch aus diesem Objekt lassen sich zur Laufzeit alle Properties
        ansprechen (sie liessen sich auch über (einmensch.hasOwnProperty(einmprop))
        erreichen).</remark>
      <code id="1">if (einmprop === 'toString') writelog(einmprop + "; Result: " +
        einmensch[einmprop].call(einmensch, ""));</code>
      <remark idcode="5">Resultat: "toString; Result: Lotte Rielos"</remark>
      <code id="1">else writelog(einmprop + ": " + einmensch[einmprop]); }</code>
      <remark idcode="5">Resultate: "vorname: Lotte" bzw. "nachname: Rielos"</remark>
    </Listing>
    <Listing id="8" Thema="JSON, Datei: mensch.js">
      <code id="1">{"mensch":[</code>
      <remark idcode="5">deklariert eine Datenstruktur "mensch" analog eines Arrays</remark>
      <code id="1">{"nachname":"Holzflos","vorname":"Hugo"},</code>
      <remark idcode="5">bildet zwei Wertepaare, die in <k>JavaScript</k> in Properties
        "nachname" und "vorname" eingelesen und einem Objekt zugewiesen werden
        können.</remark>
      <code id="1">{"nachname":"Nixlos","vorname":"Nicole"}</code>
      <remark idcode="5">Zweiter Property-Satz für das zweite <k>JavaScript</k>-Objekt
        "mensch". </remark>
      <code id="1">]}</code>
      <remark idcode="5">Ende der Datenstruktur</remark>
    </Listing>
    <Listing id="9" Thema="JSON-basierte, automatisch generierte und befüllte Objektliste">
      <code id="1">var results = aj.getResponse("mensch.js");</code>
      <remark idcode="1">holt Werte per AJAX aus JSON</remark>
      <code id="1">var resultate = results.mensch;</code>
      <remark idcode="1">definiert die relevante JSON-Liste</remark>
      <code id="1">var arr = new Array();</code>
      <remark idcode="1">erzeugt ein Array, das bei Auswertung der JSON-Liste automatisch mit
        Objekten gefüllt wird</remark>
      <code id="1">for (var pkat in resultate) {</code>
      <remark idcode="1">Schleife über alls mensch-Einträge in der Liste</remark>
      <code id="1">if (resultate.hasOwnProperty(pkat)) {</code>
      <remark idcode="1"/>
      <code id="1">var myobj = new Object();</code>
      <remark idcode="1">erzeuge ein neues Objekt, dem im weiteren Verlauf Properties
        zugewiesen werden</remark>
      <code id="1">for (var pp in resultate[pkat])</code>
      <remark idcode="1">Schleife über alle Properties des jeweiligen Listeintrags</remark>
      <code id="1">if (resultate[pkat].hasOwnProperty(pp))</code>
      <remark idcode="1"/>
      <code id="1">myobj[pp] = resultate[pkat][pp];</code>
      <remark idcode="1">deklariere Property-Name (z.B. vorname) und Inhalt (z.B. Nicole) für
        das aktuelle Objekt</remark>
      <code id="1">arr.push(myobj);}}</code>
      <remark idcode="1">Füge das gebildete Objekt in das Array</remark>
      <code/>
      <remark/>
      <code id="1">writelog("arr.length: " + arr.length);</code>
      <remark idcode="1">gibt die Objektanzahl im Array aus</remark>
      <code/>
      <remark/>
      <code id="1">writelog("EINZELZUGRIFF: " + arr[2]["nachname"]);</code>
      <remark idcode="1">greift auf ein konkretes Objekt im Array zu, gibt Inhalt des
        Propertys nachname aus </remark>
      <code id="1">writelog("EINZELZUGRIFF: " + arr[2].nachname);</code>
      <remark idcode="1">wie vorher, andere Schreibweise</remark>
      <code/>
      <remark/>
      <code id="1">for (var yy = 0; yy &lt; arr.length; yy++) {</code>
      <remark idcode="1">Schleife über jedes Objekt im Array</remark>
      <code id="1">for (var pr in arr[yy]) writelog(pr + ": " + arr[yy][pr]);}</code>
      <remark idcode="1">gibt alle Properties und deren Inhalte des aktuellen Objekts
        aus.</remark>
    </Listing>
    <top titel="Zusammenfassung" id="">
      <utop>Dieser Beitrag zeigt anhand diverser Beispiele, wie die Ansätze der klassischen
        objektorientierten Programmierung (Datenkapselung, Vererbung, Polymorphie,
        Reflection) im Wesentlichen und im übertragenen Sinn auch mit <k>JavaScript</k>
        umsetzbar sind.</utop>
    </top>
    <top titel="Einführung" id="">
      <utop><k>JavaScript</k> ist seit etlichen Jahren eine browserseitige Programmiersprache,
        die diverse Programmteile auf den Client verlagert - mit unterschiedlicher
        Unterstützung der Sprache durch die Browseranbieter. In vielen Fällen reduziert sich
        die Anwendung auf die clientseitige Formularvalidierung, bevor ein Request zum
        Server abgeschickt wird. Oder der <k>JavaScript</k>-Einsatz konzentriert sich auf
        eines der zahlreichen Frameworks, deren Einsatz bequemer erscheinen mag als die
        Anwendung der Standardsprache. </utop>
      <utop>Spätestens mit AJAX hat sich die Bedeutung der Sprache verstärkt. In
        wirkungsvollem Zusammenspiel mit (X)HTML CSS - auch und gerade in deren neueren
        Standards - bietet <k>JavaScript</k> äußerst effiziente Möglichkeiten, u.a. per
        DOM-Programmierung beträchtliche Programm- bzw. Businesslogiken auf den Browser zu
        übertragen. </utop>
      <utop>Etliche Webseiten setzen sehr nachhaltig auf clientseitiges <k>JavaScript</k>
        (wobei auch vielversprechende Ansätze zu einer serverseitigen Anwendung von
        <k>JavaScript</k> vorhanden sind). Der Trend zeichnet sich ab, dass die Server sich
        in Webanwendungen verstärkt auf die notwendigsten Aufgaben des qualifizierten
        Datenaustausches mit den Clients konzentrieren.</utop>
      <utop>Die Bedeutung der Sprache steigt, obwohl ihr Ruf lange Zeit nicht der beste war.
        Zu Unrecht? Möglicherweise war ihre enorme Flexibilität zu wenig bekannt. Darüber
        kann man spekulieren. Was die objektorientierte Programmierung betrifft, lassen
        viele Developer sich möglicherweise durch die Tatsache abschrecken, dass es keine
        klassenbasierte Typdeklaration gibt, wie sie es aus Java, C#.NET, PHP oder C++
        gewohnt sind. Da erschien es bequemer, mit GWT Java zu schreiben und damit
        <k>JavaScript</k> zu erzeugen.</utop>
      <utop>Ein anderer Punkt mag sein, dass der Programmierer in <k>JavaScript</k> nicht
        grundsätzlich zu typsauberem Coding gezwungen wird. Das verlockt dazu, etwaige
        Datenkonvertierungen weitgehend dem System zu überlassen, das im Regelfall
        hoffentlich schon das Richtige tun wird. </utop>
      <utop>Umso mehr, als in <k>JavaScript</k> praktisch alles ein Objekt ist (von primitiven
        Typen einmal abgesehen). Dabei treibt das System mitunter merkwürdige Blüten. So
        werden Leerstrings "" auch mal als 0 erkannt - was sich durch Anwendung typsauberer
        Operatoren wie ===, &lt;==, &gt;==, !== wieder ausbügeln lässt.</utop>
      <utop>Bei der Deklaration vieler instanziierbarer Typen spielen Functions eine
        wesentliche Rolle (auch wenn es Wege gibt, darauf zu verzichten). Bereits hier gibt
        es unterschiedliche Wege zur effizienten Deklaration. Die
        <k>function tuwas() {
          alert('Hallo'); }</k> lässt sich ebenso
        definieren als
        <k>var tuwas = function()
          { alert('Hallo'); };</k> oder als
        <k>var tuwas = new
          Function("alert('Hallo');");</k>. Keiner
        dieser Varianten lässt sich jedoch im Sinn "var m = new tuwas();" als Objekt
        instanziieren, es fehlt die innere Zuweisung von Properties per <k>this</k> bzw.
        <k>prototype</k>.</utop>
      <utop>Auch die Funktionsüberladung erscheint weit flexibler (wenn auch nicht ebenso
        typsauber) umsetzbar wie etwa in Java. Zwar macht eine mehrfache Deklaration einer
        Function wenig Sinn: eine zweite Function desselben Namens würde die erste
        überschreiben. Daher implementiert man die Function nur einmal, auch völlig ohne
        Parameter: dennoch ist sie mit beliebig vielen Parametern aufrufbar, die über
        "arguments" einzeln ansprechbar sind.</utop>
      <utop>Das funktioniert auch dann, wenn eines oder mehrere Parameter aus kompletten
        Arrays oder komplexen Objekten irgendwelcher Typen ("Klassen") besteht.
        Weitergehende Typprüfungen mit typeof, instanceof, parseInt können dann innerhalb
        der Function implementiert werden. - Ist das flexibel genug? Vermutlich ja.
        Jedenfalls bieten sich hier ideale Bedingungen, Functions für alles mögliche zu
        implementieren. Ob die dann noch wartbar sind, bleibt abzuwarten.</utop>
    </top>
    <top titel="Typdeklaration mit this und prototype" id="">
      <utop>Ähnlich flexibel lassen sich auch die Ansätze der objektorientierten
        Programmierung mit <k>JavaScript</k> umsetzen. Wenn man von der aus C++, Java, oder
        C# gewohnten klassenbasierten Definition abstrahiert, findet man im Function-Ansatz
        mit <k>prototype</k> und <k>this</k> ein flexibles, vielleicht erfrischendes
        Gegenstück. Zumal die Instanziierung mit 'var m = new Person("Sagblos");' und die
        weitere Nutzung durch den "Methodenaufruf" im Wesentlichen geläufig erscheinen
        dürfte.</utop>
      <Listing ref="2"/>
      <utop>Mit "this" (im Konstruktoraufruf) und "prototype" (ausserhalb des Konstruktors)
        können öffentlich verfügbare Properties definiert werden, die jeweils
        unterschiedliche Eigenschaften (Array, Function, komplexer oder primitiver Typ, ...)
        aufweisen und daher ihrerseits recht komplex werden mögen. Diese Properties können
        auch vererbt und dabei überschrieben werden. </utop>
      <utop>So flexibel das auch erscheinen mag: Der Schutzmechanismus der Datenkapselung
        fehlt noch, der für objektorientierte Programmierer von zentraler Bedeutung ist.
        Dabei geht es im Kern darum, dafür zu sorgen, dass auf bestimmten Feldern nur klar
        definierte Werte zulässig sind. Etwa, dass Zahlenwerte nicht negativ sein dürfen
        oder dass Strings einem bestimmten Pattern entsprechen müssen. </utop>
      <utop>Zu diesem Zweck werden in anderen OO-Sprachen Zugriffsmodifizierer definiert, auf
        die via Getter lesend und via Setter schreibend zugegriffen wird. Im Setter wird
        üblicherweise der Schutzmechanismus implementiert. </utop>
    </top>
    <top titel="Datenkapselung" id="">
      <utop>Leider gibt es in <k>JavaScript</k> keine Zugriffsmodifizierer wie private /
        protected / public. Felder, die mit prototype oder this. deklariert werden, stehen
        allgemein zur Verfügung (public). Um dennoch den Schutzmechanismus der
        Datenkapselung aufrecht zu erhalten, muss man sich anderer Verfahren bedienen. Es
        reicht definitiv nicht aus, die zu schützenden Felder mit var statt this / prototype
        zu definieren, da dieses Verfahren nicht instanzsicher ist.</utop>
      <utop>Clojures sind ein möglicherweise umständliches, dafür aber probates Mittel, um das
        Problem der Datenkapselung zu lösen (Listing 3). Freilich wird es recht aufwendig,
        für mehrere geschützte Felder einer "Klasse" jeweils ein Clojure mit Getter und
        Setter anzulegen. </utop>
      <Listing ref="3"/>
      <utop>Da erscheint es weit effizienter, die Datenkapselung für jedes geschützte Feld mit
        allen getter- und Setter-Methoden in einen separaten Typ auszulagern, der eine
        spezielle Clojure implementiert (z.B. myhiddeninteger). Dort kann das geschützte
        Feld einmalig gekapselt und via get und set verfügbar gemacht werden. Nebenbei kann
        man sich auf diese Weise die mühsame Schreibarbeit sparen, für jedes geschützte Feld
        jeweils ein getter und setter anzulegen. Im eigentlichen Property wird der
        Clojure-Typ instanziiert; damit kann Getter- bzw. Setter des Clojures gezielt
        aufgerufen werden. </utop>
      <utop>Listing 4 stellt einen etwas komplexeren Typ zur Bruchrechnung dar, der die beiden
        Properties ZAEHLER und NENNER mithilfe von myhiddeninteger kapselt. Um nochmals die
        Flexibilität der Typdefinition zu demonstrieren, wurden zwei weitere Properties
        (toString, vorzeichenwechsel) ebenfalls mithilfe von this innerhalb des Typs
        definiert. Andere Properties wie kuerzen und subtrahiere wurden bewusst ausserhalb
        mit prototype definiert. Dennoch rufen die dahinter liegenden Functions sich
        gegenseitig auf. Subtrahiere ist zudem so designt, dass hier ein weiteres
        Bruch-Objekt generiert wird.</utop>
      <Listing ref="4"/>
    </top>
    <top titel="Vererbung, Polymorphie" id="">
      <utop>Listing 5 definiert den Typ "Fahrzeug" mit den Properties "fahren" und "ausgabe".
        In den "erbenden" Typen "Fahrzeug.Auto" und "Fahrzeug.Boot" wird "fahren"
        überschrieben, "ausgabe" aber beibehalten. Nun wird für jeden Typ eine eigene
        Instanz erzeugt, die jeweils beide Properties aufruft. Während "ausgabe" für jedes
        Objekt unverändert bleibt, wird "fahren" polymorph aufgerufen, abhängig von der
        typabhängigen Implementierung.</utop>
      <Listing ref="5"/>
      <utop>Interessant ist auch der Aufruf von "instanceof": ein als Fahrzeug.Boot
        deklariertes Objekt bbb wird zur Laufzeit sowohl als Object, als Fahrzeug sowie als
        Fahrzeug.Boot erkannt - nicht jedoch als "Boot", und selbstredend auch nicht als
        "Fahrzeug.Auto.". Dagegen gibt "typeof(bbb)" lediglich "object" zurück.</utop>
      <utop>Dieser Ansatz erlaubt, selbst definierte Typen in Kategorien zusammenzufassen, die
        fachlich und sachlich zusammen gehören. Wenn die objektorientierte Programmierung in
        <k>JavaScript</k> - wie erwartet - weitere zunehmende Bedeutung erlangt, scheint
        eine bessere Strukturierung der Codes im Sinn der Java-Packages, der Namespaces
        unter .NET bzw. XML sehr empfehlenswert.</utop>
    </top>
    <top titel="Reflection" id="">
      <utop>Nützlich habe ich gefunden, dass sich alle Properties eines Objekts zur Laufzeit
        einzeln ansprechen und auch bedingungsgesteuert aufrufen lassen (Listing 6). Mit
        einiger Fantasie kann man das aufrufende Objekt vielleicht als Array aus Properties
        betrachten, deren Elemente in einer Schleife angesprochen werden können. Das ist
        brauchbar beim Abarbeiten von Objektlisten (Arrays), deren Einzelobjekte
        verschiedenen Typs sind, bei denen bestimmte Methoden polymorph aufgerufen werden
        sollen.</utop>
      <Listing ref="6"/>
      <utop>Ebenso brauchbar ist "instanceof", um eine Vererbungshierarchie zu überprüfen: bbb
        ist sowohl ein Object als auch ein Fahrzeug als auch ein Fahrzeug.Boot (vgl. Listing
        5). Zudem lässt sich über "hasOwnProperty" prüfen, ob das jeweilige Property geerbt
        wurde. Für die Aufrufbarkeit des Propertys ist es ohne Bedeutung.</utop>
    </top>
    <top titel="Objekt- Deserialisierung" id="">
      <utop>Freilich gibt es grundsätzlich noch einfachere Wege, objektorientiert zu arbeiten.
        Es ist gar nicht erforderlich, zuerst aufwendig einen komplexen Typ im Sinn einer
        Klasse zu deklarieren und dann über var x = new Type(); zu instanziieren. Objekte
        lassen sich einfach mit dem Object-Konstruktor instanziieren, und ebenso einfach
        lassen sich diesem Objekt zur Laufzeit diverse (public-) Properties zuweisen.
        Selbstverständlich gilt das auch für Functions wie der toString-Function. Es handelt
        sich dabei um vollwertige Objekte, deren Properties zur Laufzeit angesprochen bzw. -
        als functions- aufgerufen werden können.</utop>
      <Listing ref="7"/>
      <utop>Dieses Prinzip lässt sich effizient bei JSON- basiertem AJAX verwenden (Listing
        8). Die in JSON definierte Datenstruktur beinhaltet sämtliche Informationen, die man
        zur automatischen Instanziierung von Objekten und deren Properties (sowie deren
        inhaltlicher Zuweisung) benötigt. Listing 9 demonstriert das Vorgehen. Für jeden
        mensch-Eintrag in JSON wird ein neues Objekt generiert; aus jedem relevanten
        JSON-Wertepaar wird in diesem Objekt ein Property gebildet und inhaltlich befüllt.
        Schliesslich wird das Objekt in ein vorher definiertes Array eingefügt, aus dem es
        dann einzeln (direkt per Index oder allgemein per Schleife) angesprochen werden
        kann.</utop>
      <Listing ref="8"/>
    </top>
    <top titel="Ein paar Worte zu JSON" id="">
      <utop>Es scheint überflüssig, daran zu erinnern, dass die <k>JavaScript</k> Object
        Notation (JSON) im Kern <k>JavaScript</k>-Objekte definiert. Das gültige
        JSON-Dokument muss per eval() auslesbar sein. Solange eval() keine Fehler aufwirft,
        scheint demnach alles in Ordnung zu sein. Hier sollte die Frage erlaubt sein, wie
        flexibel bzw. tolerant eval() bei der Interpretation von JSON-Dokumenten ist.</utop>
      <utop>Solange die JSON-Felder im Grunde genommen Datensätze sind, handelt es sich
        vermutlich um recht einfache Datenstrukturen, bei denen keine ernsthaften Fehler zu
        erwarten sind. Da JSON aber auch in anderen Sprachen eingesetzt wird und zum
        Datenaustausch zwischen Anwendungen dient, sind die Techniken zur Generierung der
        (komplexeren) Dokumente vielfältig. Da können Programmierfehler nicht ausgeschlossen
        werden.</utop>
      <utop>Beispielsweise könnte es vorkommen, dass ein Listing mehrerer gleichnamiger Felder
        nicht als Array ausgegeben, sondern sequenziell hintereinander weggeschrieben wird.
        Etwa so: {"nachname":"Nixlos", "vorname":"Nicole", "vorname":"Nina"}. In eval()
        werden keine Fehler gemeldet, also fällt das gar nicht auf. eval() "schluckt" den
        ersten Vornamen "Nicole" und reduziert die Vornamenliste auf "Nina". JSONLint
        (http://jsonlint.com/) verfährt analog und attestiert abschließend "Valid JSON". Der
        JSON Formatter &amp; Validator (http://jsonformatter.curiousconcept.com/) erkennt
        dagegen sogar beide Vornamen "Nicole" und "Nina" und bestätigt mit "Validation
        Result: Valid". Ein JSON-Schema (analog XSD), das die Dokumentstruktur überprüfen
        würde, gibt es nicht. Und wenn das JSON-Dokument mehrere Megabyte umfasst, dann wird
        kaum ein Developer im Dokument auf Fehlersuche gehen.</utop>
      <utop>Wenn - wie bei Listing 9 - die weitere Logik darin besteht, das JSON-Dokument in
        mehrere Objekte zu überführen, deren Properties zur Laufzeit aus den JSON-Feldnamen
        generiert und aus deren Inhalten befüllt wird, dann wäre das Ergebnis "Nina Nixlos";
        "Nicole" würde überschrieben - ohne jegliche Fehlermeldung. Vermutlich würde der
        Fehler erst gefunden, wenn "Nicole Nina" sich über ihren unvollständigen Vornamen
        aufregt.</utop>
      <Listing ref="9"/>
      <utop>Damit betreten wir das weite Feld mangelnder Datenqualität, die übrigens auch
        durch Validierungen des Inputs (analog XML-Schema-Validierung) nicht restlos
        überprüft werden kann (sofern davon bei JSON überhaupt eine Rede sein kann).
        Angenommen, ein <k>JavaScript</k>-Objekt hat ein MUSS-Property "Alter", das seine
        Daten aus dem JSON-Dokument (Feld: jsAlter) bezieht. Wenn "jsAlter" aber im
        JSON-Schema des Inputstreams nur optional definiert wäre, würde sein Fehlen nicht
        auffallen. Eine leistungsfähiges Objekt-Mapping müsste diesem Umstand Rechnung
        tragen. </utop>
      <utop>Ein anderes Beispiel, das den konzeptionellen Unterschied zwischen JSON und XML
        verdeutlicht: XML ist reihenfolgeorientiert, JSON ist Array-orientiert. JSON ist
        höchstens im einfachsten Fall gleichzusetzen mit einem "schlanken" XML ohne spitze
        Klammern und schliessende Tags, dafür mit {}, [], Kommata und ":".
        Standard-XSLT-Stylesheets, die XML nach JSON transformieren, mögen aus optional
        alternierenden Elementen zwei JSON-Arrays generieren; der sachliche Zusammenhang der
        Information ginge jedoch verloren (Listing 10). </utop>
      <utop>Wie es aussieht, ist die Function eval() ebenso hochflexibel wie <k>JavaScript</k>
        selber. Daher taugt sie auch nur bedingt als Prüfkriterium für eine saubere
        Datenqualität von JSON-Dokumenten. JSON ist auch kein Webservice, der durch eine
        Description eine Kontrolle des Dokuments und der Datenstruktur ermöglicht. Das
        sollte man berücksichtigen, bevor man sich auf ein vermeintlich problemloses
        Objekt-Mapping zwischen unterschiedlichen Anwendungen via JSON freut.</utop>
    </top>
    <top titel="Fazit" id="">
      <utop>Wer <k>objektorientierte</k> gleichsetzt mit <k>klassenbasierter</k>
        Programmierung, mag in <k>JavaScript</k> keine vollwertige Alternative zur
        klassenbasierten Deklaration in Java, C#.NET, C++, ... zu erkennen. Die dort
        entwickelten Konzepte sind weit ausgereifter und vor allem typsicherer als in
        <k>JavaScript</k>. Zudem ist <k>JavaScript</k> mit einer Vielzahl globaler
        Variablen, Objekte, Functions etc. fast überladen, die mindestens ebenso
        problematisch wie flexibel sind. Denn globale Variablen lassen sich leicht -
        unbeabsichtigt oder nicht - überschreiben, und das stellt durchaus ein
        Sicherheitsrisiko dar (das sich andererseits auch wieder mit
        <k>JavaScript</k>-Bordmitteln objektorientiert ausschalten lässt).</utop>
      <utop>Aber die beträchtliche Flexibilität der Sprache <k>JavaScript</k>, die sich in
        unterschiedlichen Browsern bewähren muss (deren Hersteller die
        <k>JavaScript</k>-Standards nicht einheitlich unterstützen), rechtfertigt bereits
        ihre Existenz. Und dass diese Flexibilität erlaubt, die wesentlichen Konzepte der
        Objektorientierung nachzubilden, ohne dass der Entwickler sich allzusehr verbiegen
        muss, ist eine respektable Leistung. </utop>
      <utop>Neben herkömmlichen Browsern gibt es zusätzliche, neue Anforderungen im
        Webdevelopment. Auch für die Vielzahl mobiler Clients stellt sich die Frage nach
        einer leistungsfähigen Sprache, die einen Teil der Businesslogik übernehmen kann. Da
        <k>JavaScript</k> Performance und Flexibilität bereits hinreichend bewiesen hat, ist
        sie ein sehr ernstzunehmender Kandidat für weitere Implementierungen. </utop>
    </top>
  </Artikel>
  <Artikel titel="Software - Qualität" Datum="16.03.2014" jahr="2014" publish="true"
    id="softwarequalitaet_grupe_training">
    <top titel="Zusammenfassung">
      <utop>Kaum ein Informatiker würde ernsthaft behaupten wollen, dass Software generell
        einwandfrei arbeitet. Zu groß sind die wirtschaftlichen Schäden, die jedes Jahr auf
        fehlerhafte Software zurückgehen. </utop>
      <utop>Trotz etlicher Normen und allgemein anerkannter Verfahren ist fehlerfreie Software
        ein Ideal, das längst nicht erreicht ist. Dieser Beitrag listet einige bekannte
        Stolpersteine auf, an denen Projekte scheitern können.</utop>
    </top>
    <top titel="Planungsqualität">
      <utop>Eine solide Planung ist Grundvoraussetzung für das Gelingen jedes Vorhabens, nicht
        nur in der Softwareentwicklung. Daher gehört neben einer detaillierten Analyse der
        Problemstellung auch ein sinnvolles und wohldurchdachtes Konzept, wie die gestellte
        Aufgabe gelöst werden soll. </utop>
      <utop>Das gilt ebenso für einmalige Projekte wie für wiederkehrende, gleichartige
        Aufgaben, die zwar kundenindividuell, aber dennoch nach einem Basiskonzept umgesetzt
        werden müssen. Ohne behaupten zu wollen, dass man sich bei einmaligen Aufgaben mehr
        Fehler erlauben dürfte als bei wiederkehrenden, gleichartigen, sieht es doch so aus,
        dass die Pflicht zur peinlichen Sorgfalt bei wiederkehrenden Aufgaben deutlicher zu
        Tage tritt. </utop>
      <utop>Einer der zahlreichen Aspekte für den wirtschaftlichen Erfolg des Projekts ist die
        exakte Schätzung des Zeitaufwands. Zwar stehen ausgefeilte Netzplantechniken bereit,
        um alle erforderlichen Vorgänge und deren Abhängigkeiten, kritischer Wege,
        Meilensteine und den Bedarf an Personal und Ressourcen detailliert zu planen.
        Theoretisch ist es also möglich, auf der Basis der Planung exakte
        Fertigstellungstermine zu berechnen. </utop>
      <utop>Freilich hält sich die Realität nicht immer an jene Annahmen. Der tatsächliche
        Zeitbedarf einzelner Vorgänge kann deutlich größer ausfallen als geplant. Aufgrund
        von Vorgangsverknüpfungen kann der Liefertermin gefährdet sein. Um diesen dennoch
        halten zu können, werden ursprünglich fest eingeplante Leistungsphasen verkürzt.
        Nacheinander fallen die <k>Dokumentation</k> und Teile des Testings dem Zeit- bzw.
        Kostendruck zum Opfer. </utop>
      <utop>Beispiele, in denen Projekte in einem Desaster endeten, sind überreich vorhanden.
        Unzureichend analysierte Problemstellungen, untaugliche oder unklar formulierte
        Lösungsansätze, fehlerhafte Aufwands-, Zeit- und Kostenschätzungen durchziehen
        manches Projekt folgenreich bis zum Ende. Kleine Planungsfehler entfalten mitunter
        verheerende Wirkungen.</utop>
      <utop>Auch auf der Kundenseite bestehen nicht immer klare Vorstellungen, was genau
        implementiert werden soll. Dominante Persönlichkeiten bringen mitunter Ideen ein,
        die bei näherer Betrachtung verbesserungsfähig sind. Nachträgliche Umplanungen im
        laufenden Projekt können auf suboptimalen Kompromissen beruhen, die die
        Planungsqualität kaum erhöhen.</utop>
    </top>
    <top titel="Qualität der Dokumentation">
      <utop>Wichtig für die Nutzung, aber auch für die Wartbarkeit einer Software ist ihre
        <k>Dokumentation</k>. Sie dient ebenso zu Nachweiszwecken über erbrachte Leistungen
        wie auch dem Know-How-Transfer zwischen den diversen Beteiligten eines Projekts,
        etwa wenn unterschiedliche Developer den Quellcode warten oder
        weiterentwickeln.</utop>
      <utop>Eine gute <k>Dokumentation</k> besteht einerseits aus separaten Dokumenten wie
        Spezifikation, Testdokumentation oder Glossar. Anderersites besteht sie aus
        Kommentaren im Sourcecode, die die Bedeutung bestimmter Variablen erläutern, oder
        die erklären, warum eine bestimmte Logik angewendet wurde. Obwohl es für den Aufbau
        von Software-<k>Dokumentationen</k> Normen gibt, ist die Praxis teilweise
        ernüchternd.</utop>
      <utop>Es beginnt damit, dass nicht alle Fachplaner in der Lage sind, eindeutig und
        unmißverständlich darzulegen, was genau das Ziel der Anwendung ist und welche
        Features erforderlich sind. Diesbezügliche Rückfragen der Entwickler mögen wohl
        Klärung schaffen; jene Zusatzinformationen werden jedoch nicht immer konsequent in
        der <k>Dokumentation</k> festgehalten. Das ist spätestens dann problematisch, wenn
        die Informationen nur im Kopf des Entwicklers stehen und dieser nach einiger Zeit
        das Unternehmen verlässt.</utop>
      <utop>So stimmen nach Fertigstellung eines Projekts die <k>Dokumentation</k> und der
        Sourcecode nicht immer überein. Für einen neuen Entwickler, der in ein bestehendes
        Projekt einsteigen soll, kommt viel darauf an, sich schnell zurecht zu finden. Wenn
        die Arbeit jedoch - wegen fehlender oder qualitativ schlechter <k>Dokumentation</k>
        - mit einer zeitintensiven Forschungstätigkeit beginnt, kann der Liefertermin in
        Gefahr geraten.</utop>
      <utop>Aber auch übernehmende Entwickler dokumentieren ihre aktuellen
        Recherche-Ergebnisse keineswegs immer. Und da der Liefertermin drängt, ist für eine
        ausführliche <k>Dokumentation</k> sowieso keine Zeit. Die Abweichungen Code /
        <k>Dokumentation</k> werden nicht behoben, sondern verschärfen sich möglicherweise
        noch.</utop>
      <utop>Selbst wenn die <k>Dokumentation</k> erfolgt, ist sie nicht immer hilfreich.
        Teilweise wird sie in einem Umfang geführt, die ein intensives Studium der weit
        verzweigten Erläuterungen, Links und Hinweise erfordern. Wohl dem, der sofort weiss,
        wo er mit seiner Arbeit ansetzen muss!</utop>
      <utop>Andere <k>Dokumentationen</k> sind nicht hilfreich, weil sie nur erklären, was aus
        der nächsten Quelltextzeile ohnehin klar hervorgeht ("// Loop über ...") oder wenn
        der Developer sich in Nebensächlichkeiten verliert, die Erläuterung von
        Grundgedanken und Leitideen jedoch unterlässt.</utop>
      <utop>Wieder andere <k>Dokumentationen</k> fallen durch Kürze und Knappheit auf. In
        möglichst nur drei Worten wird die gesamte Komplexität von mehreren Tausend
        Quelltextzeilen ausgedrückt. Diese drei Worte mögen zwar präzise sein; hilfreich
        sind sie nicht, wenn die Komplexität der Problemstellung unbekannt ist. - Hilfreich
        ist auch nicht, Variablen, Programme oder Aliasse nicht mit sprechenden Namen zu
        benennen, sondern mit "a", "x" oder "hugo" - natürlich ohne jegliche Erläuterung. </utop>
      <utop>Bedenklich ist der Ansatz, zuerst alles zu implementieren und die
        <k>Dokumentation</k> hinterher zu machen. Zwar ist eine systematische Syntax
        hilfreich, die erlaubt, dass Tools wie Javadoc aus dem Quellcode automatisch ein
        übersichtliches Dokument erzeugen. Dieses ersetzt aber weder eine solide,
        überschaubare Spezifikation noch fehlende Kommentare im Sourcecode.</utop>
      <utop>Überproportional teuer wird eine fehlende oder qualitativ schlechte
        <k>Dokumentation</k> bei hoher Fluktuationsrate der Mitarbeiter. Während ein
        Entwickler, der seine Gedanken und Konzepte schriftlich niederlegt, dazu wenige
        Minuten braucht, benötigt jemand, der sich ohne diese Hilfe in die Thematik einlesen
        muss, dazu mehrere Stunden bis Tage. Mit Hilfe einer guten <k>Dokumentation</k> kann
        er das in einem Bruchteil der Zeit schaffen. Damit liegt der Zeitvorteil einer guten
        <k>Dokumentation</k> bei einem Vielfachen jener kurzen Dauer, die nötig ist, sie zu
        erstellen.</utop>
      <utop>Beim Entwickeln in großen Teams ist hilfreich, sämtliche separaten Dokumente mit
        Hilfe eines leistungsfähigen Dokument-Managementsystems zentral abzuspeichern. Die
        Leistungsfähigkeit jener Software gehört zur Dokumentationsqualität. Systeme, bei
        denen wesentliche Informationen gelöscht werden müssen, um eine bestimmte Systematik
        einzuhalten, sind hier wenig geeignet.</utop>
    </top>
    <top titel="Testen">
      <utop>Professionelles <k>Testen</k> ist wichtig, um Fehler zu finden, bevor der Kunde
        sie findet und moniert. Ob das allgemein große Vertrauen in automatisierte Testings
        auch gerechtfertigt ist, sei dahin gestellt. </utop>
      <utop>Entscheidend für den Testerfolg ist eine hinreichende Anzahl qualitativ
        unterschiedlicher Testszenarien und Sonderfälle. Deren sorgfältige Planung und
        Auswahl hängt weitgehend von dem jeweiligen Entwickler ab. Beauftragt man mehrere
        Tester, so erhält man ggf. mehrere unterschiedliche Ergebnisse. Die Übersicht der
        Anforderungen für die Gesamtstrategie der Tests wird mit steigender Projektgröße
        zusätzlich vernebelt.</utop>
      <utop>Nicht immer ist eindeutig klar, wie viele qualitativ unterschiedliche
        Testszenarien nötig sind, um sicher zu sein, dass die Software ausreichend getestet
        wurde. Wiederholungen desselben Tests sind überflüssig. Es macht wenig Sinn,
        hundertmal denselben Testfall durchzuspielen und hinterher zu behaupten, man habe
        ausreichend getestet. </utop>
      <utop>Zwar lassen sich Testdaten und Testfälle automatisch generieren. Derlei Verfahren
        sind unabdingbar, etwa um sicherstellen zu können, dass ein Prozessor grundsätzlich
        exakt arbeitet - und nicht in äußerst seltenen Fällen für ganz bestimmte Input-Daten
        fehlerhafte Ergebnisse berechnet. </utop>
      <utop>Die Qualität der generierten Testdaten hängt wesentlich von jener Software ab, die
        sie erzeugen. Wenn der Algorithmus ganz seltene Problemfälle nicht generiert, dann
        wird der Fehler nicht durch <k>Testen</k> entdeckt, sondern bereitet dafür später
        Probleme im Produktivbetrieb.</utop>
      <utop>So sind <k>Performance-</k>, <k>Last-</k>, <k>Regressions-</k> und
        <k>Komponententests</k> weitgehend automatisierbar. Was nicht heisst, dass sie in
        der Praxis auch durchgeführt werden. Nicht selten verzichtet man auf
        Komponententests; dadurch werden Fehler in den Einzelkomponenten (und deren
        Folgefehler) schwer auffindbar. </utop>
      <utop>Dagegen werden <k>Funktionstests</k> häufig manuell durchgeführt - mitunter ganz
        am Schluss, nachdem alles fertig programmiert wurde. Zudem kommt es vor, dass die
        Testumgebung nicht der späteren Produktivumgebung entspricht, was die Qualität der
        Testergebnisse generell in Frage stellt.</utop>
      <utop><k>Penetrationstests</k>, die zur Aufdeckung von Sicherheitsmängeln in der
        erstellten Software sowie im gesamten System dienen, werden bei einem beträchtlichen
        Anteil aller Projekte überhaupt nicht durchgeführt. Überhaupt sind
        Sicherheitsaspekte kein integraler Bestandteil bei etlichen Tests.</utop>
    </top>
    <top titel="Qualität der Implementierung">
      <utop>Auf den ersten Blick sind Qualitätsprobleme bei der Implementierung gar nicht
        vorstellbar. Syntaxfehler werden spätestens vom Interpreter oder Compiler gemeldet.
        Logische Fehler fallen spätestens beim Test auf. Profiler kommen bei der
        Performanceanalyse zum Einsatz. Versionierungssysteme unterstützen bei der
        Entwicklung im Team. </utop>
      <utop>Und dennoch: unterschiedliche Zugriffsrechte auf Datenbanken oder aktuelle
        Software schränken effizientes Arbeiten ein. Nur wenige Mitarbeiter haben Zugriff
        auf das gesamte benötigte Equipment. Nur diese Wenigen beherrschen das System so
        weit, dass sie in kritischen Situationen eingreifen können. Auch unterschiedliche
        Wissensstände (Programmierer mit geringer Erfahrung kosten deutlich weniger) tragen
        nicht dazu bei, angestrebte Qualitätsstandards einzuhalten. </utop>
      <utop>Je näher der Abgabetermin, desto größer die Hektik und desto geringer die
        Sorgfalt. Dokumentiert wird nicht mehr, Tests fallen aus - keine Zeit. Die
        Deckungsgleichheit zwischen Spezifikation und Implementierung geht zunehmend
        verloren. Nicht selten werden Programmteile, an denen wochenlang getüftelt wurde, in
        letzter Minute auf den Server geschoben.</utop>
      <utop>Features, die bei hektisch durchgeführten Abschlusstests immer noch Probleme
        verursachen, werden kurzerhand gelöscht. Wenn das auch nichts mehr hilft, verzichtet
        man auf störende Tests. Somit wissen weder die Kunden noch die Entwickler genau,
        welche Codeteile im Produktivbetrieb tatsächlich wirksam sind.</utop>
    </top>
    <top titel="Datenqualität">
      <utop>Eine wichtige Grundlage für die Funktionstüchtigkeit einer Software ist die
        Qualität der Daten, mit denen sie arbeitet. Diese Daten können entweder bereits
        vorliegen, sie können neu eingehen, etwa durch B2B-Interaktion, oder sie können
        durch Programme generiert werden. </utop>
      <utop>Um effizient damit arbeiten zu können, müssen jene Daten korrekt und verfügbar
        sein. Sehen wir mal von Vorfällen ab, in denen eine Datenbank oder ein Server
        vorübergehend den Dienst aufgekündigt hat, so ist die Bereitstellung etlicher
        Terabytes an Daten technisch kein Problem. </utop>
      <utop>Korrekte Daten müssen aktuell und vollständig sein. Sie sind eine wesentliche
        Grundlage für effizientes Arbeiten. Kontaktadressen zum Kunden, zuverlässige
        Stammdaten sollten selbstverständlich sein. Nur auf der Basis einer aktuellen und
        vollständigen Datenlage lassen sich komplexe Geschäftsmodelle implementieren oder
        Managemententscheidungen von weittragender Bedeutung treffen.</utop>
      <utop>Ein zentraler Qualitätsmaßstab für die Korrektheit der Daten sind exakt definierte
        Datenstrukturen, die den Zeichensatz, den Datentyp der jeweiligen Felder sowie
        etwaige gegenseitige (referenzielle) Abhängigkeiten zwischen ihnen umfassen (so muss
        die Rechnungsadresse gefüllt sein, um eine Rechnung als Art der Zahlung eintragen zu
        können). Hier kommen Regeln für die Prüfung der Konsistenz, der Plausibilität und
        der Integrität zum Einsatz.</utop>
      <utop>Das setzt jedoch voraus, dass die betreffenden Datenstrukturen einwandfrei
        definiert wurden. In nicht wenigen Fällen werden jedoch nur die Datentypen der
        Felder deklariert, und auch das recht lax: da wird eine 5stellige Zahl aus Gründen
        der Bequemlichkeit als 20stelliger String benannt. Auf gegenseitige Abhängigkeiten
        zwischen den Feldern wird gleich ganz verzichtet (zur Vermeidung von "Spannungen").
        Derlei Nachlässigkeiten schränken die Brauchbarkeit der Datenstrukturen als
        Qualitätsmaßstab ein. Die Validierung von Daten mithilfe fragwürdiger Strukturen
        ist selbst auch fragwürdig - sofern überhaupt validiert wird.</utop>
      <utop>Hinzu kommt, dass es schwierig ist, Datenstrukturen zu Beginn eines
        Softwareprojektes einmal für alle Zeiten unveränderlich zu definieren. Einige
        Anforderungen bzw. Abhängigkeiten verfallen, andere kommen neu hinzu. (Wer immer nur
        nach Plan arbeitet, kriegt bestenfalls, was der Plan vorsieht, aber nicht, was er
        braucht.) Wenn die Datenstrukturen (und die darauf aufbauende Anwendung) nicht
        "mitwachsen", dann stehen zahlreiche Anwender der Software bald vor dem Problem,
        dass zusätzliche Informationen nicht in die vorgegebene Struktur passen. </utop>
      <utop>Wichtige Zusatzinformationen werden dann häufig außerhalb der eigentlichen
        Anwendung in unsystematischer Form abgespeichert. Sie liegen in unterschiedlichen,
        nicht miteinander verknüpften Datentöpfen, die getrennt voneinander gepflegt werden,
        von sehr unterschiedlicher Aktualität sind und damit ein ideales Arbeitsfeld für
        Hobby-Archäologen darstellen. (Eines ihrer Forschungsergebnisse dürfte die
        Feststellung sein, dass zahlreiche Dokumente aus Microsofts Office-Familie
        stammen.)</utop>
      <utop>Gravierender ist, dass die "Schattendaten" sich einer systematischen
        Auswertbarkeit durch die eigentliche Anwendung entziehen. Sie stehen für die komplex
        definierten Geschäftsmodelle oder für Managemententscheidungen oft nicht zur
        Verfügung. Fehlentscheidungen sind vorprogrammiert. Auch ist die doppelte Pflege der
        ursprünglichen Datenstrukturen und der sie ergänzenden "Schattendaten"
        kostenintensiv. </utop>
      <utop>Fehlende, fehlerhafte oder veraltete Daten sind IT-Kostentreiber von
        herausragender Bedeutung. Allein der Kostenaufwand für die Recherche und Korrektur
        der Daten ist beträchtlich. Weit gravierender können Folgekosten werden: die
        wirtschaftlichen Schäden, die durch fehlerhafte Software / mangelhafte Datenqualität
        verursacht werden, umfassen jährlich stolze Milliardenbeträge. </utop>
      <utop>Tools für die Messung von Datenqualität sind auf dem Markt ebenso verfügbar wie
        strategische Ansätze, diese zu verbessern. Aber der bloße Einsatz von Tools hilft
        wenig, wenn der nachhaltige Wille fehlt. </utop>
    </top>
    <top titel="Sicherheit">
      <utop>Ein weiterer Kernaspekt für die Geschäftsinteressen jedes Softwareproduzenten und
        seiner Kunden ist die Sicherheit der Applikationen. Selbstverständlich soll
        Unbefugten verwehrt bleiben, Daten bzw. die Businesslogik ausspionieren oder gar
        beeinflussen zu können. Selbstverständlich ist Datenschutz von elementarer
        Bedeutung. </utop>
      <utop>In der Praxis beschränkt sich das teilweise auf den Einsatz handelsüblicher Tools,
        die zwar vor Malware und Viren schützen, aber nicht die selbst geschriebene Software
        absichern. Passwortgeschützte Web-Logins sind nicht hinreichend, wenn das darunter
        liegende System offen ist wie ein Scheunentor. Vergebene Adminrechte werden nicht
        durchweg konsequent wieder zurück genommen, wenn die für die Vergabe vorgesehenen
        Aufgaben beendet wurden.</utop>
    </top>
    <top titel="Fazit">
      <utop>Zwar ist das allgemeine Bewusstsein für mangelnde Softwarequalität durchaus
        vorhanden, aber konkrete Aktivitäten sind auf breiter Front nicht erkennbar.
        Möglicherweise sind verstärkte gesetzliche Schutzvorschriften nötig, um den Vertrieb
        und Einsatz von grob fahrlässig erstellter Software einzudämmen.</utop>
    </top>
  </Artikel>
</Artikelliste>

wg / 25. Oktober 2020



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.






Vielen Dank für Ihr Interesse an meiner Arbeit.



V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/Artikelliste_2_HTML_PDF_Docbook3.html