WinForms mit Elm Architektur

 

Nachdem ich vor einem Jahr mich angefangen habe mit Elm Architektur zu beschäftigen und das Entwickeln damit zu lieben gelernt habe, war die Desktop-Entwicklung mit WinForms ein gefühlter gewaltiger Rückschritt. Vor diesem Hintergrund habe ich mich auch mit der Frage beschäftigt, ob es möglich ist, und wie schwer es wäre, die Elm Architektur nach WinForms zu bringen.

Hintergrund zur Elm Architektur

Zuerst mal kann ich jedem nur empfehlen die Dokumentation zu lesen. Zurück? Wunderbar!

Die Elm Architektur hat aus meiner Sicht viele Punkte, die für sie sprechen. Heute will ich im speziellen den Aspekt der „Subscriptions“ betrachten.

Subscriptions sind dafür gedacht, die An-/und Abmeldung an Ereignissen zu ersetzen.

In C# ist die normale Anmeldung an einem Event wie folgt möglich:

 

Im obigen Beispiel wird die Anmeldung imperativ durchgeführt. Die scheinbare Einfachheit verbirgt einige Tücken, auf die man in der Regel erst später stößt. Diese Tücken beinhalten, dass man als Entwickler neben der Anmeldung auch für die Abmeldung des EventHandlers verantwortlich ist. Vor allem wenn die Eventquelle länger lebt, als der Listener muss man darauf achten, um keine Event-Leaks zu produzieren.

Zwiebeln und Komponentenschichten

Um das Subscription Feeling in WinForms nachempfinden zu können, gibt es zwei Dinge zu beachten:

1) Wie kommen die Subscriptions aus den geschachtelten Komponenten bis nach oben zur Integrationswurzel?

2) Wie kann ein rohes Ereignis in die notwendige Message-Struktur gebracht werden, so dass sie bis zu ihnen zugestellt wird?

Betrachten wir die Problemstellungen nacheinander und rollen das Ganze von hinten auf.

Eine Komponente verarbeitet nur eine Message ihres Typs, nennen wir diesen Typen erst mal T1. Damit eine Subscription also zugestellt werden kann, brauchen wir eine Funktion, die das rohe Ereignis in den Message Typ der Komponente transformieren kann. Da wir in WinForms unterwegs sind, nehmen wir erst mal an, dass ein rohes Ereignis vom Typ EventArgs ist. Das bedeutet, wir benötigen eine Funktion f: EventArgs -> T1. Mit dieser Funktion kann also eine Message erzeugt werden, welche die Komponente auch konsumieren kann.

Eine Schicht darüber liegt eine weitere Komponente. Diese konsumiert nur Nachrichten vom Typ T2. Damit das Ereignis weitergegeben wird, muss es also auch in den Typ T2 eingepackt werden. Wir benötigen also eine Funktion g: T1 -> T2.

Wenn noch weitere Schichten folgen, können wir das gleiche Prinzip weiter anwenden. Am Ende haben wir eine Funktionsverkettung wie folgt:

s: ((EventArgs -> T1) -> T2) -> … -> Ti

Oder anders geschrieben:

s: i(h(g(f(EventArgs))))

In C# könnte das wie folgt aussehen:

wobei Sub wie folgt definiert ist:

Mit der Select Methode haben wir auch schon einen Teil des ersten Problems adressiert. Der zweite Teil stellt sich, sobald man mehr als eine Kind-Komponente hat. Dieses Problem löst sich auf, wenn wir die Subscription als Listen abfragen. Damit haben wir auch vermieden, dass man null in Betracht zieht, um auszudrücken, dass keine Subscriptions benötigt sind. In diesem Fall nehmen wir einfach die leere Liste.

Wenn wir nun mehrere Kind-Komponenten haben, können wir ihre Subscriptions einfach konkatenieren und ggf. unsere eigenen ergänzen.

Proof of concept

Um unsere Überlegungen am Ende noch auf die Probe zu stellen, habe ich einen Proof of concept implementiert. Das Ziel war hierbei keine optimierte Implementierung, sondern es hat mir gereicht, wenn es kompiliert und funktioniert.

Winforms mit Elm-Architektur, generic.de AG

Oberfläche des Proof of concept

Als Anwendung habe ich mich dabei für die oben abgebildete Liste an Zählern entschieden. Die Anwendung so strukturiert, dass ein einzelner Zähler eine eigene Komponente ist. Mehrere dieser Zähler werden in einer weiteren Komponente gehalten. Jede Sekunde sollen die Zähler hochzählen. Um das Hochzählen zu realisieren, soll eine Subscription verwendet werden, die über einen Timer realisiert ist.

Das Ergebnis des Proof of concept habe ich auf GitHub hochgeladen. Wie gesagt, es ist sicherlich nicht reif für den produktiven Einsatz. Dazu war es auch nicht gedacht.

Fazit

Ausgehend von der eher theoretischen Überlegung, wie das Subscription Handling der Elm Architektur auch in WinForms funktionieren könnte, sind wir am Ende bei einem funktionierenden Proof of concept angelangt. Persönlich bin ich schon sehr gespannt, wie weit ich das obige Konzept noch treiben kann, um ggf. die noch fehlenden Elemente der Elm Architektur einbauen zu können. Wer weiß, vielleicht wollen dann plötzlich mehr Entwickler wieder für WinForms entwickeln.

 

Vielen Dank für’s Lesen.