Follow my new blog

Sonntag, 21. Februar 2010

Event-Based Components – Der nächste Schritt der Komponentenorientierung?

Wie wäre es eigentlich, wenn wir Komponenten zwar synchron, aber nachrichtenorientiert koppeln würden? Wie wäre es, wenn EDA – Event-Driven Architecture – nicht nur eine Sache großer Anwendungen wäre, sondern in Form von Event-Based Components (EBC) auch kleine Applikationen evolvierbarer gestalten helfen würde?

Über die Vorteile von Komponentenorientierung an sich möchte ich mich hier nicht schon wieder auslassen ;-) Komponenten als binäre Codeeinheiten mit separatem Kontrakt sind für mich schlicht die Basis jeder bewussten Anwendungsarchitektur.

Wie solche Komponenten aber definiert und dann in Code gegossen werden, darüber lässt sich immer wieder nachdenken. Bisher habe ich den folgenden Ansatz vertreten:

  • Die Architektur einer Software resultiert in einem Komponentenabhängigkeitsdiagramm.
  • Die Implementation der Komponenten verteilt sich auf zwei Assemblies: eine für den Kontrakt, eine für die Funktionalität.
  • Wenn Komponenten andere brauchen, d.h. von ihnen abhängig sind, dann werden diese Komponenten der abhängigen von einem DI Container injiziert.

Komponentenorientierung “traditionell”

Hier eine ganz einfache Architektur mit drei Komponenten für ein Szenario, dass Ihnen bekannt vorkommen sollte ;-)

image

Daraus ergäben sich die Assemblies:

  • compiler.contract.dll
  • compiler.dll
  • parser.contract.dll
  • parser.dll
  • codegenerator.contract.dll
  • codegenerator.dll

compiler.dll würde compiler.contract.dll referenzieren, weil sie deren Kontrakt exportiert, d.h. implementiert. Und compiler.dll würde parser.contract.dll sowie codegenerator.contract.dll referenzieren, weil sie deren Kontrakte importiert. Zur Laufzeit braucht compiler.dll dann natürlich Implementationen dieser Kontrakte. Kennen tut sie deshalb parser.dll und codegenerator.dll nicht. Dependency Injection macht es möglich.

Und wie sehen die Kontrakte aus? Das hängt von den Diensten der Komponenten ab. Üblicherweise wird jede Komponente mindestens durch ein Interface für seine “Hauptdienstleistung” definiert.

compiler.contract.dll:

interface ICompiler

{

    Func<double, double> Compile(string formel);

}

parser.contract.dll:

interface IParser

{

    ASTNode Parse(string formal);

}

codegenerator.contract.dll:

interface ICodeGenerator

{

    Func<double, double> Translate(ASTNode program);

}

Und wo ist der ominöse ASTNode definiert? Da zwei Komponenten ihn brauchen, die nicht von einander abhängen, brauchen wir noch eine weitere Assembly:

compiler.datenmodell.contract.dll:

class ASTNode

{

   

}

Diese reine Kontraktassembly wird von allen anderen referenziert. Sie enthält einen allen gemeinsame Vorstellung davon, wie Code im Compiler repräsentiert werden sollte: als abstrakter Syntaxbaum.

Die Implementationen der Komponenten ist wenig spannend. Nur den Compiler möchte ich hervorheben. Er ist ja abhängig von den anderen beiden Komponenten und muss daher Instanzen von ihnen zur Laufzeit bekannt gemacht bekommen:

class Compiler : ICompiler

{

    private readonly IParser parser;

    private readonly ICodeGenerator codegenerator;

    public Compiler(IParser parser, ICodeGenerator codegenerator)

    {

        this.parser = parser;

        this.codegenerator = codegenerator;

    }

    public Func<double, double> Compile(string formel)

    {

        var program = this.parser.Parse(formel);

        return this.codegenerator.Translate(program);

    }

}

