Follow my new blog

Freitag, 19. Februar 2010

Einfacher im Gleichschritt – Dienstaufrufe mit der CCR synchronisieren

In der dotnetpro 3/2010 gehen Tobias Richling et al. in einem Artikel der Frage nach, wie asynchrone WCF-Dienstaufrufe synchronisiert werden können. Sie überlegen also, wie ein Client, der Service S1 und Service S2 asynchron (!) aufruft, es so einrichten kann, dass er erst weiter macht, wenn beide (!) Services ein Resultat geliefert haben.

Bei synchronen Aufrufen stellt sich diese Frage nicht. Da geht es ohnehin erst weiter im Client, wenn beide Aufrufe zurückgekehrt sind.  Hier das Beispiel dazu aus dem Artikel:

image

Im Client geht es erst nach der letzten Zeile weiter, wenn sowohl GetArticleList() wie auch GetSpecialOfferArticle() synchron aufgerufen wurden.

Symptomkur

Was aber, wenn beide Aufrufe asynchron sind? Bei Silverlight ist das gar nicht anders möglich. Und bei anderen Client-Plattformen sollten Sie auch darüber nachdenken, denn asynchrone Aufrufe bringen Performancevorteile. Sie laufen ja parallel, egal ob der Client mit einem oder mehreren Prozessorkernen ausgestattet ist.

Im Artikel sieht ein asynchroner Aufruf mit WCF so aus:

image

Nicht ganz trivial, oder? Und davon gibt es zwei – die dann eben, wenn nacheinander ausgeführt, zum Problem führen: Der Client muss irgendwie dafür sorgen, dass er auf die Ergebnisse von proxy.GetArticleListAsync() und proxy.GetSpecialOfferArticleAsync() wartet, bevor er weiter macht.

Wie das gehen kann, dazu macht sich der Artikel Gedanken und stellt am Ende eine allgemeine Lösung vor. Mit der sieht das Warten dann so aus:

image

Man registriert einen Eventhandler (AllComplete), der gerufen wird, wenn schließlich beide Dienstaufrufe zurückgekehrt sind, registriert die Dienstaufrufe (AddServiceCall()), die getätigt werden sollen und startet sie schließlich (ExecuteAll()). Das alles macht möglich ein sog. ServiceCallSynchronizer.

Das sieht auf den ersten Blick doch ordentlich aus. Auf den zweiten schleicht sich bei mir aber zumindest kleines Unbehagen ein, weil die Dienstaufrufe nicht typsicher registriert werden. Die Methodennamen sind als Zeichenketten anzugeben. Ob es eine Methode gibt, stellt sich also erst zur Laufzeit heraus. Ebenso gibt es keine Argumentprüfung zur Compilezeit, weil ja AddServiceCall() nicht weißt, ob und welche Parameter eine Dienstmethode hat. Das ist beides nicht schön.

Wer mit WCF arbeiten muss, mag den Autoren aber dennoch danken. Sie lösen mit Liebe ein Problem, das ansonsten ein Projekt ins Stocken bringen kann. Immerhin umfasst der allgemeine Aufrufsynchronisierer einige Dutzend Zeilen, über die Sie sich nun nicht mehr den Kopf zerbrechen müssen:

image

Wer die Lösung im Detail sehen möchte, der lese den Artikel.

Das Übel an der Wurzel packen

Ich habe nichts gegen WCF – aber dieses Beispiel zeigt mir wieder, dass ich mit WCF nicht direkt arbeiten möchte in meinem Anwendungscode. Ich halte die im Artikel vorgestellte Lösung für eine Symptomkur. Das Symptom: WCF bietet keinen Synchronisationsmechanismus für asynchrone Aufrufe.

Und was ist das Grundproblem, wenn das nur ein Symptom ist? WCF ist das Grundproblem. Denn WCF ist ein Frameworkbolide, der immer noch in der synchronen Kommunikation verwurzelt ist. Asynchronizität geht zwar auch irgendwie, ist aber nicht der Fokus. Dabei ist Asynchronizität – zumindest aus meiner Sicht – eine unverbrüchliche Bedingung für die Kommunikation in verteilten Anwendungen.

