Kategorien
Themen

Zwei Phase Commit

ist nicht zu empfehlen.

Ich habe schon in den 1990ziger Jahren zu diesem Thema einiges gelesen und auch ausprobiert. So um 1992 hätte ich den Zwei Phasen Commit bei vernetzten MS-DOS Rechner sehr gut gebrauchen können. Das Thema begeisterte mich!

In Projekten nutzten meine Kunden immer eine Grossrechner, auf dem der zentrale Datenbestand auf einer DB2 Instanz lag, da braucht es kein Zwei Phase Commit, dort gibt es ja nur eine Datenbank Instanz, dem ein Commit gesendet wird. Und bei Dialog Programmen, Präsentations- und Fach-, ja sogar bei den auf die Datenbank schreibenden Zugriffsmodulen darf der Programmierer kein Commit setzen, dieses ist einzige Sache des Transaktionsmonitors. In Batch-Programmen habe ich natürlich Commits gesetzt.

Nun war ich bei einem Kunden, und dort gab es einen Programmier-Leitfaden. Unter anderem ist der Zwei Phasen Commit verboten.

Das Verbot ist sehr sinnvoll!

Es sollte, wenn es möglich ist, nur mit einer Datenbank Instanz gearbeitet werden, dann kapselt diese die Transaktionen.

Es gibt Konstellationen, wo leider schreibende Zugriff auf verschiedene Datenbanken notwendig sind. Bei einer festen Kopplung der Systeme bietet sich der Zwei Phase Commit an.

Läuft alles rund, schadet der Zwei Phase Commit nicht, aber braucht man ihn? Der Sinn ist es ja, dass ein Programm einen fachlich bedingten Rollback oder eine technische Komponenten auf Grund von Schwierigkeiten einen technischen Rollback auslösen kann, und dann klar ist, bis wohin zurückgedreht werden muss. 

Vor dem ersten schreibenden Zugriff sollte der Entwickler sicherstellen, dass die Daten passen, es also keinen fachlichen Grund für ein Rollback gibt. Es sind Situationen denkbar, wo in der Zeitspanne zwischen der Prüfung und dem Schreiben etwas passiert, aber diese sind bei guter Programmierung eher eine theoretische Wahrscheinlichkeiten.

Damit sind wir bei den technischen Gründen. Der Ausfall einer Datenbank ist denkbar. Am wahrscheinlichsten ist jedoch, dass Netzwerkverbindungen „sterben“. 

Nun stellen wir uns vor, bei einer Transaktion mit Zwei Phase Commit ist die erste Phase durchgelaufen und das steuernde System sendet die zweite Phase. Genau in dieser Zeitspanne sterben nacheinander, aber in wohl unbekannter Reihenfolge, die Netzwerkverbindungen zu den anderen Systemen.

In den Systemen, die noch die zweite Phase empfangen haben, ist die Transaktion abgeschlossen, bei den anderen ist die Transaktion noch offen und diese Datenbanken wartet auf ihren Timeout, um dann ihr Rollback durchzuführen.

Jetzt ist das Chaos perfekt! Die Transaktion befindet sich in einen undefinierten Zustand, statt „entweder alles oder nichts“ ist jetzt ein „teils/teils“.
Damit hat die geplante Transaktionen einen Zustand wie als wenn keine Transaktionen genutzt werden. 

Der gesamte Aufwand bei der Programmierung, die Lizenzkosten, der Overhead zur Laufzeit für das Zwei Phasen Commit bringen also nichts (oder nur sehr wenig) im Vergleich zum Verzicht auf Transaktionen.

Hoffentlich wird in allen Systemen beim Speichern der „erfasst-am“ Timestamp gesetzt, dann besteht die Möglichkeit der Reparatur. In allen betroffenen Systemen werden die Datensätze, die in der fraglichen Zeitspanne (und natürlich auch davor und danach) geändert wurden, gesammelt. Nun wird geschaut, ob zu einem fachlichen Begriff (zum Beispiel den Konto-Nummern) die Sätze zusammenpassen, also der Zustand der Datenbank wird mit den Vorgaben aus dem Fachkonzept verglichen. Zwischen Fachkonzept und Inhalt der Datenbank liegt bei der Entwicklung viel Arbeit, und bei dieser Reparatur ist der Aufwand vergleichbar. Was innerhalb einer Minute im Online-Betrieb passieren kann, wird einen Entwickler mehrere Tage beschäftigen, wenn er Zugriff auf alle Datenbanken hat. Muss er Daten aus einer entfernten Datenbank, bei einem anderen Unternehmen, anfordern und falls notwendig per Mail oder gar „schriftlich mit drei Durchschlägen“ die Datenreparaturen beantragen, werden aus Tagen schnell Wochen.

Ein kommerzielles Anwendungssystem mit vielen Anwendern (viele meiner Kunden arbeiten mit einer fünfstelligen Anzahl an Online Usern, deren hauptsächlicher „Erwerbszweck“ das Pflegen des Datenbestands ist) kann nicht ohne Transaktionen betrieben werden.

Es gibt aber eine Lösung: Die Systeme werden lose gekoppelt.

Hier gibt es zwei Stufen.

Die einfache Stufe:
Der führende, juristische Bestand, die Golden Source, sende Nachrichten, besser eine asynchrone Transaktion (WebService, SOAP, MQSeries) an einen anderen Bestand, dort werden die Nachrichten aufbereitet und in die Datenbank geschrieben. Es gibt keinen fachlichen Grund, warum die Daten nicht geschrieben werden können, ja, sogar falls die Daten offensichtlich falsch wären, was in der Golden Source steht, muss in die anderen Datenbank übernommen werden. Sollte etwas schiefgehen, kann es nur technische Gründe haben, dann ist der Betreuer des empfangenden Systems für den Wiederanlauf verantwortlich. Bei diesem Wiederanlauf werden die nicht abgeschlossenen Transaktionen automatisch abgearbeitet.

Technisch ist das Vorgehen sehr einfach. 
Kurz vor Abschluss der Transaktion auf dem führenden System wird die asynchrone Transaktion im Asynchronen Transaktion-Monitor geöffnet, es wird die Nachricht geschickt. 
Ist die Transaktion angenommen, gibt der Asynchrone Transaktion-Monitor sein Okay und das führende System kann seine Transaktion mit dem Commit gegen seine Datenbank schliessen. 
Die asynchrone „Tochter-Transaktion“ wird an das empfangene System geschickt, dort verarbeitet.
Nach dem Commit gegenüber der dortigen Datenbank wird mit dem Commit gegenüber dem Asynchronen Transaktion-Monitor die Transaktion geschlossen.
Die Transaktion muss auf dem empfangenen System durchlaufen, eine Information aus der Golden Source muss ja übernommen werden, es gibt keinen fachlichen Grund, die Daten nicht zu speichern.
Geht etwas schief, wird ein technisches Problem vorliegen, es verbleibt eine offene Transaktion im Asynchronen Transaktions-Monitor, die über ein Systemtool an den Administrator gemeldet wird. 

