Monsterklassen und das SRP

Monsterklassen und das SRP

Phase 1: Das initiale Konzept

Nehmen wir an, es gäbe eine Klasse, die folgende Aufgabe erledigt:

Version 1

Folgerichtig gibt es jede Menge aufeinander abgestimmter Methoden, um die Struktur aufzubauen:

	public IJSONArray    AddArray( string key) { ... }
	public IJSONObject   AddObject( string key) { ... }
	public IJSONProperty SetProperty( string key) { ... }
Und so weiter. Mit den zurückgelieferten Teilen kann man dann ebenso verfahren. Außerdem gibt es auf der Ausgabeseite eine oder mehrere Methoden zum Rendern:
	public void Render( Stream destination) { ... }
Bis hierher muß man als Anwender der Klasse nicht wissen, wie das ganze intern funktioniert. Es ist einfach, intuitiv anwendbar, sozusagen "neat and clean".

Phase 2: Verschlimmbesserung

Jetzt kommt aber der Fall, wo sich der Code des Anwenders nicht mehr an das erinnern kann, was er zuvor gerade getan hat. Es scheint daher erforderlich, ja geradezu alternativlos, in den Interna der diensteifrigen JSON-Renderer-Klasse nach bereits angelegten Properties zu gründeln, Listen zu enumerieren, Werte wieder auszulesen und was nicht alles noch.

Ergo werden einfach schnell (klicki-klacki) ein paar Get-Methoden angeheftet und fertig.

Fertig? Ja, aber total. Das Konzept sieht nämlich jetzt plötzlich so aus:

Version 2

Und noch viel schlimmer: die auf die obige Aufgabe hin optimierte Klasse sieht sich damit plötzlich völlig anderen Anforderungen ausgesetzt, was zB. zu Performanceproblemen führen kann und dem bekannten Effekt „Warum hat der das nur so komisch geschrieben, da hätte man doch gleich XYZ machen können, das wäre doch viel besser gewesen?“. Ja klar – im Lichte der neuen Anforderungen vielleicht, aber wir reden ja auch nicht mehr über das ursprüngliche Konzept!

Vermeide kurzfristige Lösungen für langfristige Probleme
Es wird vermutlich nicht lange dauern, da werden noch Navigations-und Traversierungsroutinen ergänzt, dazu vielleicht noch ein Parser, um existierendes JSON in den Objektbaum einlesen zu können, und so weiter. Ok, das muß jetzt nicht unbedingt schlecht sein, man sollte sich jedoch vor Augen halten, daß es durch diese scheinbar marginale Änderung eben KEINE auf die einzige Aufgabe des Renderns hin optimierte Klasse mehr ist, sondern irgendetwas völlig Anderes. Und in den meisten Fällen ist genau das absolut kontraproduktiv - eine kurzfristige Lösung, die langfristige Probleme erzeugt.

Außerdem führt dieser Weg des unbedachten Konzept-Verwässerns geradewegs in Richtung der Monsterklassen, die wir alle gut kennen: Die wollen immer alles können, aber genau betrachtet geht nichts wirklich richtig gut oder performant. Das konkrete Beispiel läßt sich beliebig austauschen, und jeder findet wohl beim Nachdenken selbst schnell ein paar Klassen, Subsysteme und Units, auf die das zutrifft.

Was ist denn nun aber die Lösung? Gibt es Vorschläge?

Wie so oft gibt es keine Patentlösung, sondern man sollte das ganze fallabhängig betrachten. Im konkreten Fall war es vermutlich pure Faulheit, den aufrufenden Algorithmus ordentlich zu designen und sich stattdessen lieber bei den Interna einer anderen Klasse zu bedienen.

„Wieso denn aber nicht? Die Werte sind doch da drin?! Ich habe die doch selbst übergeben!“

Sicher, aber es sind eben nicht mehr deine Daten, sondern die des JSON-Renderers. Und es ist einzig und allein dem Renderer überlassen, was er damit macht – dich als Aufrufer geht das fei garnix mehr an. Der Pfeil im ursprünglichen Konzept geht nämlich nicht grundlos nur in eine Richtung, sondern das ist ein Teil der Idee.

Test (neudeutsch: Self Assessment)

Wer nun immer noch meint, es wäre überhaupt kein Problem, hat das mit der Kapselung und Separation of Concerns irgendwie wohl doch noch nicht ganz verstanden. Schauen wir mal:

Welche Antwort wählst Du? A oder B?

Antwort A: Schnittstellen sollten

Antwort B: Schnittstellen sollten

Auflösung

Natürlich ist Antwort B richtig. Die Option A führt früher oder später unweigerlich zu fetten Monsterklassen und aufgrund der viel zu engen Kopplung dazu, daß das äußerste linke Ende der Software über die Interna der Klasse ganz rechts außen Bescheid wissen muß.

Das aber ist tödlich, denn damit erzeugt man statt einer schönen, modularen Lösung aus lose gekoppelten und leicht wart- und wiederverwendbaren Modulen ein unauflösbares Gestrüpp an Abhängigkeiten, das jeden Wartungsprogrammierer zur Verzweiflung treibt.