Aber ich will hier nicht philosophisch werden ;-) Lieber zeige ich einfach, wie aus meiner Sicht das Übel an der Wurzel gepackt werden könnte. WCF will ich dabei gar nicht ersetzen. WCF soll nur für mich als Anwendungsprogrammierer unsichtbar werden.

Hier meine Lösung:

image

That´s it.

Nur eine Kleinigkeit fehlt darin, die ich aber nur ausgelagert habe, um das Abstraktionsniveau einheitlich zu halten. Doch das ist auch nur eine simple Erweiterungsmethode:

image

Sieht das übersichtlich aus? Ist das alles typsicher? War das “out of the box” möglich? Drei Mal Ja.

Übersichtlich, typsicher und “out of the box” ist das alles, weil ich eben nicht WCF benutze, sondern ein Kommunikationsframework, das konsequent auf Asynchronizität setzt. Das ist der Xcoordination Application Space (AppSpace), der als Open Source Komponente bei CodePlex liegt und die Verteilung von Anwendungen erlaubt, die mit Microsofts Concurrency Coordination Runtime (CCR) arbeiten.

Wie funktioniert nun mein Code? Dazu müssen Sie verstehen, wie Dienste im AppSpace aufgerufen werden. Das geschieht immer über sog. Ports. Das sind typisierte Nachrichtenkanäle, die die CCR zur Verfügung stellt.

Dienste halten Sie also nicht in Form von Methoden in der Hand, sondern als Ports. Sie können AppSpace-Dienste nur indirekt anstoßen vermittels von Nachrichten. AppSpace-Anwendungen haben also immer eine Event-Driven Architecture (EDA), denn Dienste registrieren Eventhandler auf ihren Ports, um an die Nachrichten ihrer Clients zu kommen.

Einen Dienst einzurichten, ist mit dem AppSpace sehr einfach. Hier die Dienstimplementation:

public class MyArticleService : PArticleService

{

    [XcoConcurrent]

    internal void ProcessGetArticleList(GetArticleList query)

    {

        …

    }

 

    [XcoConcurrent]

    internal void ProcessGetSpecialOfferArticle(GetSpecialOfferArticle query)

    {

        …

    }

}

Und hier der Code, mit dem der Dienst gehostet wird, so dass Clients ihn aufrufen können:

using(var server = new XcoAppSpace("wcf.port=12345"))