Die Verarbeitung wird zu diesem Zeitpunkt gestoppt, insbesondere wenn nicht komplette Sätze, sondern nur die Differenzen übertragen werden. 

Als Beispiel:
Im Datensatz steht das Feld A bisher auf ‚hello‘ und das Feld B auf ‚Ding‘.
Die gestoppte Nachricht lautet „Setze Feld A auf ‚bye‘ und Feld B auf ‚Auto’“.
Kurz darauf kommt eine Nachricht „Setze Feld A auf ’start’“, die durchlaufen würde.
Dann steht nach dieser Nachricht in der Datenbank Feld A ’start‘ und Feld B ‚Ding‘. 
Wird jetzt die gestoppte Nachricht später angenommen, steht in der Datenbank Feld A ‚bye‘ statt ’start‘.
Verwerfen wir die Nachricht komplett, steht in Feld B ‚Ding‘ statt ‚Auto‘.

Umgehen lässt sich das Problem, in dem immer komplette Inhalte als Nachricht gesendet werden und bei Bedarf der aktuelle Zustand angefordert werden kann. Die gestoppte Nachricht würde also ausgesondert und sobald das technische Problem behoben ist, die Golden Source um eine Komplett-Lieferung der betroffenen Daten gebeten.

Die etwas komplexere Stufe: 
Das empfangende System ist (auch) ein juristischer Bestand und darf Nachrichten ablehnen.

Der erste Teil, also das Abarbeiten der Transaktion im führendem System, bleibt fast identisch. 
Die asynchrone Transaktion erhält eine eindeutige ID, und diese ID wird zusammen mit einigen Zusatzinformationen in einer lokalen Verwaltungstabelle nach der Annahme durch den Asynchronen Transaktion Monitor, aber vor dem abschliessenden Commit, gespeichert.

Nach der Verarbeitung durch das empfangene Systeme, egal ob erfolgreich oder auch nicht, wird die gesendete asynchrone Transaktion geschlossen (Commit) und zeitgleich (im Zweifel direkt davor) ein Response, also eine neue asynchrone Transaktion, mit der ID der ursprünglichen Transaktion sowie einer Meldung, also Okay oder dem Fehler, an das ursprüngliche System gesendet. 

Sofern es eine Okay-Meldung ist, setzt das sendende System das „Erfolgreich“ Kennzeichen in seiner Verwaltungstabelle und schliesst die Response-Transaktion.  

Bei einer Fehlermeldung wird die Response-Transaktion nach dem Schreiben der Information in der Verwaltungstabelle geschlossen. Der ursprüngliche Anwender oder ein zentraler Arbeitsplatz bearbeitet dann die ursprüngliche Datenänderung so, dass die neue Transaktion auch vom empfangenden System angenommen wird. 

Bei bekannten Konstellationen ist auch eine automatische Korrektur möglich, wenn zum Beispiel eine Vollmacht zwischen zwei Kunden oder einem Vertrag zu einem Kunden nicht angenommen wird, weil der Bevollmächtigte noch nicht im empfangenen System vorhanden war, Der Bevollmächtigte wird Kunde automatisch gesendet und nach dem Response für die erfolgreich Anlage folgt die Vollmacht.

Kategorien
Themen

Wir müssen an die Zukunft denken, Daten leben lang

Daten leben lang! 

Eine Rentenversicherung beginnt mit der Einzahlungsphase mit dem Berufsstart, bei vielen Menschen also, wenn sie 16 Jahre alt sind. Circa 50 Jahre später beginnt dann die Rentenphase, die bis zum Tod dauert. Nach dem Tod sind die Unterlagen noch einige Jahre verfügbar zu halten. Damit kann ein Datensatz über 100 Jahre benötigt werden.

Ein anderer Fall, zu dem es sogar eine Gerichtsentscheidung gibt, sind Sparbücher. Ein Sparbuch war vor vielen Jahrzehnten verschwunden, wohl für ungültig erklärt worden und das Guthaben wurde auf ein anderes Konto übertragen. Viele Jahre nach dem Tod des Inhaber tauchte das nicht als „ungültig“ gestempelte Sparbuch wieder auf. Die Bank konnte nicht anhand ihrer Unterlagen beweisen, dass das Sparbuch für ungültig erklärt worden war und musste an die Erben das Guthaben inklusive der Zinsen auszahlen.

Auch in anderen Bereichen müssen Daten sehr lange leben. Mit der Entwicklung des Airbus A300 wurde 1970 begonnen, die letzten Maschinen werden voraussichtlich 2050 ausgemustert. Mit der Entwicklung des US Bombers B52 wurde 1946 gestartet, die letzten Maschinen in den 1960er Jahren gebaut und sollen auch bis 2050 einsatzfähig bleiben. Beide Flugzeugen wurden noch auf Papier konstruiert.

Rechnen wir die Zeiträume aus der Vergangenheit für einen heute ins Berufsleben tretenden Menschen oder ein heute neu entwickeltes Flugzeug hoch, sind die heute erfassten Daten noch in 100 Jahren in operativen IT-Systemen zu finden und müssen bei einer Umstellung migriert werden. Papierzeichnungen gibt es hier nicht mehr.

In den Anfangstagen der IT waren die Datenstrukturen fest in den Anwendungsprogrammen verdrahtet. Heute findet man dieses noch in vielen hierarchischen Datenbanksystemen, deren Grundlagen in eben diesen Anfangstagen der IT gelegt wurden. Das wir diese Form der Datenspeicherung heute noch finden liegt an dem Aufwand, diese auf moderne Formen umzustellen.

Das Problem der Datenmigrationen bei Systemumstellungen wurde erkannt und die Daten nun in einer flachen Form so gespeichert, dass die Überführung auf ein neues Anwendungssystem vereinfacht wurde. Die Informationen über die Datenstruktur bleiben jedoch noch in den Anwendungsprogrammen.

Mit der Entwicklung der Relationalen Datenbanken wurde die Datenspeicherung und die Datenstruktur-Beschreibung ein eigenes System ausgelagert. Damit sind Datenmigrationen zwischen Anwendungssystemen, deren Entwicklungssysteme, Programmiersprachen etc. inkompatible sind, möglich. Auch muss der Entwickler, der die Daten überträgt, nicht die Programmiersprache des alten Anwendungssystem beherrschen.

