Bequeme Abstraktion ganz konkret
Bei der Lektüre von „Clean Architecture" ist mir aufgefallen, dass Robert C. Martin immer wieder von Abstraktion spricht. Er beschreibt sogar eine Metrik für den Grad der Abstraktion von „Komponenten":
Abstraktheit A = Na / Nc
Mit
- Na für die Zahl der abstrakten Klassen und Interfaces in einer „Komponente"
- Nc für die Zahl der Klassen in der „Komponente"
„Komponenten" mit A=1 enthalten ausschließlich abstrakte Klassen bzw. Interfaces. Wenn jedoch A=0, dann enthalten sie nur nicht-abstrakte (oder konkrete) Klassen.
Einerseits hat mir das gefallen, denn ich bin ein großer Freund von Abstraktion. Andererseits jedoch bin ich nicht ganz warm geworden mit dem, was Robert C. Martin da geschrieben hat.
Abstraktion in der von ihm beschriebenen Weise ist nützlich. Ich möchte sie nicht missen. Nur scheint sie mir vergleichsweise, hm, schwach. Mit ihr werden wir nicht so viel produktiver, wie es nötig ist, finde ich. Darauf habe ich auch schon in einem anderen Artikel angespielt. (Und obendrein führt sie zu einer selbst gemachten Komplexität, die ich gruselig finde. Aber davon ein andermal mehr.)
Nun habe ich mir ein paar Gedanken gemacht, um mein Unbehagen zu konkretisieren. Jetzt kann ich genauer sagen, was mir fehlt.
Abstraktion ist nicht gleich Abstraktion
Zunächst einmal ist mir klar geworden, dass es überhaupt verschiedene Arten der Abstraktion gibt. Ich sehe derzeit diese:
- Aggregation
- Destillation
- Integration
Aggregation
Bei der Aggregation geht es darum, das Gemeinsame in Verschiedenem zu sehen. Diese Figuren…
…lassen sich auf mehrere Weisen aggregiert, d.h. klassifizieren bzw. kategorisieren. Die Kategorien könnten z.B. Farben sein
oder es könnten „Grundformen" sein:
Es liegt im Auge des Betrachters, wie bei der aggregierenden Abstraktion das Viele auf Weniges reduziert wird. Denn darum geht es: ums Reduzieren, ums Abziehen und Entfernen. Wikipedia schreibt:
Das Wort Abstraktion (lateinisch abstractus ‚abgezogen, Partizip Perfekt Passiv von abs-trahere ‚abziehen‘, ‚entfernen‘, ‚trennen‘) bezeichnet meist den induktivenDenkprozess des Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres oder Einfacheres.
Das Unterscheidende wird vernachlässigt zugunsten von etwas Gemeinsamem. Details treten in den Hintergrund. In den Vordergrund tritt das Aggregat, die Klasse oder Kategorie als neues Ganzes, in dem die Vielheit eingeschlossen ist.
Abstraktionen dieser Art machen es möglich, allgemeine Aussagen zu treffen. Aus Peter und Maria werden Kunden. Aus einem BMW und einem Toyota werden Kraftfahrzeuge.
In der Softwareentwicklung abstrahieren wir durch Aggregation, wenn wir Funktionen in Klassen zusammenfassen oder Klassen zu Bibliotheken usw. Module sind das Mittel der aggregierenden Abstraktion.
Aus den Funktionen Read()
und Write()
wird die Klasse Repository{}
. Aus den Klassen Game{}
und Referee{}
wird die Bibliothek TicTacToe.dll
.
Module sind Kategorien, die Funktionen bzw. „kleinere“ Module zusammenfassen nach einem gemeinsamem Zweck.
Bei der Aggregation bleibt das Aggregierte erhalten. Es wird lediglich zusammengefasst unter einer Überschrift. Die soll den geistigen Umgang damit erleichtern. Man muss sich nicht ständig mit einer großen Menge von Dingen oder ihren ganzen Details auseinandersetzen. Bei Bedarf steht das Aggregierte jedoch wieder als Einzelnes zur Verfügung.
Destillation
Die Destillation ist eine Aggregation - allerdings gehen bei dieser Abstraktion die vielen Einzelnen verloren. Die Destillation zieht eine Essenz aus dem Verschiedenem. Diese Essenz ersetzt anschließend das Verschiedene bzw. steht als etwas Eigenständiges und Neues neben ihnen.
Wenn Sie einen Text lesen und ihn anschließend zusammenfassen, dann destillieren Sie. Der ganze Reichtum des Textes wird ersetzt durch Ihr abstract.
In diesem Beispiel wird zuerst aggregiert; es entsteht die Kategorie „Blau“, in der Elemente nach ihrer gemeinsamen Farbe zusammengefasst sind. Und in einem weiteren Schritt wird diese Farbe als etwas Eigenständiges herausdestilliert; die ursprünglichen Elemente verschwinden. Die „Blauheit“ als Extrakt ist etwas Neues, das wiederum als Grundlage für Weiteres dienen kann.
Dasselbe geschieht, wenn Sie aus verschiedenen Klassen eine Basisklasse, gar eine abstrakte Klasse oder ein Interface herausziehen. Der Detailreichtum der Logik in Ausgangsklassen verschwindet. Zurück bleibt eine Essenz.
Jetzt ist klar, dass Robert C. Martin sehr pauschal in seinem Gebrauch des Begriffs Abstraktion ist. In seiner Metrik A setzt er zwei Abstraktionsarten ins Verhältnis: er teilt die Zahl der Destillate in einer „Komponente“ durch die Zahl ihrer Aggregate (oder die Summe aus Aggregaten plus Destillaten, damit keine Division durch 0 entsteht).
Was aber ist denn entscheidend für die Segnungen einer Clean Architecture, die er in seinem Buch vertritt? Sind Destillate wichtig, um sauber zu sein? Welchen Stellenwert haben Aggregate?
So viel ist klar: mit Destillaten kann man nicht nur Überblick gewinnen, sondern auch Produktivität - zumindest wenn das Destillat nicht nur ein Begriff ist, sondern Logik enthält.
Herausdestillierte Logik ist wiederverwendbare Logik. Sie ist wie Milchpulver oder ein Same. Einfach Wasser drauf geben und sehen, was daraus entsteht ;-) Oder bei Logik: einfach weitere Logik in einer Ableitung/Implementation dazu geben und schauen, wie etwas Neues mit weniger Aufwand entsteht. Das ist der Gedanke hinter Vererbung, bei der die Basisklasse eine grundlegende Leistung anbietet, die abgeleitete Klassen „modulieren“.
Da Robert C. Martin jedoch nur von abstrakten Klassen und Interfaces spricht, scheint er mir nicht so sehr an dieser Art Logik-Destillate zu denken. Es sind eher reine Form-Destillate bestehend aus Funktionssignaturen.
Solche Form-Destillate bringen keinen Produktivitätsvorteil durch Wiederverwendbarkeit, sondern Polymorphie. Die ist nützlich für den Austausch von konkreten Objekten, sie macht flexibel. Wenn verschiedene Klassen auf demselben Form-Destillat basieren, können ihre Instanzen unterschiedslos dort benutzt werden, wo das Form-Destillat bekannt ist. Oder anders ausgedrückt: Robert C. Martin geht es um Dependency Inversion, also Abhängigkeit von Abstraktionen - genauer: Destillaten, noch genauer: Form-Destillaten -, statt von Implementationen. Konkrete Implementationen werden erst zur Laufzeit von außen bereitgestellt (Inversion of Control).
Integration
Aggregation und Destillation sind Abstraktionen, die auf dem Gemeinsamen in einer Vielheit basieren. Integration ist demgegenüber jedoch ganz anders. Integration bringt ganz bewusst das Verschiedene zusammen, weil es verschieden ist.
Integration setzt etwas Neues aus sich ergänzendem Verschiedenen zusammen. Integration ist wie ein Puzzle: die Teile sehen alle unterschiedlich, gar sinnlos aus, doch zusammen ergeben sie ein bedeutsames Bild.
Statt Integration kann man wohl auch Komposition sagen. Denn was entsteht ist etwas, das vorher nicht da war, ein kreatives Werk.
Aggregation und Destillation schaffen im Grunde nichts Neues. Sie kontourieren und betonen lediglich, was schon vorhanden ist, eben das Gemeinsame in einer Vielheit.
Demgegenüber ist Integration erfinderisch. Sie erkennt, wie Verschiedenes zusammengefügt werden kann zu etwas nützlichem Neuem.
Destillation kann die Produktivität steigern durch Wiederverwendbarkeit. Integration hingegen steigert die Produktivität durch Bequemlichkeit.
Früher mussten Sie einen Topf und einen Herd und einen Wecker getrennt bedienen, um Eier zu kochen. Heute benutzen Sie einfach einen Eierkocher. Oder allgemeiner: Wo heute etwas auf Knopfdruck entsteht, wofür früher viele Handgriffe nötig waren, ist Abstraktion durch Integration die Zauberformel dahinter.
Früher musste man Code wie diesen Schreiben, um die Zeilen aus einer Textdatei zu lesen:
Heute macht es die Nutzung einer Integration viel einfacher:
Die Framework-Methode File.ReadAllLines()
abstrahiert von den Details der vielen einzelnen Logik-Anweisungen, die eigentlich nötig sind.
Integration schmerzlich vermisst
Es wird so viel über Abstraktion in der Softwareentwicklung gesprochen, doch ein differenzierter Blick darauf fehlt, finde ich.
Und es wird so viel über Aggregation und Destillation gesprochen, dass man scheinbar übersieht, dass es auch noch die Integration gibt.
Das ist schade, denn die Integration ist es, die vor allem einen Produktivitätsgewinn verspricht.
Für Aggregation und Destillation braucht es zunächst eine gewisse Masse, eine Vielheit, bevor überhaupt Muster entstehen können, die man dann herauspräpariert. Das geschieht in einem „induktiven Denkprozess“, wie Wikipedia es nennt.
Vielheit wiederum braucht Zeit. Es muss etwas aufgehäuft werden. Man muss zulassen, dass Verschiedenes entsteht, was sich später vielleicht wiederum zusammenfassen lässt.
Zeit jedoch ist etwas, das in der Softwareentwicklung notorisch knapp ist. Angesichts des großen Wunsches nach mehr Produktivität entstehen daher vorzeitige Abstraktionen (premature abstraction) durch Aggregation und Destillation. Ihnen fehlt schlicht die Substanz. Die Vielheit ist nur mager, die Muster darin fadenscheinig. Und so ist ein Produktivitätsgewinn höchstens kurzfristig; mittelfristig entsteht Verschwendung, weil die vorzeitigen Abstraktionen aufgrund neuer Erkenntnisse aufgegeben bzw. umgebaut werden müssen.
Wiederverwendung von Logik-Destillaten ist möglich - braucht aber Zeit. Produktivitätsgewinne durch sie sind keine leicht zu pflückenden Früchte.
Produktivität durch Abstraktion für Bequemlichkeit hingegen ist viel früher zu erlangen. Abstraktion durch Integration kann schon am ersten Tag geerntet werden. Allerdings braucht es dazu ein anderes Mindset. Hier ist nicht Mustererkennung gefragt, sondern Kreativität und Erfindungsreichtum.
Integration ist eine ursprüngliche Ingenieursleistung. Nicht Handwerker, nicht Forscher erfinden bequemlichkeitsstiftende Werkzeuge, sondern Ingenieure.
Leider ist das Bewusstsein in dieser Hinsicht in der Branche vergleichsweise schwach entwickelt, wie mir scheint. Jedenfalls was die Abstraktion durch Integration innerhalb von Projekten/Produkten angeht. Über Projekte und Unternehmen hinweg gibt es einen ungeheuren Drang zur Vereinfachung, zu mehr Bequemlichkeit, zu mehr Produktivität durch Integration.
Vom Wesen der Integration
Abstraktion erzeugt eine Hierarchie. Vieles geht auf die eine oder andere Weise in Wenigem auf. Dieser Schritt kann beliebig oft wiederholt werden. Auch Abstraktionen lassen sich weiter abstrahieren.
Ziel der Integration als Abstraktion ist es, mit dem entstehenden Wenigen etwas zu vereinfachen, d.h. es bequemer zu machen, einen Effekt zu erzielen.
Integration geht dafür bottom-up vor. Aus einer Menge von Elementen werden solche ausgewählt, die zusammengesetzt etwas neues Nützliches ergeben. Ausgangspunkt ist dafür ein Problem, zu dem eine Lösung gesucht wird.
Die Auswahl aus der Elementmenge erfolgt mithin zielgerichtet. Integration bringt nicht zufällig Elemente zusammen und fragt sich, was deren Summe wohl Nützliches darstellen könnte. Sie sucht vielmehr nach Passendem in dem, was vorliegt - oder generiert weitere Elemente, um die Auswahl zu erhöhen.
Zu einem Problem werden zuerst mögliche Lösungsbausteine gesammelt. Die sollten nicht zu grob und nicht zu fein sein. Grobe Bausteine sind sperrig und müssten vor Verwendung angepasst werden. Feine Bausteine andererseits müssten in unhandlich großer Zahl zusammengesetzt werden. Die Kunst der Integration beginnt daher beim richtigen Abstraktionsgrad der Menge von Einzelteilen.
Aus dieser Menge werden dann geeignete Bausteine ausgewählt soweit vorhanden. Fehlen noch welche zu einer problemlösenden Integration, muss die Menge erweitert werden. Zur Kunst der Integration gehört mithin nicht nur handwerkliches Geschick, um existierende Bausteine nutzbringend zusammenzustecken zu etwas Größerem, sondern auch ein offener Geist bei der Suche nach weiteren Integrationskandidaten.
Integration beginnt also bei einem Problem, aus dem eine Bausteinmenge abgeleitet wird, aus der dann Elemente zu eine Lösung integriert werden.
Integration: Problem -> Bausteinmenge -> komplementäre Elemente -> Lösung
Das Ergebnis ist eine Hierarchie, deren Knoten einerseits Integrationen von darunter hängenden sind und andererseits selbst wiederum Elemente von darüber liegenden Integrationen. Integrationen hängen von ihren Teilen ab; fehlen welche oder mangelt ihnen eine Qualität, ist die Integration unvollständig.
Mit jeder Integrationsebene, d.h. mit jedem Abstraktionsschritt, steigt dabei die Bequemlichkeit der Erreichung eines Zwecks - bei allerdings gleichzeitig abnehmender Breite des Einsatzgebietes.
Die Leistung der Integration ist es, aus einem Haufen an Elementen etwas konstruktiv Neues zu bilden. Das ist das Mehr über die Summe der Teile hinaus.
Integration ist eine wertschaffende, eigenständige Leistung. Sie ist kategorial anders als das, was die zusammengefügten Elemente zum Ganzen beitragen. Sie besteht in der Auswahl und im Verbinden von Einzelteilen, damit nützliches Neues entsteht, bei dem die Einzelteile quasi unsichtbar sind.
—
Softwareentwicklung braucht einen differenzierten Blick für alle Arten von Abstraktionen. Hier habe ich drei beschrieben - von denen ich die Integration für am entwicklungsbedürftigsten innerhalb von Softwareprojekten halte.
So weit die recht theoretische Auseinandersetzung mit dem Thema. Im nächsten Artikel will ich das mit einem Beispiel konkreter machen.