{

    server.RunWorker<PArticleService, MyArticleService>("TheArticleService");

    …

Das ist nicht sehr aufwändig, oder? Und ich muss gar nicht auf die Segnungen von WCF verzichten, wie der Konfigurationsstring des AppSpace zeigt. Nur sind mir die ganzen lästigen WCF-Details einerlei. Ich kann mich auf das Wesentliche konzentrieren: Geschäftslogik.

Und wo sind die ominösen CCR Ports? Die stecken im Kontrakt des Dienstes, denn natürlich hat der AppSpace auch sein ABC: Address, Binding, Contract. Die Adresse des Dienstes ist localhost:12345/TheArticleService. Das Binding ist Net.Tcp von WCF. Und der Contract sieht so aus:

[Serializable]

public class Article

{

    public string description;

}

 

public class PArticleService : PortSet<PArticleService.GetArticleList,

                                       PArticleService.GetSpecialOfferArticle>

{

    [Serializable]

    public class GetArticleList

    {

        public Port<Article[]> response;       

    }

 

    [Serializable]

    public class GetSpecialOfferArticle

    {

        public int forMonth;

        public int forYear;

 

        public Port<Article> response;

    }

}

Das sieht für Sie sicher ungewöhnlich aus. Aber im Grunde ist es nicht viel anders als bei WCF. Ein AppSpace-Dienst hat auch eine Schnittstelle. Die besteht aber nicht aus einem interface sondern aus einer Liste von CCR Ports (PArticleService). Auf jedem Port “lauscht” dann ein Eventhandler (s. Process…() Methoden in der Dienstimplementation).

Ports haben keine Signatur, sondern transportieren nur Nachrichten eines Typs. Deshalb müssen die üblichen Dienstmethodensignaturen in Nachrichtentypen übersetzt werden. Aus

Article GetSpecialOfferArticle(int forMonth, int forYear);

wird dann ein Port für GetSpecialOfferArticle-Nachrichten. Und jede Nachricht enthält die ursprünglichen Methodenparameter als Felder. Ein Resultat liefert die Dienstmethode schließlich via einem Antwort-Port (response).

Das ist anders als bei WCF und Webservices. Aber ich würde sagen, es ist nicht schwer zu verstehen. Vor allem ist es aber 1. symmetrisch und 2. weniger trugschlussbehaftet.

Die Symmetrie zwischen Client und Service ergibt sich daraus, dass die Kommunikation in beide Richtungen via Ports läuft. Dadurch werden z.B. Notifkationen ein Kinderspiel. Und Sie unterliegen weniger Trugschlüssen bzgl. der Kommunikation, weil Sie sofort sehen, dass sie verteilt/asynchron ist, dass sie damit mal länger dauern kann, dass der Dienst auch mal nicht antworten kann usw.

Der Client holt sich dann einen “Proxy” für einen entfernten AppSpace-Dienst mit ConnectWorker<T>(). Der bietet Ports entsprechend dem Kontrakt, an die der Client seine Nachrichten schickt.

Und jetzt der Trick mit der Synchronisation: Dafür muss ich keinen Aufwand treiben, weil die CCR einen Synchronisationsoperator bietet, ein Join. Mit ihm kann der Client ganz einfach darauf waren, dass Nachrichten in mehreren Ports angekommen sind. Im Beispiel sind das die Antwort-Ports, die der Client in die Nachrichten an den Dienst gesteckt hat.

Eine Anweisung (client.Join()) genügt daher, um den Client-Code, der mit den Ergebnissen beider Dienstaufrufe weiter arbeiten sollen, erst dann auszuführen, wenn auch beide Dienste geliefert haben. Egal, in welcher Reihenfolge sie fertig geworden sind.

Fazit

Ich kann mir nicht helfen, aber ich finde die AppSpace/CCR-Lösung verständlicher, kürzer und “ehrlicher”. Das liegt für mich daran, dass AppSpace/CCR für Asynchronizität gemacht sind. Wo WCF sich strecken muss, um seinem synchronen Erbe zu entwachsen, da sind AppSpace/CCR schon lange angekommen.

Asynchronen Code zu schreiben, ist ungewohnt. Aber mit ein wenig Übung werden Sie feststellen, dass schon in relativ einfachen verteilten Szenarien dadurch viele Vorteile entstehen. Gerade für den “kleinen Verteilungshunger zwischendurch”, d.h. dort, wo Sie bisher nicht an Verteilung zu denken gewagt haben, da bieten AppSpace/CCR Ihnen eine Plattform, die es Ihnen einfach macht, den Einstieg zu finden. Ob die Kommunikation dann “auf dem Draht” mit WCF (TCP, Named Pipes) oder MSMQ oder Jabber oder Named Pipes läuft, das ist Ihnen egal. Ihr Programmiermodell ist immer gleich. Probieren Sie es mal aus.

Samstag, 16. Januar 2010

Ja, wo programmiert er denn? [endlich-clean.net]

Das Jahr beginnt gleich wieder gut: Mit einer Menge Veranstaltungen. Und alle stehen Sie im Zeichen von Clean Code Developer (CCD). Das freut mich sehr, denn gedacht hätte ich das vor 12 Monaten nicht, als Stefan Lieser und ich die CCD Initiative gestartet haben. Irgendwie haben wir damit aber einen Nerv der Entwickler-Communities getroffen…

(Fast) Kein .NET-Event ist heute mehr ohne CCD-bezogenen Vortrag. Das ist toll! Denn damit ist der Beweis erbracht, dass Entwickler nicht nur an neuesten Technologien, sondern auch an Qualität interessiert sind. Es wäre schön, wenn wir uns dadurch als Branche ein wenig von der pessimistischen Sicht Nicolai Josuttis´ entfernten, der die Code-Qualität schon als tot proklamiert hat.

Und wo gibt es nun CCD live zu sehen? Wo können Sie mit Stefan und mir face2face diskutieren?

  • imageDen Anfang macht die OOP in München im Januar. Dort sind wir am 28.1. um 9h frisch und munter auf der Bühne – und verteilen auch eine ganze Reihe “CCD Devotionalien” ;-)
  • Im Februar folgt die VSone 2010 – ebenfalls in München, quasi unserem derzeitigen CCD-Hauptquartier. Stefan hält einen Vortrag über Domain Driven Design, das wir nahe an CCD sehen und macht einen TDD Workshop. Und ich spreche über Refactoring von Brownfield-Projekten und machen einen Workshop über… ebenfalls TDD, aber in Verbindung mit dem Thema Architektur. Beides liegt für mich dicht beieinander. “Ralf programmiert” heißt der Workshop, weil mir sehr daran gelegen ist, am Code zu demonstrieren, wie Konzepte und Praktiken zusammen kommen. Wir werden Software entwerfen, im Kleinen wie im Großen. Und wir werden eine Praktik wie TDD üben und die Brille des Flow-Patterns aufsetzen. Ich denke, das führt dann zu fruchtbaren Diskussionen und einigen Aha-Erlebnissen.
  • Im März ist lädt dann die dotnetpro zu 3 Tagen voller CCD ein: dem powerday, dem powerworkshop und dem powercoaching. Der powerday ist ein Präsentationstag, der einen Überblick über viele Aspekte von CCD geben soll; die besondere Herausforderung dabei an uns: Wir wollen auf der Bühne Brownfield-Code aus dem Publikum refaktorisieren. Ich bin gespannt, was die Teilnehmer uns da so einreichen. Während des powerworkshops wollen wir dann mit den Teilnehmern einen Ausschnitt an image Prinzipien und Praktiken von CCD einüben. Die Themen sind dann nicht so breit, es geht dafür in die Tiefe. Und das powercoaching ist eine Veranstaltung, bei der auch der Chef gern dabei sein kann; denn da geht es um den Review von konkretem Projektcode. Wir wollen uns mit Teams ihre Architektur und den Code-IST-Zustand ansehen. Das passiert ebenfalls auf einer (kleinen) Bühne und ist insofern ein Experiment. Experimentell ist natürlich nicht der Review – der funktioniert garantiert. Aber wir glauben, dass auch ansonsten Unbeteiligte aus so einem Review von Code Dritter etwas lernen können. Deshalb dürfen andere Teams, die ebenfalls zum Coaching kommen, das Coaching anderer verfolgen.
  • Mit diesen Veranstaltungen aber nicht genug CCD! Von Januar bis April laufen auch noch zwei CCD-Trainings – natürlich in München ;-) Bei der School of .NET sind da sogar noch 1-2 Plätze frei. Wer also kurzentschlossen noch mitmachen will, der ist herzlich eingeladen.