Die Datenstrukturen, gerade bei komplexen Sachverhalten, so zu entwickeln und in einem Anwendungssystem umzusetzen, dass später, also in vielleicht 20 Jahren, eine Umstellung der Daten auf ein neues Anwendungssystem möglich ist, ist sehr anspruchsvoll und kostet damit Zeit und Geld.

Aus diesem Grund sind gerade im Bereich der Objekt-Orientierung Programmierung Frameworks entstanden, die ein Objekt speichern und bei Bedarf wieder hervorholen. 

Damit haben wir, was die Struktur der Datenspeicherung angeht, den gleichen Zustand wie in den Anfangstagen der IT. Nur wusste man es damals vielleicht nicht besser, auf jeden Fall war damals von der zur Verfügung stehenden Hardware die Speicherung in Datenbanken nicht möglich gewesen.

Falls in 5 oder 10 Jahren die Daten (Objekte, also Dinge, die über EINE Programmiersprache beschrieben sind) auf eine andere Plattform zu übertragen sind, wird es sehr schwierig werden, ähnlich wie in der Vergangenheit die Ablösung einer hierarchischen Datenbank (z.B. IMS) durch eine Relationale Datenbank (DB2). 

Wenn man keine Rentenversicherungen oder Sparbücher verwaltet oder keine Flugzeuge konstruiert, warum soll man dann auf die Migration der Daten achten? Fast jedes IT-System verwaltet Verträge oder erfasst Sachverhalte, die zu Verträgen gehören. Und Vertragsunterlagen sind schon aus Steuerrechtlichen Gründen einige Jahre nach Beendigung des Vertrages aufzubewahren und einem Prüfer zur Verfügung zu stellen. 

Als muss man entweder das alte IT-System mit den Alt-Daten zumindest 10 Jahre nach dem „Abschalten“ am Laufen halten, oder eben die Daten migrieren. Die Daten aus den aktuellen Verträgen müssen sowieso migriert werden, also übernimmt man auch die Daten von Verträgen, die vor 9 Jahren ausgelaufen sind.

Kategorien
Themen

Wie die Datenbank organisieren?

In Datenbanken werden verschiedene Arten von Datensätzen gespeichert, je eine pro Tabellen.
Es gibt zum Beispiel Girokonten oder Depots bei Banken, Auftragspositionen in Warenwirtschaftssystemen, Personen, Telefonnummern, …

Zu jeder Art gibt es einen fachlichen Schlüssel, bei einer Rechnungs-Unterposition zum Beispiel Rechnungs-, Auftrags-, Positions- und Unter-Nummer. Also ein recht langer Schlüssel. 

Für eine Adresse ist der fachliche Schlüssel die Kunden-Nummer, die Adress-Art und eine laufende Nummer (es kann mehrere Adressen einer Art, zum Beispiel „Ferienwohnung“ geben), eigentlich auch zu lang.

Insbesondere, wenn noch ein Unterscheidungsmerkmal für die zeitlich Abfolge benötigt wird.
Eine Adresse kann nicht einfach in der Datenbanktabelle überschrieben werden, die bisherige Adresse muss ja nachvollziehbar sein. Also ist die zeitliche Abgrenzung der bisherigen und der neuen Adresse über den Zeitpunkt „gültig-von“ oder „erfasst-am“ notwendig.

Ein Kunde kann viele Adressen haben, seinen Wohnsitz, ein Postfach, eine oder mehrere Urlaubsadressen, … Letztlich sind Telefon, Fax, Email-Adresse, … auch Adressen.

Und einige Kunden können identische Adressen haben. 
Ein Mann als KFZ-Halter ist Kunde einer Versicherung.
Dieser Mann ist auch als Ehemann in dem „Gemeinschaftskunden“ Ehepaar vorhanden. Das Ehepaar hat zum Beispiel eine Hausratversicherung. Damit ist der Mann, also eine Person, als zwei unterschiedliche Kunden gespeichert.
Trotzdem hat er als Person sicher nur einen Wohnsitz, und wenn das Ehepaar umzieht, dann zieht nicht nur der Mit-Vertragspartner der Hausratversicherung, sondern auch der KFZ-Halter um.

Ist es nicht sinnvoll, den Wohnsitz oder die Telefonnummer nur einmal zu speichern und über eine Zuordnung-Tabelle beiden Kunden zuzuordnen, aber unter welcher Kundennummer? Wir haben hier m:n Beziehungen, also mehrere Kunden zu mehreren Adressen.

Was passiert, wenn der Kunde seine bisherige Ferienwohnung zur Hauptadresse macht? Ist es sinnvoll, die Adress-Art in der eindeutigen Schlüssel für die technischen Zugriffe aufzunehmen?

In Datenbanken gibt es meist den Primär-Schlüssel, den die Datenbank auch für interne Zwecke nutzt. Ein Feld des Primär-Schlüssels kann nicht geändert werden, es ist eine Kopie des bisherigen Datensatzes mit dem geänderten Feld zu erstellen und der bisherige Datensatz zu löschen. Mittlerweile kann auch DB2 ein Update auf ein Primär-Schlüsselfeld durchführen, also „Update Kunden-Tabelle Set Kunden-Nummer = ‚4712‘ Where Kunden-Nummer = ‚4711‘, aber intern führt die Datenbank sicher keinen Update, sondern das oben genante Verfahren durch. Ein Update auf ein Feld des Primär-Schlüssels sollte, da hier auch historische Zusammenhänge verloren gehen, immer die Ausnahme sein, die im Rahmen von Reorganisationen stattfinden, aber nie in einem Anwendungsprogramm verwendet werden.

Am Besten ist es, wenn jeder Datensatz seine eindeutige ID erhält, dazu kommt dann noch für die Eindeutigkeit im zeitlichen Ablauf der Zeitpunkt (Timestamp), an dem die Version eingefügt wurde.

Die hierarchischen Verbindung zu seinem übergeordneten Datensatz geschieht über die ID des übergeordneten Datensatzes. Bei einer Auftrags-Unterposition, also der ID, nicht der Nummer, der Auftragsposition plus die Unternummer. 
Wird die Position innerhalb des Auftrags oder zu einem anderen Auftrag verschoben, ist nur deren Positionsnummer anzupassen, die Unterpositionen verweisen ja nur auf die ID und wandern mit.
Der Timestamp der Auftragsposition gehört nicht in die Unterposition, denn ändert sich die Position, erhält also einen neuen Timestamp, bleibt die Unterposition ja weiterhin die Unterposition.

Bei einer Adresse ist eine Verknüpfungstabelle besser, also Kunden-ID, Adress-ID, Adress-Art.  Zusätzlich dann als technische Hilfe noch der Adress-Typ (Post-Adresse, Postfach, Telefon, …) angegeben werden, dann kann sofort auf die richtige Datenbank-Tabelle zugegriffen werden.