Diese Bekanntmachung ist kein Hexenwerk. Mit einem DI Container ist das ganz einfach. Der übernimmt die Befüllung der Ctor-Parameter bei Instanzierung eines Compiler-Objektes. Hier ein Beispiel mit Microsoft Unity:

IUnityContainer uc = new UnityContainer();

uc.RegisterType<IParser, Parser>();

uc.RegisterType<ICodeGenerator, CodeGenerator>();

uc.RegisterType<ICompiler, Compiler>();

ICompiler c = uc.Resolve<ICompiler>();

var f = c.Compile("2*x");

Soweit meine bisherige Vorstellung von Komponentenorientierung. Das funktioniert alles wunderbar. Ist erprobt. Läuft. Alles kein Problem.

Und doch… irgendwie bin ich nicht so ganz zufrieden damit. Mich sticht der Zweifel, ob das schon der Weisheit letzter Schluss ist in Bezug auf “Composability”. Kann man Komponenten, also die architekturellen Grundbausteine von Software, in dieser Weise schon optimal “zusammenstecken”?

Kritik der “traditionellen” Komponentenorientierung

Wenn Komponenten Bausteine sein sollen wie Lego-Bausteine oder elektronische Bauteile, dann, so glaube ich, widerspricht dem, wie wir mit ihren Abhängigkeiten umgehen.

Erstens dürfen Komponenten beim “traditionellen” Ansatz überhaupt Abhängigkeiten haben. Das scheint mir nicht ganz passend in Bezug auf den Baustein-Begriff. Ein Lego-Baustein braucht keine anderen. Er passt mit anderen zusammen, aber er braucht sie nicht. Dasselbe gilt für einen Transistor oder einen Widerstand oder auch einen Prozessor. Keines dieser Bauteile braucht ein anderes zum Funktionieren. Es braucht Signale (Input) im Rahmen einer Spezifikation, aber woher diese Signale kommen und wohin der eigene Output geht… das ist den Bauteilen egal. Davon wissen sie nichts. Dafür ist die Platine zuständig, in der sie stecken.

Zweitens injizieren wir die Abhängigkeiten in Komponenteninstanzen. Wir verändern die Komponenten also zur Laufzeit, um ihre dynamischen Abhängigkeiten zu befriedigen. Das fühlt sich zumindest irgendwie merkwürdig im Vergleich zu realweltlichen Bausteinen an. Und es kann für Testzwecke relativ aufwändig sein, wenn dafür Attrappen gebaut werden müssen.

Drittens sind die Abhängigkeiten gewöhntlich definiert in Form von Interfaces. Es geht also immer um Bündel von Operationen. Eine abhängige Komponente läuft damit aber Gefahr, Zugriff auf Operationen zu bekommen, die sie nichts angehen. Im obigen Beispiel wird das nicht deutlich, aber sobald Servicekomponenten mehreren Clientkomponenten dienen, wachsen ihre Kontrakte schnell über das hinaus, was einer dieser Clients von Ihnen braucht.

Viertens ist die Spezifikation einer Komponente nicht sehr kompakt, wenn ich dafür mehrere Kontrakte anschauen muss. Sie besteht ja aus dem exportierten und allen importierten Kontrakten. Wenn ich wissen will, wie eine Komponente zu implementieren ist, dann sind 1+n Kontrakte zu konsultieren. Es gibt keinen einen Ort, an dem kompakt beschrieben ist, wie eine Komponente mit ihrer Umwelt interagiert.

Fünftens tue ich mich immer noch schwer mit der Schachtelung von Komponenten. Im Beispiel oben habe ich zwar drei hübsche Komponenten, aber eigentlich würde ich sie anders bezeichnen und in eine umfassendere einschachteln wollen:

image

Erst diese umfassende Codeeinheit wäre der Compiler. Die bisherigen sind nur seine Bausteine. Mit der bisherigen Komponentenorientierung finde ich das zu planen aber nicht so einfach. Wie soll die umfassende Codeeinheit auch eine Komponente sein, wenn es ihre Konstituenten auch sind?