Wo ich im ersten Quartal programmiere, dürfte damit klar sein :-) Vielleicht haben Sie ja Lust, uns hier oder da zu begegnen. Dann schauen Sie doch vorbei und diskutieren mit uns über Ihre Erfahrungen mit CCD oder löchern uns mit Fragen. Wir freuen uns drauf.

Montag, 4. Januar 2010

Aspekttrennung im GUI

Mich wundert, dass es bisher noch niemand bemerkt zu haben scheint: Der WinForms-Designer (und auch der WPF-Designer) vermischt zwei Aspekte bei der GUI-Programmierung. Das sind die Aspekte Gestaltung und Funktionalität. Dass er das tut, finden wir natürlich alle irgendwie bequem. Doch mich beschleicht das Gefühl, dass hier zuviel des Guten getan wird.

Ein Beispiel ein kleines Formular zum Addieren zweier Zahlen:

image

Da steckt etwas Gestaltung drin –  Steuerelemente wollten ausgewählt und angeordnet werden – und Funktionalität – Ereignisbehandlungsroutinen wollten hinter die Steuerelemente gestellt werden.

Die Programmierung war ganz einfach: Steuerelemente auf das Formular ziehen (Gestaltung), Doppelklick auf ein Steuerelement, um einen Eventhandler zu schreiben (Funktionalität). So weit, so gut. Das möchte ich kaum anders haben.

Wenn ich genau hinschaue, stört mich aber das Ergebnis dieser einfachen Programmierung im Code. Das sieht nämlich so aus:

public partial class WinDoTheMath : Form

{

    public WinDoTheMath()

    {

        InitializeComponent();

    }

 

    private void button1_Click(object sender, EventArgs e)

    {…}

 

    private void textBox1_Validating(object sender, CancelEventArgs e)

    {…}

}