Jeder Datenbank-Tabelle in einem kommerziellen Umfeld besitzt die Felder 
– “erstellt-durch“ für den Anwender, der diese Satz eingefügt oder geändert hat. Bei einer Änderung durch ein Batch-Programm gibt es keinen Anwender, hier kann der Name des Programms als „technischer User“ genutzt werden. Das Feld wird immer automatisch gesetzt.
– „erfasst-am“, wann (Timestamp) der Datensatz in dieser Version in die Datenbank eingestellt wurde.

Einige Satztype werden nie ungültig, ein Beispiel sind Journal-Sätze in einer Buchhaltung.
Bei einige Satztypen reicht ein Kennzeichen, dass sie ab „jetzt“ ungültig sind. 
Andere Satztypen haben einen Gültigkeitszeitraum, also „gültig-von“ und „gültig-bis“, zum Beispiel Adressen.

Die beiden letzten Typen benötigen also ein Feld 
– „ersetzt-am“, wann dieser Datensatz ungültig wurde, wobei bei Adressen dieses Feld angibt, wann sie durch eine anderer Version mit einen anderen „gültig-bis“ ersetzt wurden.

Ob dieses Feld „ersetzt-am“ per Update-Anweisung gesetzt wird, oder in eine „Ersetzungs-Tabelle“ ausgelagert wird, um nur mit dem Insert-Statement arbeiten zu können, ist eine taktische Entscheidung.

Und jetzt das alles entscheide Feld:
– „Satz-ID“, dieses ist eine grosse Zahl (64-Bit Integer), ein technischer Begriff, kein Anwender sollte sie je sehen, sie wird auch über keine Schnittstelle an andere Systeme geliefert oder von diesen empfangen und nur über sie werden innerhalb der Datenbank Verknüpfungen aufgebaut.

Der Primär-Schlüssel jeder Datenbank-Tabelle ist „Satz-ID“ plus „erfasst-am“. 

Da es nur einen gültigen Datensatz geben kann, die ersetzten Datensätze ausgeblendet sind, kann bei Abfragen das „erfasst-am“ wegfallen.

Die Satz-ID wird durch den ID-Geber, einem zentralen Prozess, eindeutig im Gesamt-System vergeben. Eine Satz-ID kann also immer nur einmal und innerhalb einer Datenbank-Tabelle vorkommen.

Was bringt dieses Vorgehen:
Zum einen lassen sich Relationen sehr einfach aufbauen.
Ersetzte Datensätze sind natürlich auszublenden.
Statt der Relation über viele fachliche Schlüsselfelder, ich habe in Projekten Tabellen mit einem Dutzend fachlichen Schlüsseln gesehen (Skontro trennende Merkmale), basieren alle Relationen nur auf der Satz-ID.

Ein weiterer Vorteil ist das Zusammenspiel von verschiedenen Umgebungen.
Ein Unternehmen, das Software entwickelt und nutzt, hat zumindest folgende Umgebungen:
– Entwicklung
– Test
– Schulung
– Produktion

Einige meiner Kunden hatten noch weitere Umgebungen, Vor-Entwicklung (hier wurden zentrale Komponenten entwickelt), statt einer Entwicklungs-Umgebung je eine für das nächste und das übernächste Release, Entwicklungs-Umgebungen pro Team, mehrere Test-Stufen, eine Schulungs-Umgebung mit dem produktiven Release, eine Schulungs-Umgebung mit dem nächsten Release, eine Umgebung für Pilot-Kunden, …

Wie die Software, also die Sourcen und die Lademodule verteilt sind, ist ein Thema, auf das ich an anderer Stelle eingehen möchte.

Jede Umgebung hat ihren eigenen Datenbestand, also ihre eigene Datenbank.

Zu jeder Test- und Produktions-Umgebung gibt je eine Wartungs-Umgebung.
Hier werden notwendige Bugfixes entwickelt. Zu Test dient ein eigener Datenbestand. Zwischen der Produktion und deren Wartungsumgebung sollte auch noch ein Test-Umgebung vorhanden sein.

Die Schulungsumgebung ist ein besonderer Datenbestand, hier ist eine überschaubare Menge an didaktisch aufbereiten, künstlichen Daten vorhanden, alle Personen sind „erfunden“, die Provisionssätze maximal die Hälfte der minimalen Provision, die ein Vertriebsmitarbeiter erhält, …
Und von der Schulungsumgebung existiert ein Backup, dass vor jeder Schulung aufgespielt wird. 

Wichtig ist der Zusammenhang zwischen einer Produktions- beziehungsweise Test-Umgebungen, jeweils mit ihre Wartung–Umgebungen.
In der Produktion ist testen verboten!
Entwickler-Tests, also die ersten Versuche des Entwicklers, die noch einiges durcheinander bringen können, sollten auch nicht die Umgebung für die Tester ausser Gefecht setzen.
Manchmal wird beim Entwickeln ein direkter schreibender Zugriff auf die Daten benötigt, ein SQL Script lässt sich direkt schneller austesten, und diese Rechte werden auch in einer Test-Umgebung aus guten Gründen nicht gewährt.

Datenkonstellationen, die zu Problemen führen, müssen also schnell und unkompliziert in die Umgebung, auf die ein Entwickler direkt zugreifen kann, kopiert werden.

Dabei Namen, Anschriften und anderer personenbezogene Daten zu anomysieren ist selbstverständlich. Dabei darf natürlich kein Zusammenhang für den Testfall zerstört werden. Bei einer KFZ Haftpflichtversicherung und einem Problem bei der Betragsberechnung könnte der Wechsel der Daten einer 50 jährigen Beamtin gegen die eines 19 jährigen männlich Mauerlehrling nicht den benötigten Testfall ergeben, oder bei einer Hausratversicherung der „Umzug“ aus der Flussniederung auf einen Hügel.

Aber das eigentliche Problem sind die Daten, die schon innerhalb der Testumgebung vorhanden sind. Tritt der Fehler in der Produktion beim Kunden 4711 auf und gibt es schon einen Kunden 4711 in der Testumgebung, was tun? 

Für alle fachlichen Schüssel neue Werte, die bisher noch nicht in der Testumgebung genutzt werden, suchen und umschlüsseln? Geht es um triviale Datenkonstellation (der Kunde und seine Telefonnummer), funktioniert es einfach, aber solche Daten sind schneller händisch aufgebaut. Was ist aber bei Verträgen mit mehreren Personen in verschiedenen Rollen, die unterschiedliche relevante Merkmale haben? Ist die Ursache für die Probleme beim Testen dann ein Fehler in der Software, oder lag es an einem Fehler beim Umschlüsseln?

Ist es besser, aus der Test-Datenbank alle Daten zu den fachlichen Schlüssel, die zu laden sind, zu löschen? Und nach 5 Minuten stehen die Kollegen, deren aktuelle Testfälle gerade gelöscht wurden, vor dem Schreibtisch.

