Exceptions Als Architektur Bestandteil
 
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Hallo zusammen,

die Diskussionen zum Thema Exceptions auf diesem Wiki finde ich super-interessant. Ich möchte mal einen Beitrag leisten, der die Sache aus Sicht meines Spezialgebiets, der Software-Architektur, beleuchtet.

-- MatthiasBohlen

Dafür muss ich zunächst ein paar Sachen postulieren, die außerhalb des Scopes dieses Problems liegen (sonst läuft die Sache aus dem Ruder und wird nicht klar):

  1. Das Problem wird durch Java bloßgelegt, da dort Exceptions (meist) deklariert werden müssen und vom Compiler geprüft werden. Deshalb zeige ich das Problem in Java.
  2. Exceptions sind gut, wenn man sie mit Disziplin verwendet.
  3. Das Problem ist, den Überblick zu behalten.
  4. Verlorener Überblick führt automatisch zu Disziplinlosigkeit.
OK, also worum geht es mir wirklich?

Exceptions bei faulen Entwicklern

In Java hat man die Möglichkeit, Exceptions entweder sofort zu behandeln (try...catch einfach sofort hinschreiben) oder aber durchzureichen an den Aufrufer der aktuellen Methode. Dieser Aufrufer könnte (faulerweise) die Exception wieder durchreichen und so weiter, bis es das arme Schwein am Ende trifft (meistens den GUI-Entwickler!), der dann alle Exceptions fangen und wüste Meldungen ausgeben muss, mit denen der User nichts anzufangen weiß.

Richtig? Falsch! Das Problem tritt viel früher auf!

Es passiert nämlich unterwegs etwas, was das ganze Entwicklungsteam aufregen kann. Nehmen wir mal ein Beispiel: An sich hätten wir gern folgendes:

Person lesePersonAusDatenbank (String name, String vorname)
    throws PersonNichtGefundenException
{
    // Person in Datenbank suchen
    // gefundene Daten in Person-Objekt verpacken
    // und mit "return" zurückgeben

    // oder aber:
    throw new PersonNichtGefundenException
       ("Person '" + vorname + " " + name + "' nicht gefunden.");
}

Bei Datenbankoperationen kann nun dummerweise noch etwas anderes passieren als ein simples "nicht gefunden": die Connection könnte z. B. abreißen, weil jemand den DB-Server runterfährt oder das Ethernet mal grade einen kleinen Aussetzer hat.

Für diese Fälle wirft die JDBC-API eine java.sql.SQLException mit einer entsprechenden Meldung darin.

Was also macht ein fauler Entwickler mit dieser Exception? Ganz einfach, er reicht sie durch:

Person lesePersonAusDatenbank (String name, String vorname)
    throws PersonNichtGefundenException, java.sql.SQLException
{
    Connection con = ConnectionPool.getConnection("...");
    Statement stmt = /* irgendwie statement bauen */ ;
    ResultSet rs = /* irgendwie das stmt ausführen */ ;

    while (rs.next())
    {
      Date   gebDatum = rs.getDate("GEBDATUM");
      String adresse  = rs.getString("ADRESSE");
      return new Person (name, vorname, gebDatum, adresse);
    }
    
    throw new PersonNichtGefundenException
       ("Person '" + vorname + " " + name + "' nicht gefunden.");
}

Wir sehen in dem Beispiel schon das Problem: Um "faul" zu sein und eine Exception durchzureichen, muss ich die throws-Klausel meiner Methode um ein Element erweitern, also in diesem Falle um java.sql.SQLException. Damit zwinge ich nun alle im Team, die lesePersonAusDatenbank() aufrufen wollen, sich mit dieser dummen Exception auseinanderzusetzen oder sie ebenfalls in ihre throws-Klauseln aufzunehmen. Solche Probleme greifen also bei faulen Entwicklungsteams rasch um sich.

Denkt man daran, dass es noch viel mehr Gründe gibt, Exceptions zu bekommen, so können solche throws-Klauseln sehr schnell sehr lang werden.

Abhilfe durch Architekturkonvention

Wir greifen am besten architektonisch ein und vereinbaren im Team eine Konvention, wie mit Exceptions umgegangen wird. Eine solche Konvention könnte so lauten:

  1. Pro Komponente gibt es eine Exception-Basisklasse. Wir nennen sie so wie die Komponente heißt und setzen "Exception" dahinter. Beispiel: Die Komponente "Kunden" bekommt eine "KundenException".
  2. Alle Exceptions, die in dieser Komponente geworfen werden, haben von der <Komponente>Exception abzuleiten.
  3. Die throws-Klauseln aller Methoden aller Klassen, die in der Schnittstelle einer Komponente vorkommen, lauten einfach "throws <Komponente>Exception".
  4. Das Werfen anderer Exceptions über die Komponentenschnittstelle hinweg ist verboten.