Ganz normal – aber subtil verstörend. Denn was mir hier fehlt, das ist eine Zuordnung der Ereignisbehandlungsroutinen. Die “stehen im Code nur so rum”. Ich muss mir anhand der im Methodennamen steckenden Hinweise zusammenreimen, wie/wann sie zum Einsatz kommen.

Das finde ich inzwischen irgendwie umständlich – oder anders ausgedrückt: Der Zwang zum Zusammenreimen erhöht für mich die Komplexität des Codes. Ich sehe einfach nicht die Abhängigkeiten bzw. Zusammenhänge. Die hat der Designer nämlich im code behind versteckt:

private void InitializeComponent()

{

    …

    //

    // textBox1

    //

    this.textBox1.Location = new System.Drawing.Point(12, 12);

    this.textBox1.Name = "textBox1";

    this.textBox1.Size = new System.Drawing.Size(55, 20);

    this.textBox1.TabIndex = 0;

    this.textBox1.Validating +=

           new System.ComponentModel.CancelEventHandler(this.textBox1_Validating);

    //

    // textBox2

    //

    this.textBox2.Location = new System.Drawing.Point(121, 12);

    this.textBox2.Name = "textBox2";

    this.textBox2.Size = new System.Drawing.Size(55, 20);

    this.textBox2.TabIndex = 1;

    this.textBox2.Validating +=

           new System.ComponentModel.CancelEventHandler(this.textBox1_Validating);

    //

    // button1

    //

    this.button1.Location = new System.Drawing.Point(202, 9);

    this.button1.Name = "button1";

    this.button1.Size = new System.Drawing.Size(75, 23);

    this.button1.TabIndex = 2;

    this.button1.Text = "Add";

    this.button1.UseVisualStyleBackColor = true;

    this.button1.Click += new System.EventHandler(this.button1_Click);

    …

Sehen Sie die Zusammenhänge? Die stecken in der jeweils letzten Zeile der Codeblöcke für die Steuerelemente. Dort wird der Eventhandler dem Control zugewiesen. Da geht es um Funktionalität. Und was steht davor? Das geht es um die Gestaltung.

Gestaltung und Funktionalität sind im code behind vermischt. Das ist gut gemeint vom Designer – aber ich finde das nicht mehr verständnisfördernd. Früher war das genial; heute fühle ich mich dadurch verwirrt. Liegt das am Alter? ;-) Nein, ich glaube, das liegt am durch Clean Code Developer geschärften Blick für Verständlichkeit und Abhängigkeiten.

Wo Zusammenhänge nicht sofort klar werden, da regt sich ein ungutes Gefühl bei mir. Und unklar sind sie, wenn ich nicht dort, wo ich Code lese – also im “foreground code” –, mich leicht darüber informieren kann, wer die beteiligten Parteien sind und wie sie zusammen hängen. Weder weiß ich dort, welche Steuerelemente es gibt, noch weiß ich, wie daran Ereignisbehandlungsroutinen geknüpft sind. Das finde ich unschön.

Ich werde daher in Zukunft nicht mehr einfach auf Steuerelementen doppelklicken, um mir Eventhandler generieren zu lassen! Stattdessen schreibe ich die von Hand. Dabei kann ich dann auch wählen, wie ich sie implementiere: ob als eigenständige Methode oder doch nur als Lambda Funktion:

public partial class WinDoTheMath : Form

{

    public WinDoTheMath()

    {

        InitializeComponent();

 

        this.textBox1.Validating += ValidateNumberEntered;

        this.textBox2.Validating += ValidateNumberEntered;

 

        this.button1.Click += (s, e) =>

              {

                  var sum = int.Parse(textBox1.Text) + int.Parse(textBox2.Text);

                  MessageBox.Show(string.Format("Sum: {0}", sum));

              };

    }

 

    private void ValidateNumberEntered(object sender, CancelEventArgs e)

    {

        int i;

        e.Cancel = !int.TryParse(((TextBox) sender).Text, out i);

 

        this.errorProvider1.SetError((Control)sender,

                                     e.Cancel ? "Input is not an integer!" : "");

    }

}

Jetzt habe ich alles auf einen Blick in der Codeansicht: die wirklich relevanten Steuerelemente mit ihrer Funktionalität.