Es gibt aber eine Lösung!

Wir arbeiten ja mit Satz-IDs als Primärschlüssel und über diese sind alle Verknüpfungen hergestellt.

Und jede Umgebung hat ihren eigenen ID-Generator und für jede Umgebung einen eindeutigen Wertebereich für die IDs.

Der Wertbereich von 0 bis 1 Milliarde (2 hoch 30) ist für Fremdschlüssel, die von den Administratoren per Script angelegt werden, vorgesehen.

Dann kommt der Bereich für die produktive Umgebung, von (2 hoch 30) + 1 bis 2 hoch 63, den Rest, also (2 hoch 63) + 1 bis (2 hoch 64) – 1 werden gerecht auf die verschiedenen Entwicklungs- und Test-Umgebungen verteilt, wobei je „freier“ eine Umgebung ist, desto höher ist der Wertebereich. Gibt es eine Satz-ID mit einem höheren Wert wie der Maximalwert lauf ID-Geber, wurden wohl (zurechtgebogene) Daten aus einer Entwicklungs- in eine Test-Umgebung kopiert. Diese Kontrollmöglichkeit ist für die interne Revision wichtig.

Finden wir eine Satz-ID aus dem Wertebereich der produktiven Umgebung in einer Test- oder Entwicklungs-Umgebung, stammt diese Satz-ID ursprünglich aus der Produktion.
Wird jeder Datensatz mit dieser Satz-ID gelöscht und dann durch die Daten aus der Produktion ersetzt, wird kein mühsam von einem Kollegen erstellten Testfall zerstören.

Also können die Daten aus der produktiven Umgebung entladen, die verwendeten Satz-IDs ermitteln, diese in der Zielumgebung löschen (sind sie nicht vorhanden, umso besser!). Dann können die Daten geladen werden und sofort kann das Problem aus der Produktion nachgestellt werden.

Sollte ein fachlicher Schlüssel, zum Bespiel die Kunden-Nummer 4711, schon vorhanden sein, können die Daten nicht geladen werden, es gibt ja einen Index auf die Kunden-Nummer, und diese ist unique definiert. Aber die Kunden-Nummer steht ja nur in einem Datensatz, und wird sie dort auf 4712 geändert, hat es keine weiteren Auswirkungen auf den Testfall.

Ach ja, darum eigentlich Kunden-Nummer, und das noch als numerischer Wert? Interessiert der Mittelwert der Kunden-Nummer? Solange ein Feld nicht für Additionen genutzt wird, kann es als Zeichenkette definiert werden, und dann kann „4711“ einfach in „P4711“ geändert werden.

In der Vergangenheit traf ich Kollegen, meist kurz nach der Ausbildung, die alle Tabellen „richtig“ normalisieren wollten. Und in der Adress-Tabelle, die von ihnen erstellt wurde, gab es ein Feld „Postleitzahl“, dabei ist doch die Postleitzahl recht einfach aus Ort (Köln), Strasse (Aachener Strasse) und Hausnummer (256) oder der Postfach-Nummer herzuleiten, gehört also nicht in eine normalisierte Datenbank-Tabelle, oder? 

Normalisieren soll den Aufwand bei einem Daten-Update minimieren, aber bitte nicht auf Kosten des Aufwands bei Abfragen. Aus diesem Grund packe ich gerne die Postleitzahl (der Lieferadresse zum Zeitpunkt des Auftrags) in Auftrags-Unterpositionen, denn die Verteilung von Artikel, Preis und Postleitzahl ist eine sehr häufige Auswertung. 

Kategorien
Themen

Was fehlt in COBOL?

COBOL ist eine sehr alte Programmiersprache, nun ja, sehr alt, 1960 ist noch nicht alt, ich bin 2 Jahre jünger und fühle mich auch nicht alt.

Es gab mehrere Standards, 1960, 1968, 1974, 1985, aktuell ist der von 2002. Es gibt Erweiterungen in Richtung Objektorientierung seit Mitte der 1990ziger Jahre.

In Projekten habe ich Mitte der 1980ziger Jahre mit COBOL-74 gearbeitet. Dort brauchte man den „GO TO“ Befehle und hinter jedem Befehl ist einen Punkt „.“ notwendig.
Seit Anfang der 1990ziger Jahre wird COBOL-85 genutzt, mit dieser Version lassen sich Programm sehr gut strukturieren, der „GO TO“ wird zwar unterstützt, wird aber nicht mehr benötigt. Und die Punkte als Abschluss einer Anweisung stören jetzt eher.

Die OOP-Erweiterungen oder gar die Version von 2002 (Frei-Format) habe ich bisher noch nicht in Projekten gesehen, meist geht es bei meinen Kunden ja auch um Erweiterungen von Anwendungssystemen, die vor einigen Jahren konzipiert wurden.

Das Schöne an COBOL ist, dass eine Source, die vor 50 Jahren erstellt wurde, noch heute mit den aktuellen Compiler übersetzbar ist. 

Meines Wissens ist nur der „ALTER“ Befehl nicht mehr im Sprachstandard enthalten, und dieser Befehl war schon bei meinen ersten Berührungen mit COBOL ganz oben auf der Liste der Verbote. Mit ALTER konnte man zur Laufzeit die Reihenfolgen der Abarbeitung ändern, damit war der Programmablauf nicht mehr nachzuvollziehen, gut dass dieser Befehl verschwunden ist.

Aber was fehlt in COBOL heute?

An Befehlen und Sprachstandards eigentlich nichts. Es gibt vier Punkt:

Der Pretty-Printer
Bei dem ersten Punkt geht es wirklich um den Punkt. In Projekten habe ich häufig mit Programmen zu tun, die vor 20 oder mehr Jahren erstellt wurden. Damals waren einige Programmierer noch nicht vollständig mit COBOL-85 vertraut und diese Entwickler nutzten innerhalb von Sections (Blöcken) den Punkt. Ein Punkt terminiert jede Anweisung.
Statt
if x
   if y
      move a to b
   else
      move c to b
   end-if
end-if

kann man auch schreiben
if x
if y
move a to b
else
move c to b.

Es gibt auch einen Befehl, um direkt hinter den nächsten Punkt zu springen (next sentence).
Die Anweisungen-Folge:

move ‚a‘ to b
move ‚c‘ to d
continue.
block-ende.
exit.

ist, sofern nicht innerhalb einer IF-Anweisung, einer Schleife oder ähnliches identisch mit 

move ‚a‘ to b.
move ‚c‘ to d
continue.
block-ende.
exit.

Steht weiter oben im Block „next sentence“, befindet sich bei der zweiten Version das ‚c‘ in der Variable d, und der Entwickler sucht. Haben Sie sofort den Punkt bei „b.“ gesehen? 