Jetzt werden einige schon zum Schrei ansetzen, aber wartet noch eine Sekunde! :-)

Es braucht nun noch eine Festlegung, was mit Exceptions ist, die ich kassiere und per definitionem nicht über die Schnittstelle weiterreichen darf, also zum Beispiel die oben genannte SQLException. Die Antwort ist: ich muss überlegen, was diese Exception im Sinne meiner Komponente, die ich gerade schreibe, bedeutet. Als nächstes muss ich dieser Bedeutung dann durch eine Exception, die aus meiner Komponente kommt, Nachdruck verleihen.

Obiges Beispiel würde dann wie folgt gelöst:

Bei einem technischen Fehler in einer solchen Operation hat der User eigentlich nur noch eine Chance: Den Admin benachrichtigen, der schaut ins Log und startet evtl. den DB-Server neu oder steckt den Ethernet-Stecker wieder rein. Etwas anderes geht schlecht. Also:

Erst Voraussetzungen schaffen: Die <Komponente>Exception definieren...

class PersonenDatenbankException extends Exception
{
   ...
}

und:

class PersonNichtGefundenException
      extends PersonenDatenbankException 
{
   ...
}

... dann zuschlagen:

Person lesePersonAusDatenbank (String name, String vorname)
    throws PersonenDatenbankException
{
  try
  {
    Connection con = ConnectionPool.getConnection("...");
    Statement stmt = /* irgendwie statement bauen */ ;
    ResultSet rs = /* irgendwie das stmt ausführen */ ;

    while (rs.next())
    {
      Date   gebDatum = rs.getDate("GEBDATUM");
      String adresse  = rs.getString("ADRESSE");
      return new Person (name, vorname, gebDatum, adresse);
    }

    throw new PersonNichtGefundenException
       ("Person '" + vorname + " " + name + "' nicht gefunden.");
  }
  catch (SQLException se)
  {
    FehlerLog.loggeTechnischenFehler(2137, "lesePersonAusDatenbank", se);
    throw new PersonenDatenbankException
      ("Technischer Fehler Nr. 2137 - bitte Administrator benachrichtigen", se);
  }
}

Dann: Eintrag ins Betriebshandbuch dieser Software machen. Kapitel "Administration der Anwendung", Abschnitt "Fehlerprotokoll", Unterabschnitt "Bedeutung der Fehlernummern" ... na, Ihr wißt schon, was ich meine.

Was hat's gebracht?

Das Konzept ist erweiterbar:

Diskussion

Bitte beim Diskutieren nicht das Beispiel angreifen, sondern das Prinzip! Was sagt Ihr dazu?

-- MatthiasBohlen

Jetzt werden einige schon zum Schrei ansetzen, aber wartet noch eine Sekunde! :-): Da ich EinfachNurEinFaulerProgrammierer? bin, gefällt mir das Prinzip des Exception durchreichens. Daher würde ich versuchen das arme Schwein, das es am Ende trifft (meistens den GUI-Entwickler!) besser zu unterstützen. Zum Beispiel durch eine getrennte Einheit zur Fehlerbehandlung, die dann die entsprechenden Log Einträge vornimmt, anstatt das über alle Komponenten zu verteilen.

Meine Aussage oben war vielleicht missverständlich. Ich wollte eigentlich damit sagen, dass das Problem des "armen Schweins am Ende" nichts ist im Vergleich dazu, dass die throws-Klauseln im gesamten System dazu tendieren, alle möglichen und unmöglchen Exceptions zu enthalten. Mein Beitrag hat zum Ziel, letzteres in den Griff zu bekommen, dann hat es der GUI-Entwickler auch leichter. -- mb

Aja! Also meinst Du, Exceptions mehrerer Komponenten zu verwalten ist einfacher als die Exceptions nach Ursachen gruppiert? Ich frage, da ich mit so großen Projekten keine Erfahrung habe.

Wenn eine neue Exception dazukommt, bleibt die Signatur der Methode stabil. => Der Code des Aufrufers braucht nicht geändert werden.

Wenn eine neue Fehlersituation auftritt, muß der Aufrufer vielleicht geändert werden um auf diese Situation zu reagieren, das ändert sich nicht, wenn die Exception jetzt so oder so ist. Wird die Signatur verändert, beschwrt sich wenigstens der Kompiler. Vielleicht wäre Der Code des Aufrufers muß nicht unbedingt geändert werden. klarer?

Abschließend, das typische KO-Argument bei solchen Diskussionen: Ein wirklich fauler Programmierer würde sowieso nur 'Personen|Datenbank|Exception|s' werfen und 'Exceptions' fangen und damit die gesamte Architektur untergraben.

Na klar! -- mb

-- DavidSchmitt


Siehe auch ExceptionsInSpracheLava


KategorieException


StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 21. Januar 2004 19:59 (diff))
Suchbegriff: gesucht wird
im Titel
im Text