Und ich habe die beiden Aspekte Gestaltung und Funktionalität sauber getrennt. Der Designer ist jetzt wirklich nur noch für die Gestaltung zuständig. Die schaue ich mir im Design View an – und der Code dafür liegt irgendwo verborgen, weil er mich für die Funktionalität, mit der ich sonst beschäftigt bin, nicht interessiert.

Das finde ich sauber. Wer noch?

Freitag, 1. Januar 2010

%%TITLE%%

%%CONTENT%%

Donnerstag, 31. Dezember 2009

Linksammlung zum Thema Monads [OOP 2010]

image Bei der Beschäftigung mit Funktionaler Programmierung stoße ich immer wieder auf den Begriff Monad. Leider bringt mich jedoch der zugehörige Wikipedia-Artikel bei dessen Verständnis nicht weiter. Deshalb habe ich jetzt ein wenig gegooglet. Für mich als Entwickler, der bisher (vor allem) mit imperativen Programmiersprachen zu tun hatte, sind dabei die folgenden Beiträge herausgekommen, die ich als verständnisfördernd empfinde.
So ganz bin ich mit diesen Beiträgen allerdings immer noch nicht zufrieden. Ich finde die Erklärungen für Monads immer noch recht theoretisch und fern der imperativen objektorientierten Programmierpraxis.

Wie würde ich nun Monads erklären? Hm... mal sehen. In einem zukünftigen Beitrag versuche ich das mal.

Samstag, 26. Dezember 2009

Neues Format für längere Blogartikel? [OOP 2010]

Es gibt keine formale Beschränkung für die Länge von Blogartikeln. Und so habe ich bisher kürzere wie längere unterschiedslos hier gepostet. Nun mir jedoch Zweifel gekommen, ob ich das auch weiterhin tun soll.

Online Zeitungen verfahren ja schon lange so, dass sie längere Beiträge splitten. Beispiel www.zeit.de: Artikel werden zunächst häppchenweise gezeigt, wie Sie hier sehen können; erst wenn Sie “Auf einer Seite lesen” anklicken, bekommen Sie alles auf einer Seite. Die Zeit hat für solche verschiedenen “Views” natürlich ein hübsches Redaktionssystem, bei dem auch noch ein PDF rausfällt. Das kann ich mir natürlich nicht leisten. Was also tun?

Wie wäre es denn, wenn ich längere Beiträge nicht mehr direkt ins Blog schriebe, sondern wie folgt veröffentlichen würde:

Zustand als Abhängigkeit - IoC konsequent gedacht

Wenn Sie mögen, können Sie diese Ansicht auf den ganzen Bildschirm vergrößern. Oder Sie stellen vom “Book View” um auf den “Scroll View”, um quasi wieder alles auf einer langen Seite zu haben. Oder Sie drucken den Inhalt aus. Oder Sie exportieren ihn als PDF, um ihn auf Ihrem favorisierten ebook-Reader zu lesen.

Was meinen Sie? Wäre solche Differenzierung in der Publikation nicht eine gute Sache? Blogartikel, die ca. eine Bildschirmseite lang sind, veröffentliche ich weiterhin direkt im Blog. Aber Artikel von mehreren Bildschirmseiten oder gar mehreren Druckseiten biete ich Ihnen im obigen Format.

Denn seien wir ehrlich: Längere Texte am Bildschirm zu lesen, ist immer unschön. Es macht wenig Unterschied, ob sie häppchenweise präsentiert werden oder in einem Stück. Längere Inhalte brauchen Überblick. Den bietet nur ein Ausdruck – wofür ein Blog keine wirklich gute Grundlage ist – oder eine Ganzseitendarstellung im Hochformat auf einem speziellen Reader.

Vergleichen Sie einmal den Text im obigen Viewer bzw. nach Ausdruck mit der ursprünglichen Veröffentlichung. Welche Präsentation finden Sie lese- bzw. verständnisfreundlicher?

Nachtrag: In einigen Kommentaren zu diesem Posting wurde gegen solche Flash-Darstellung eingewandt, dass der Content dann nicht von Suchmaschinen indiziert würde. Das ist jedoch falsch wie diese Suche beweist: http://www.google.de/search?q="zustand+als+abhängigkeit"+site:scribd.com (die Einschränkung mit site:scribd.com habe ich nur gemacht, um den Link zum Dokument auf die erste Ergebnisseite zu bringen). Das erste Ergebnis ist ein Link auf das obige Dokument: http://www.scribd.com/doc/24512457/Zustand-als-Abhangigkeit-IoC-konsequent-gedacht.