Falls ja, stellen Sie sich vor, dass ein wichtiges Programm, vor vielen Jahren von einem Kollegen geschrieben, vor kurzem von Ihnen leicht erweitert, nicht läuft, Sie haben das Problem auf eine Section mit mehreren hundert Anweisungen eingegrenzt, die Source ist schlecht formatiert, einige Zeilen sind auf „Kommentar“ gesetzt, es ist mitten in der Nacht, Sie erhalten regelmäßig Anrufe, wann es weitergeht, … Also viel Stress, und Sie wissen noch nicht, dass es der eine Punkt ist.

Falls die beiden Move Befehle in ein if Statement müssen, wird auch gerne der Punkt übersehen. Sofern der Entwickler das if mit einem end-if abschliesst, findet der Compiler zwar nicht den Fehler, bemerkt aber dass eine end-if zuviel ist.

Leider gab es auch den ein oder anderen Kollegen, der seine Sourcen nicht vernünftig formatierte. Ähnlich wie bei der Programmiersprache c ist COBOL vieles möglich.

Schön wäre ein Pretty-Printer für COBOL Sourcen, der auch die unnützen Punkte entfernt und eine gute Formatierung erstellt. Leider geht dieses nur mit einem Programm, das entsprechend dem Compiler die Source analysiert. Im ersten Beispiel ist ja der Punkt durch zwei end-if zu ersetzen. Manche Punkte können auch nicht ohne weitere Änderung entfernt werden, siehe das next sentence.

Eine Section sollte anschliessend folgen Aufbau. Diesen Aufbau nutze ich auch in meinen Projekten  und bat meine Kollegen, bisher immer mit Erfolg, bitte, ihn einzuhalten. Wobei, die meisten Kollegen machen es ja auch so!

sektion-name SECTION.
    viele Anweisungen, aber nie einen Punkt
    CONTINUE.
sektion-name-ENDE.
    EXIT.

Ein weiterer Fall für den Pretty-Print wäre die Zuweisung auf Aufzählungstype.
01 aufzaehlungs-typ pic x.
88 ist-wahr value ‚1‘.
88 ist-falsch value ‚2‘.
88 weiss-nicht value space.

move ‚1‘ to aufzaehlungs-typ

ist zulässig, aber unübersichtlich, besser wären

set ist-wahr to true

Das Copy-Statement
COBOL lebt, ähnlich wie c, von den Include Dateien, auch wenn sie der COBOL Programmierer in Deutschland meist „Copystrecken“ nennt.
Der Sprachstandard hat den COPY Befehl, und hierbei gibt es den optionalen Parameter „REPLACING“. Ein COBOL Programm hat einen globalen Namensraum, die definierte Variable „meine-variable“ ist also im gesamten Programm sichtbar, eine zweite Variable mit diesem Namen kann es aber geben, der Compiler kann sie aber nicht am Namen unterscheiden.
Gibt es „meine-variable“ in der Struktur eingabe-satz und der Struktur ausgabe-satz, reicht nicht

move meine-variable to meine-variable

sondern es muss qualifiziert werden,

move meine-variable in eingabe-satz to meine-variable in ausgabe-satz

Statt des „in“ kann auch „of“ genutzt werden, ich persönlich nutze „in“ für das Qualifizieren und „of“, wenn ich mit Pointern (ja, das geht auch in COBOL) arbeite.

Es gibt aber auch eine weitere Möglichkeit, die ich lieber nutze.
Beim COPY Statement wird mittels REPLACING der Prefix des Namens angepasst. In den Copystrecken steht dann
10 *-meine-variable pic x(4).

Im Programm
01 eingabe-satz.
copy eingabe-include replacing *- by ein-.

01 ausgabe-satz.
copy ausgabe-include replacing *- by aus-.

move ein-meine-variable to aus-meine-variable

In dem Beispiel ist die Version mit Qualifizierung noch übersichtlich, ich habe aber schon Sourcen gehabt, bei denen die Variablen- und Strukturnamen 20 oder mehr Zeichen lang (zulässig sind 31 Zeichen, und bei generierten Copystrecken sieht man 31 Zeichen häufig) waren und zur Eindeutigkeit über 3 Stufen zu qualifizieren war, also zum Beispiel

move meine-string-variable in eingabe-variante-eins in eingabesatz-kunden to meine-string-variable in ausgabe-variante-zwei in ausgabesatz-bevollmaechtigter

Und das nicht nur für ein Feld, sondern für alle Felder, die bei einem Kunden benötigt werden.

Das COPY Statement mit dem REPLACING ist schön, aber leider wird es erst durch den Compiler ausgewertet. Enthält ein Programm auch SQL Anweisungen, läuft vor dem Compiler der DB2 Preprozessor. Dieser muss die Include-Dateien mit den Host-Variablen einbinden. Host-Variablen sind die Speicherstellen, in die bei Abfrage die Ergebnisse von der Datenbank kopiert oder abgefragt werden. Und diese „exec sql include db2-struktur end-exec“ kennt leider kein Replacing.

Ansätze für Lösungen habe ich schon gesehen. 

Es gibt eine Version des Build Prozesses, bei der alles in einem Lauf passiert, also nicht vor dem Compiler der Preprozessor aufgerufen wird. Bisher habe ich diesen Build Prozesse noch in keinem Projekt gesehen. 

Eine weitere Lösung wäre, den Build um einen eigene Schritt zum Einbinden aller Include Dateien zu erweitern, also auf „exec sql include“ und copy zu verzichten. Dieses Vorgehen hat auch Vorteile bei der Source-Verwaltung. Bei einigen Kunden gab es dieses Vorgehen mit include Anweisungen, aber leider ohne einen Replacing Parameter.

Wenn es „zu wild“ mit dem Qualifizieren sind, siehe das Kopieren von Kunde nach Bevollmächtigter, kann über ein eingebettetes (oder ein externes) Unterprogramm ein eigener Namensraum geschaffen werden.

In dem Unterprogramm steht dann
procedure division using ein, aus.
move meine-string-variable in ein to meine-string-variable in aus

und im Hauptprogramm
call unterprogramm using  eingabe-variante-eins in eingabesatz-kunden, ausgabe-variante-zwei in ausgabesatz-bevollmaechtigter

Ein eindeutige Definition für den Timestamp
In der Datenbank ist ein Timestamp intern meist eine 64-Bit Ganzzahl. In einem COBOL Programm erhält man einen String, meist in einer Länge von 26 Zeichen, entweder als

„2015-03-15-16.04.05.123456“ oder
“2015-03-15 16:04:05.123“