Das sind für mich genügend Gründe, weiter darüber nachzudenken, wie Komponenten noch besser gebaut werden können, so dass Software wahrhaft aus Bausteinen besteht.

Ein Ansatz dafür, den ich gerade spannend finde, ist die konsequente Ereignisorientierung.

Komponentenkommunikation über Ereignisse

Fünf Gründe, über die “traditionelle” Komponentenorientierung nachzudenken. Aber geht es anders irgendwie auch fünf Mal besser? Ich glaube, schon. Mit der Ereignisorientierung lösen sich manche Probleme in Luft auf und anderes wird klarer, wie mir scheint.

image Auf geht´s… Was bedeutet Ereignisorientierung für Komponenten? Zum Thema gibt es ein interessantes Buch: Event-based Programming von Ted Faison. Das hat mich auf die Spur von Event-Based Components gebracht. Allerdings weicht meine Vorstellungen in einigen Punkte von der des Buches ab. Mir ist seine Darstellung gerade bei der Praxis ein wenig zu allgemein. Aber die Grundgedanken darin finde ich sehr spannend… Hier deshalb meine Version ihrer Implementation.

Ich fange mal mit einer etwas anderen Notation an als der bisherigen. Die ist den Wire-Diagrams (Signaldiagramme) des Buches angelehnt. Hier zum warm werden eine ganz simle Event-Based Component (EBC), die als Dienstleistung die Verarbeitung eines Kommandos anbietet. Kommando bedeutet dabei im Sinne der Command-Query Separation, dass kein Resultat an den Aufrufer geliefert wird.

image

Ein Rechteck statt eines Kreises bzw. einer Ellipse ist nur ein oberflächlicher Unterschied zwischen den Diagrammen. Die wahre Andersartigkeit steckt in der Implementation. Hier der Kontrakt für die Komponente:

interface IEventBasedComponent

{

    void ProcessIncomingCommand(IncomingCommand cmd);

}

Wie bei der “traditionellen” Komponentenorientierung ist der Kontrakt einer EBC ein Interface. Für jede eingehende Nachricht, die eine Komponente "versteht”, gibt es darin eine Methode mit einem Parameter. Der hat einen für jede Nachricht spezifischen Typ.

Ob die Methode dann wie oben den Typ ihrer Nachricht im Namen widerspiegeln sollte oder nicht, weiß ich noch nicht so recht. Im Augenblick ist das mal meine Konvention inklusive eines Prefixes wie “Process”/”Execute” (für Kommandos) oder “Fulfill”/“Inquire” (für Queries).

EBC-Methoden für eingehende Nachrichten haben damit keine normale Signatur mit vielen Parametern oder einem Return-Wert. Es sind immer nur void-Methoden mit nur einem Parameter.

Jetzt zu ausgehenden Nachrichten oder besser zum Output. Denn um nichts anderes handelt es sich, auch wenn solche Nachrichten eine Antwort erwarten, wenn sie eine Komponente verlassen.

image

Hier unterscheiden sich Event-Based Components nun deutlich von den “traditionellen”. Output-Nachrichten werden ausschließlich über Events verschickt. Der Kontrakt sieht dafür dann so aus:

interface IEventBasedComponent

{

    event Action<OutgoingCommand> OnOutgoingCommand;

}

Das ist der Trick an der ganzen Sache mit der Ereignisorientierung: Nachrichten an andere Komponenten werden als Events verschickt. Wieder haben die Methoden nur einen Parameter und keinen Rückgabewert.

Bei der Benennung der Delegaten folge ich wieder einem Muster. Als Name verwende ich wieder den Nachrichtentyp und setze einen Präfix davor, z.B. “On” oder “Issue” (für Kommandos) oder “Request” (für Queries).

Hört sich irgendwie nicht so spektakulär an, oder? Ich glaube aber nach erster Evaluation, dass die Beschränkung der Kommunikation auf synchrone Input- und Output-Nachrichten in dieser Weise grundsätzliche und positive Folgen hat. Doch davon ein andermal…

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.