Objektorientierung als Behaviorismus der Softwareentwicklung [OOP 2010]

Die Objektorientierung steht uns im Weg zu evolvierbareren Programmen. Ja, das glaube ich immer mehr. Nicht, dass ich Objekte missen möchte – sie sind nützliche und wichtige Strukturierungsmittel für Software.  Ich will natürlich Funktionseinheiten mit eigenem Zustand definieren können. Aber die Sprache der Objektorientierung wie sie C++, Java, C# und auch VB vorgeben, ist einfach nicht für Flexibilität und Evolvierbarkeit gedacht gewesen. Und da Sprache unser Denken bestimmt, kommen bei der Benutzung objektorientierter Sprachen eben nicht einfach flexible und evolvierbare Programme heraus.

Objektorientierung ist sozusagen die Brille der Programmier-Behavioristen. Wer durch sie blickt, vereinfacht die Welt extrem – manchmal bis zur Unkenntlichkeit. Wo die Welt der Behavioristen durch Reiz-Reaktion determiniert ist, da ist sie für “die Objektorientierten” durch Zustand und Synchronizität geprägt.

image Und so wie Behaviorismus bei der Dressur eines Tanzbären funktionieren mag, so funktioniert Objektorientierung auch bei einem simplen Malprogramm. Ok, sie “funktioniert” auch bei viel größeren Anwendungen – doch ich meine mit “funktionieren” nicht, dass objektorientierter Code am Ende läuft, sondern ob mit den Mitteln der Objektorientierung etwas heraus kommt, was sich auch weiterentwickeln lässt. Denn das ist ein wesentliches und trotzdem oft unterschätztes Qualitätsmerkmal von Software. Beim Tanzbären ist das hingegen nicht wichtig. Wenn der keine neuen Tricks mehr lernt, nimmt man einen neuen.

Wer hingegen mehr als einen Tanzbären dirigieren will, z.B. ein ganzes Unternehmen, der ist mit Behaviorismus schlecht bedient. Unternehmensführung funktioniert schlecht mit Zuckerbrot und Peitsche als Mittel, um Reize auszulösen und Reaktionen zu provozieren, die eine vielköpfige Belegschaft verlässlich zum Erfolg führen.

Dito die Objektorientierung in nicht mehr einfach überschaubaren Szenarien. Direkte, streng typisierte und synchrone Kopplung ist das falsche Denkmuster für evolvierbare Software. Solche Kopplung ist aber der Default der Objektorientierung. Nur mit Mühe und Disziplin kann man sich von ihm lösen. Das mag dann zwar das Expertentum von Softwareentwicklern ausmachen – doch wie in Panik die Moral schnell verdampft, so verflüchtigen sich unter Projektdruck auch solche programmierzivilisatorischen Errungenschaften. Was dann nicht durch die Sprache quasi erzwungen wird, das findet nicht statt.

Deshalb bin ich für möglichst hart verdrahtete neue Defaults – wenn schon nicht neue Sprachen, die die Evolvierbarkeit begünstigen. Ein solcher Default ist die echte Komponentenorientierung, bei der Komponentenimplementationen physisch alleingestellt in Komponentenwerkbänken stattfinden und Komponenten einander nicht mehr direkt referenzieren. Deshalb bin ich für Flows als ersten groben Implementationsansatz für jede Realisierung eines Features; denn in Flows gibt es keine Kopplung mehr zwischen den Schritten, so dass die Evolvierbarkeit sehr hoch ist. Deshalb bin ich für die Nutzung eines in-proc Bussystems, d.h. selbst wenn die Kommunikation noch lokal und synchron ist. Ein solcher Bus entkoppelt noch mehr als ein DI Container.

Komponentenorientierung begrenzt die OO-Defaults lokal auf die Implementation einer überschaubaren Komponente. Und DI Container, Flows sowie Busse sind neue Defaults für die Kopplung von Strukturen, wenn es unüberschaubar wird – was immer schneller passiert, als wir uns vorstellen.

Wie wäre es, damit einmal im neuen Jahr zu experimentieren? Der überkommene Programmierbehaviorismus würde einem differenzierteren Verständnis von Software weichen.