Die drei letzten Stellen ist der Geschwindigkeit der heutigen Rechner, oder war es der Rechner vor 20 Jahren, geschuldet. Ein Timestamp muss eindeutig sein, und wenn der Rechner mehr wie 1000 Inserts pro Sekunde in die Datenbank schafft, reichen eben keine Millisekunden zur Eindeutigkeit.

Viel entscheidender ist der Bindestrich oder das Leerzeichen vor den Stunde und die Doppelpunkte oder die Punkte als Trenner zwischen Stunde, Minute und Sekunde.

Der erste Timestamp wird bei Grossrechner-Programmen, die mit dem DB2 Preprozessor übersetzt wurden, verwendet.
Der zweite Timestamp stammt zum Bespiel von Programmen, die mit dem Microfocus Compiler übersetzt wurden und der ODBC Schnittstelle nutzen. Es ist das Timestamp-Format von ODBC.

Hier sollte ein Schalter vorhanden sein, der das ODBC Format automatisch in der IBM Format umsetzt.

Ach ja, es gibt noch weitere Inkompatibilitäten zwischen verschiedenen COBOL Compilern, die eigentlich nicht seinen sollten, oder zumindest eine Warnung beim Compilieren erzeugen sollten.

Während des Studiums durfte ich ein COBOL Programm, das auf einem IBM Mainframe laufen sollte, schreiben. Ich bereitete natürlich das Programm auf dem PC vor, dort hatte ich ja auch eine COBOL Entwicklungsumgebung. Die Aufgabe lies sich iterativ oder rekursiv lösen, rekursiv war modern, und die Verwaltung der Variablen-Sätze pro Rekursions-Tiefe spannend. Also entwickelte ich die rekursive Version, auf dem PC lief sie super. Die Source war schnell auf dem Grossrechner übertragen und übersetzt. Das Programm lief auch, nur der letzte Rücksprung aus der Rekursion klappte nicht, das Programm war in einer Endlosschleife bzw. brach ab, weil der Datensatz für die Rekursions-Teile -1 nicht definiert war. 
Ich suchte mit Hilfe meines Dozenten mehrere Wochen nach der Lösung, bis er (oder seine Supportanfrage bei IBM) in den Tiefen der Compiler Dokumentation den Grund gefunden hatte. 
Der Compiler auf dem PC hat einen Return-Stack. Bei jedem Perform (Unterprogramm Aufruf) wird die Adresse im Programm auf einen Stapel-Speicher (First-In, First-Out) gespeichert, bei einem Rücksprung Befehl wird diese Adresse geholt und dort mit dem Programmablauf weitergemacht. So funktioniert auch ein rekursiver Aufruf. 
Auf dem Grossrechner ist dieser Return-Stack nicht vorhanden, hier merkt sich das Programm am Anfang der Sektion („Unterprogramm“), von wo es aufgerufen wurde. Beim ersten Aufruf der Rekursion aus dem Hauptteil steht dort also die Adresse des Hauptteils, beim zweiten Aufruf, aus der ersten Stufe der Rekursion, wird die Adresse mit der der Rekursion überschrieben. Geht die Rekursion eine Stufe tiefer, wird die Adresse mit sich selber überschrieben, beim Rücksprung macht das Programm an der richtigen Stelle weiter. Nur der letzte Rücksprung in den Hauptteil klappt nicht, dessen Adresse wurde ja überschrieben.
Das Umstellen von den rekursiven auf ein iteratives Vorgehen war schnell gemacht.

Mehr End-Statements
Es gibt in COBOL viele END Statements, einige benötigt der Compiler, zum Beispiel end-if, end-perform, end-evaluate (ausser man setzt einen Punkt). Andere sind optional, zum Beispiel end-string, end-compute, end-display, …. Diese braucht der Compiler nicht, bei 

compute x = y * 10
move x to z

weiss der Compiler, dass das move nicht mehr zum compute gehört. 
Beim Lesen einer Source ist aber

compute
x = y * 10
end-compute
move x to z

übersichtlicher. Dieses ist auch ein Thema für den Pretty-Printer.

Manchmal fehlt mir das end-move. Der move Befehl ist ja einfach, aber

move spaces to a, b, c, d, e, f

ist zulässig, es werden Leerzeichen auf die Variablen a, b, c, d, e, f zugewiesen.
Jetzt das Ganze mit den qualifizierten Variablen-Name, dann steht da „move“ und einige Bildschirmseiten weiter unten der nächste Befehl, wo hört der move Befehl auf?

Kategorien
Themen

String mit fester Länge

String in Java haben immer eine beliebige Länge, das ist in vielen Fällen aus sehr praktisch.
Ausser, die String sollen in einer Datenbank gespeichert werden, hier dürfen sie nicht zu lang sein, oder bei einer XML Message, wo in der XSD die Länge festgelegt ist.
Ein beliebtes Austauschformat sind Dateien mit fester Satzlänge, die können auch nur mit einigem Aufwand bearbeitet werden.
Auf einem GUI haben Eingabefelder meist eine feste Länge, die mit der gewünschten Länge des  Stringfeldes übereinstimmt, also definieren wir die Länge im GUI und ein Kollege in der Datenbank, ein weiterer Kollege in der XSD, …

Die Klasse String ist ein Wrapper um ein Array of char, damit wird bei jeder Zuweisung ein neues Array erzeugt und das alte „weggeworfen“, und irgendwann muss der Speicher aufgeräumt werden. Okay, bei den heutigen Rechner fällt das nur selten auf, wenn dann aber in kritischen Situation.

Bei einem String mit fester Länge kann ein char Array beim Instanzieren erzeugt und dann immer weiter verwendet werden.

Kategorien
Themen

Klassen entsprechend der XSD Definitionen

Die XSD ist erst einige Zeit nach Java entstanden. Hier können Datentypen sehr gut definiert werden, z.B. Strings mit maxLength, Werte-Bereichen. Für andere Datentypen gibt es auch entsprechende Restrictions. 
In der DDL von SQL kann eine Teilmenge hiervon definiert werden, einige Restriktionen, z.B. die Länge, sind sogar Pflicht.
Nur in Java kennen wir den String, das Integer, … ohne weitere Einschränkungen. Also werden Klassen erfunden, die ein Wrapper um einen String sind, um die Einschränkungen nachzubilden.
Hier ist Programmiersprache gefordert, Datentypen bereitzustellen, die vollständig der Definitionen aus einer XSD entsprechen.

Kategorien
Themen

implements Klasse

Mehrfachvererbung, so wie in c++, gibt es in Java nicht. Das ist auch gut so!
Nehmen wir eine Klasse_A, die die Methode tutwas() besitzt. Die Klasse_B ist von Klasse_A abgeleitet und überschreibt die Methode tutwas(). Die Klasse_C ist ebenfalls von Klasse_A abgeleitet und überschreibt auch tutwas(). Nun habe wir die Klasse_D, die von Klasse_B und Klasse_C abgeleitet wird, Klasse_D wird instanziert und die Methode tutwas() aufgerufen.

Welche Methode? Die von Klasse_A, Klasse_B oder Klasse_C?

Objektorientiert heisst nach meinem Verständnis zuerst „Ein Objekt der Klasse x reagiert auf jede Nachricht (Methode), auf die eine Eltern-Klasse von x reagiert, sinnvoll.“ Damit ist isInstance(), selber aufgerufen, vom Compiler oder Runtime genutzt, eine wichtige Eigenschaft einer Klasse.

MeinAutobus ist eine Instanz der Klasse AutoBus, die von PersonenTransporter und StrassenFahrzeug abgeleitet ist. Beide Klassen sind von der Basisklasse „Fahrzeug“ abgeleitet.

AutoBus soll auf die Nachrichten „gibt-Anzahl-Passagier-Plätze“ (von PersonenTransporter) und „gibt-Schadstoff-Plakette“ (von StrassenFahrzeug) sinnvoll reagieren können. MeinAutobus.isInstance(PersonenTransporter) und MeinAutobus.isInstance(StrassenFahrzeug) sind wahr.

In Java können wir nur von mehreren Interfaces erben, also bauen wir die Vererbungskette über Interfaces auf. Es gibt also ein InterfacePersonenTransporter, ein InterfaceStrassenFahrzeug, ein InterfaceAutoBus, …
Und zu jedem Interface gibt es eine Klasse, die das Interface implementiert und in der alle Methoden, die benötigt werden, programmiert sind.
Solange die Vererbungskette komplett über Interfaces bereitgestellt wird und klar ist, welche Klasse das Interface implementiert, ist es nur einiges an Mehrarbeit.
Braucht man aber eine Klasse, zu der es kein Interface gibt, hat man ein Problem.

Jede Klasse kann zu einem Interface umgewandelt werden. Das kann auch der Compiler recht einfach machen. 

Wenn also bei der Klassen-Definition 
public class AutoBus extends StrassenFahrzeug implements PersonenTransporter
oder auch 
public class AutoBus extends PersonenTransporter implements StrassenFahrzeug
geht würde, wären sinnvolle Vererbungsketten einfacher dazustellen.

Wenn Sie nun glauben, das Ganze seit sehr theoretisch, schauen Sie sich bitte jcifs.smb.SmbFile an, sieht aus wie java.io.File, benutzt man wie java.io.File, hat aber kein java.io.File als Eltern, sondern leitet sich von java.net.URLConnection ab. Mit SmbFile wird auf Samba Shares, also Netzwerk Laufwerken, zugegriffen. In einem Projekt schrieb ich ein Framework, das auf „normalen“ Dateien und solche auf Samba Share zugreifen musste. Glücklicherweise benötigte ich nur wenige Methoden, also ein Interface MyFile erstellt, die Methoden definiert. Und das Interface mit einer Klasse MyFileJFS, die eine Variable vom Typ java.io.File und mit einer Klasse MyFileSamba, die eine Variable vom Typ  jcifs.smb.SmbFile implementiert. Wie die Methoden aussah, können Sie sich sicher vorstellen.

public ReturnType xxx() { return file.xxx()}

Bei den 5 Methoden, die ich von File benötigte, ist es etwas Schreibarbeit, was ist bei komplexen Anforderungen?

Kategorien
Themen

Evaluate

Wer in Java das switch Statement oder komplexe if/else if/else Abfolgen mag, der möge sich das COBOL EVALUATE anschauen.
Ich kenne keine kompaktere Möglichkeit, Entscheidungstabellen zu implementieren.

Kategorien
Themen

„IS“ oder „ALIAS“

Schauen Sie sich mal die Signaturen von vielen Methoden an:

public String meineMethode( String x1, String x2, String x3, …, String X25 )

Was passiert, wenn man mit der Reihenfolge durcheinander kommt? Der Compiler kann es nicht merken, vielleicht läuft das Programm sogar durch alle Tests. Und später in der produktiven Umgebung gibt es falsche Daten, die bei den Jahresabschlussarbeiten auffallen, …

Wie soll der Compiler oder das Runtime festgelegen, welcher der beiden Methoden mit gleichen Namen (z.B. connect()) und 4 Strings als Parameter zu nehmen ist?

Also einfach ein char[] oder eine byte[] dazwischen mischen? Sowas habe ich schon bei einem SFTP Library gesehen. Ist aber unschön.

Von String kann nicht abgeleitet werden, da das Laufzeitverhalten bei finalen Klassen günstiger ist. 
Und int ist keine Klasse, davon kann also überhaupt nicht abgeleitet werden.
Also baut man Klassen „Vorname“, „Nachname“, …, die jeweils ein Stringfeld enthalten, und wer den Vornamen mit einen anderen String vergleichen will, schreibt
Vorname vname = …;

vname.getInhalt().equals( …

Wie wäre eine „Klassen-Definition“
public class Vorname alias String;

Die Zuweisung „vname = nname;“ für zu einem Fehler, den schon der Compiler erkennt.
Der Vergleich vname.equals(nname) wäre aber möglich, da hier zwei Strings miteinander verglichen werden.
Ein 
String testWert = vname; 
ist statthaft, aber ein 
Vorname vname2 = „Peter“; 
würde ebenso wie
Vorname vname3 = new String(„Peter“); 
eine Warnung erzeugen.
Ein 
Vorname vname4 = new Vorname( „Peter“ );
oder ein 
String test = „Peter“;
Vorname vname5 = new Vorname( test ); 
ist der Königsweg.

Die Methoden
public Kunde erzeugeKunde( JurName name, HR_Eintrag hr, …
public Kunde erzeugeKunde( Vorname vname, Nachname nname, …
sind unterscheidbar.

Wenn man sich die Objektstrukturen anschaut, die aus WSDLs (also WebService Definitionen) z.B. eines SAP System, erzeugt werden, wird sich über jeden „Alias-String“ statt einer eigenen Klasse freuen.

Kategorien
Themen

Null-Objekt

Ein Objekt (besser Variable) mit Inhalt null reagiert auf jeden Methodenaufruf mit einer Null-Pointer-Exception. Schöner wäre ein Objekt, das auf jeden Methodenaufruf einigermassen sinnvoll reagiert, als Beispiel:
String zeichenkette = null;

if( zeichenkette.equals(„Hallo“)) …

würde nicht die Exception werfen, sondern sagen, dass null („nichts“) nicht gleich „Hallo“ ist.

Aber diese Erweiterung wäre sehr inkompatible zu den bisher programmierten Programmen, also besser nicht.

Es gibt aber einige Dinge, die sind sinnvoll.