Dieses Blog durchsuchen

Samstag, 24. April 2010

1. MPT Beispiel - Teil 1

So, jetzt gibt es das erste praktische Beispiel zur Micro-Pattern-Trilogie.
Ich hatte gründlich darüber nachgedacht, ob ich extra für jedes Triplet eine eigene kleine Anwendung entwickeln soll, oder nicht. Aber ich dachte mir so, für das CML Triplet lohnt sich keine eigenständige Anwendung, weil es schlicht zu primitiv ist.
Ich hatte da aber schon mal etwas probiert, was vom Umfang her einem realistischen Beispiel würdig ist. Da kommt im Prinzip alles vor, außer des CPS Triplets. (Das ist aber nicht schlimm. Das kommt dann einfach ein anderes Mal dran.)
Weil Bilder bekanntlich mehr sagen, als tausend Worte, stelle ich die Anwendung einfach mal in einem kurzen Video vor:


(Oh Gott! Da kommt man sich teilweise blöd vor, wenn man mit 'nem Mikrofon spricht. Naja, ich bin nicht so der Rede-Mensch. Aber irgendwie fetzt das trotzdem! Ich werde sicher noch mehr Videos dieser Art machen. Aber da wird dann etwas mehr los sein, als in diesem Video. *g*)

Ja, wie du gesehen hast, wird es dynamisch. Das heißt, es werden zur Laufzeit neue Ansichten erzeugt, wobei jede einzelne Ansicht mithilfe des TFR Triplets den aktuell eingegebenen Text erhält. Außerdem - und das war damals im Prinzip der eigentliche Grund für dieses kleine Programm - bekommt das Hauptformular die verschiedenen Ansichten auf die selbe Weise.

Ich würde sagen, wir fangen mit dem einfachsten Teil der Software an. Das wäre in diesem Fall die Benutzeroberfläche, bzw. dessen Innenleben ... also der Quellcode.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace PFDynamic
{
 public partial class Form1 :
  Form,
  IAmTextTransmitter,
  IAmAddTextViewCaller,
  IAmTextViewReceiver
 {
  public Form1()
  {
   InitializeComponent();
  }

  public event EventHandler SendText;

  public string ReturnText()
  {
   return textBox1.Text;
  }

  private void textBox1_TextChanged(object sender, EventArgs e)
  {
   Say.Carefully(SendText, this);
  }

  private void button1_Click(object sender, EventArgs e)
  {
   Say.Carefully(AddTextView, this);
  }

  public event EventHandler AddTextView;

  public void ReceiveTextView(IAmTextView textView)
  {
   this.Controls.Add(textView.Control);
   Say.Carefully(SendText, this);
  }
 }
}

Das ist wirklich alles! Mehr manueller Quellcode steckt nicht in der Form1-Klasse.
Ich werde das Listing nun einzeln erklären.


Zeige mir deine implementierten Schnittstellen und ich sage dir, wer du bist!
Diese Aussage ist wörtlich zu verstehen. Sehen wir uns dazu den Klassenkopf an:

public partial class Form1 :
  Form,
  IAmTextTransmitter,
  IAmAddTextViewCaller,
  IAmTextViewReceiver

Ich weiß an dieser Stelle exakt 4 Dinge von Form1. Zum Einen ist sie von der Basisklasse "Form" abgeleitet. Das ist aber nichts Besonderes.
Viel interessanter sind die Schnittstellen! Diese kann ich direkt so lesen, als wären sie eine "Personenbeschreibung" von Form1.
"IAmTextTransmitter" bedeutet: "Ich bin ein Text-Transmitter." Somit weiß ich, dass Form1 aktiv einen Text versendet.
"IAmAddTextViewCaller" bedeutet: "Ich bin ein AddTextView-Caller." Somit weiß ich, dass Form1 sagt, dass eine Textansicht hinzugefügt werden soll.
"IAmTextViewReceiver" bedeutet: "Ich bin ein TextView-Receiver." Somit weiß ich, dass Form1 passiv eine Textansicht empfängt.

Ohne den eigentlichen Quelltext gesehen zu haben, weiß ich in dem Moment sofort, welche Rolle die Form1 in dem Gesamtsystem spielt. Das geht aber eben nur, wenn hier keine Willkürlichkeit herrscht.
Sehen wir uns dazu die Schnittstellen selbst einmal an. (Alle Schnittstellen sind natürlich im Namespace "PFDynamic" enthalten. Ich habe das nur zur Übersichtlichkeit weggelassen.)

public interface IAmTextTransmitter
{
 event EventHandler SendText;
 string ReturnText();
}

Zufällig passt diese Schnittstelle exakt zu dem theoretischen Beispiel meiner TFR Triplet Erklärung. ;-)
Man sieht hier, dass die Schnittstelle ein Event "SendText" und eine Methode "ReturnText()" fordert. Mehr nicht.
Die anderen beiden Schnittstellen sind genauso übersichtlich und erklären sich quasi selber, weshalb ich da nichts dazu sage.

public interface IAmAddTextViewCaller
{
 event EventHandler AddTextView;
}

public interface IAmTextViewReceiver
{
 void ReceiveTextView(IAmTextView textView);
}

Aber man sieht hier, dass die Schnittstellen nur das Nötigste enthalten. In der Regel sind es nicht mehr, als zwei Elemente. Das ermöglicht es, einer Klasse mal schnell eine Schnittstelle zuzuweisen und diese zu implementieren.
Kommen wir zum nächsten Punkt!

Mit der Idee fängt alles an!
So banal das klingt, ist es aber auch! Es ist ein bisschen sowas, wie "Programming by Wishful Thinking". Allerdings mit der totalen Entkopplung der Objekte zueinander.
Bezogen auf diese kleine Anwendung, dachte ich mir Folgendes:
"Wenn ich auf einen Button klicke, soll eine Ansicht hinzugefügt werden. In einer TextBox möchte ich Text eingeben können, der automatisch bei allen anderen Ansichten aktualisiert wird. Zudem möchte ich jede Ansicht individuell entfernen können."

So klickte ich mir zunächst die Oberfläche zusammen. Einen Button und eine TextBox. Die Ansichten ließ ich logischerweise weg, weil die ja dynamisch hinzugefügt werden sollten.
Ich klickte doppelt auf den Button, weil ich ja das Klick-Event haben wollte. Und was wollte ich mit dem Klick erreichen? Richtig, ich wollte eine Text-Ansicht hinzufügen. So programmierte ich es dann auch:

private void button1_Click(object sender, EventArgs e)
{
 Say.Carefully(AddTextView, this);
}

Der Aufruf mit dem "Say Carefully" ist nur ein einfacher Wrapper, um mir Arbeit abzunehmen und die Lesbarkeit zu erhöhen. So sieht diese Wrapper-Klasse aus:

using System;

static class Say
{ 
 public static void Carefully(EventHandler statement, object caller)
 {
  if (statement != null)
   statement(caller, EventArgs.Empty);
 }

 public static void Carefully(EventHandler statement, object caller, EventArgs arguments)
 {
  if (statement != null)
   statement(caller, arguments);
 }
}

Diese Hilfsklasse verwende ich in allen MPT-Projekten. Das aber nur am Rande.
Wie man in dem Beispiel gesehen hat, formuliere ich bei dem Button-Klick im Grunde nur das, was ich damit erreichen will. Um das "WIE" habe ich mir in dem Moment noch keine Gedanken gemacht, weil dies nur die Gedanken blockieren würde. Durch den Button-Klick soll eine Ansicht hinzugefügt werden. Punkt! Mehr ist an dieser Stelle nicht wichtig. Dann hatte ich die jeweilige Caller-Schnittstelle dazu erstellt. (Siehe oben!)

Die nächste Sache: So wie ich Text in die TextBox eingebe oder verändere, soll dieser an alle Empfänger gesendet werden.
Also nahm ich hierfür einfach das "TextChanged" Ereignis der TextBox her:

private void textBox1_TextChanged(object sender, EventArgs e)
{
 Say.Carefully(SendText, this);
}

Das sollte sich auch wieder selbst erklären, denke ich. Das macht eben einen weiteren großen Vorteil aus, dass man den Quelltext nicht extra kommentieren braucht. Der Quellcode ist selbstredend. So sollte das IMHO auch sein. Wer Kommentare braucht, hat IMHO eine unsaubere Codebase und sollte an der Verständlichkeit seines Quellcodes arbeiten.

So, und wer Text sendet, muss diesen auch bereitstellen! Dafür gibt es ja diese Funktion:

public string ReturnText()
{
 return textBox1.Text;
}

Hier wird eben einfach der Text in der TextBox zurückgegeben. Einfacher geht es nicht.

Eine letzte Sache fehlt noch: Wir wollten ja auch Text-Views empfangen. Kommt also nun zum Schluss diese Möglichkeit hinzu:

public void ReceiveTextView(IAmTextView textView)
{
 this.Controls.Add(textView.Control);
 Say.Carefully(SendText, this);
}

Auch diese Methode kam quasi so zustande, wie ich sie gern verwenden wollte. Wir erinnern uns: Wir wollten alles nur über Schnittstellen abwickeln!
So wollte ich als Parameter eine Schnittstelle, die sich selber als "IAmTextView" bezeichnet. Und diese dient quasi als Wrapper für das "Control", welches ich ja in der ersten Anweisung der "Controls"-Liste der Form-Klasse hinzufüge. Sonst wäre dies nicht Bestandteil der Form und somit nicht sichtbar.
Danach möchte ich, dass nochmal der Text gesendet wird. Denn die neu hinzugefügten Ansichten sind ja praktisch noch jungfräulich ... also leer.

Das ist auch ein wichtiger Punkt! Ich hätte das Senden des Textes auch in das Button-Klick Event einfügen können, z.B. so:

private void button1_Click(object sender, EventArgs e)
{
 Say.Carefully(AddTextView, this);
 Say.Carefully(SendText, this);
}

Die Software hätte genauso funktioniert, da dies quasi serielle Funktionsaufrufe sind. Was wäre aber, wenn das Event "AddTextView" gar nicht bedient wird? Dann wird "SendText" umsonst aufgerufen, weil ja keine Ansicht hinzugefügt wurde und alle bestehenden Ansichten bereits aktualisiert sind. Ein unnötiger Funktionsaufruf, also. Bei diesem Beispiel fällt das ja nicht ins Gewicht. In der Praxis könnten hier aber komplexe Operationen dahinter stehen, was dann sehr wohl stören könnte.

Oder ein anderes Beispiel:
Nach dem Aufruf von "AddTextView" wird die Verarbeitung an einen Thread übergeben, wodurch die Funktion sofort wieder zurück kommt und das Event "SendText" aufruft. Der andere Thread ist aber noch dabei, die "TextView" zu generieren. (Das kann ja in der Praxis auch sehr komplex sein.) Nun wurde "SendText" aber schon ausgeführt, obwohl noch keine neue "TextView" erzeugt wurde. Kurze Zeit später ist der Thread soweit und wir bekommen die "TextView". Aber sie wird keine Daten enthalten, weil der Zug (SendText) schon abgefahren ist. Dumm gelaufen. (Aber keine Angst! Der Kunde wird sich schon über den Bug beschweren. *g*)

Daher sollte man sich immer folgende Frage stellen:
"Sind die Ereignisse abhängig oder unabhängig voneinander?"
Am besten versucht man sich dabei immer das Thread-Beispiel vorzustellen. Also das Szenario, dass die Events quasi parallel ausgeführt werden.
Sind es parallele Prozesse, kann man diese auch parallel aufrufen bzw. implementieren.
Müssen die Events seriell, also nacheinander ausgeführt werden, sollten diese auch so aufgerufen werden.

Das soll es soweit erstmal gewesen sein. Im zweiten Teil geht es dann weiter. (Ich schätze mal, dass ich dann die "Mittelmänner" zeige.)

Sonntag, 18. April 2010

Hypothese: "Die Micro-Pattern-Trilogie reicht völlig!"

Ehe wir uns die praktische Anwendung der Micro-Pattern-Trilogie (kurz MPT) ansehen, möchte ich hier zunächst eine kühne Behauptung aufstellen:

"Ich behaupte, dass man nur unter Zuhilfenahme der MPT absolut jede denkbare Software realisieren kann, egal wie komplex diese ist!"

Ziemlich gewagt, diese Aussage, oder? Gerade, wenn man bedenkt, dass es schon andere helle Köpfe gab, die sich viele Entwurfsmuster ausgedacht haben, um den heutigen Softwareproblemen Herr zu werden.
Ich sage auch nicht, dass alle Muster sinnlos sind. Im Gegenteil!
Es gibt aber eben auch Muster, welche IMHO redundant sind. Zum Beispiel gibt es da einmal den "Model View Controller" und den "Model View Presenter". Beide erfüllen unter'm Strich die selbe Funktion: Das Eine soll vom Anderen getrennt sein und dennoch hängen alle drei Elemente zusammen. Beim MVC sieht man diesen Widerspruch sehr schön.
Warum machen sich die Entwickler also das Leben so schwer?
Vom MVC gibt es zig Modifikationen, sodass man nicht sagen kann: "Ach ja, MVC! Deine Software funktioniert also so und so!" Der andere guckt dich dann verdutzt an und sagt: "Ähm ... nee, meine MVC-Variante funktioniert so und so!" ... Ach so, verstehe ... oder auch nicht. Gut, dass wir drüber geredet haben. ;-P

Bei der MPT sieht es aber anders aus! Hier ist die Spezifikation total simpel und eindeutig. Wenn ich von einem Caller-Mediator-Listener Triplet (kurz CML) rede, dann funktioniert es auch genau SO und nicht anders. Hier wird es keine Abwandlungen geben! (Ein Wassermolekül besteht schließlich auch immer aus zwei Wasserstoffatomen und einem Sauerstoffatom. Und wenn man von Wasser redet, weiß jeder, was das ist.)

Wenn ich mir überlege, dass es zu diesen diversen Pattern ganze Bücher gibt, welche diese erklären ... OMG!
Und manche Pattern sind auch nicht gerade einfach zu verstehen. (Wie will man dann erst Quellcode verstehen, welcher mit so einem Pattern realisiert ist?)

Sicher, MPT ist auch ein Muster. Aber ein sehr Einfaches, was letztlich jeder Mensch nachvollziehen kann.
Die MPT besteht nur aus drei einfachen Triplets:

Die Vorteile überwiegen die Nachteile um ein Vielfaches:

Totale Entkopplung der einzelnen Objekte!
Kein Objekt weiß von der Existenz eines anderen Objekts. Die einzelnen Objekte dürfen auch gar nichts voneinander wissen, geschweige denn direkt miteinander kommunizieren. Dies würde unweigerlich zu einer chaotischen Programmstruktur führen. Theoretisch sollte jedes Objekt völlig ohne Konstruktor-Argumente auskommen. Was ein Objekt benötigt, kommuniziert es einfach "ins Blaue" hinein. Irgendjemand wird "ihm" schon geben, was "er" verlangt (siehe CPS). Woher "er" es letztlich hat, interessiert "ihn" nicht. Die Mittelmänner (Mediator, Frequency, Provider) zählen hier ausdrücklich nicht als Objekte im oben genannten Sinne. Sie sind einfach notwendig, um die Objekte zu verbinden und dadurch Funktionalität zu gewährleisten. Man referenziert sie auch nicht, sondern sie sind einfach da.

Objekte kommunizieren vorsichtig bzw. passiv!
Das heißt, Objekte können zwar sagen, was sie wollen. Ihrem Wunsch muss aber nicht nachgegangen werden. Dadurch kann man auch mal temporär einen Kommunikationskanal unterbrechen, wenn man z.B. logische Fehler im Programm aufspüren will. Im besten Fall funktioniert dann eine Funktion nicht mehr, löst aber keine Exception aus. (Zwei Wassermoleküle kann man auch trennen, ohne dass die Welt untergeht. Bei der Software sollte es IMHO auch so sein.)

Man kann agil und flexibel seine Software entwickeln!
Schon lange vorbei sind die Zeiten, wo man vorher erstmal großartig seine Software plant. (Und das möglichst noch mit UML.) Man entwirft höchstens im Kopf mal ein grobes Konzept, wie das Gesamtbild aussehen soll.
Software darf man IMHO nicht mit einem statischen Gebäude vergleichen. Software ändert sich ständig! Da kann ich wirklich ein Lied von singen. Besonders, wenn eine recht komplexe Änderung in recht kurzer Zeit umgesetzt sein soll. (Sehr lustig wird es, wenn dann plötzlich Probleme auftreten, mit denen man gar nicht gerechnet hat.)
Mit der MPT kann man quasi die Software so entwickeln, wie sie einem der Kunde von der Funktionalität her erklärt. Einen "Button-Klick" in der Story kann man z.B. sofort mit dem CML verknüpfen, da dieser Klick irgendwas auslösen soll. Wie es weiter geht? Das kann an dieser Stelle sogar offen bleiben. Es ist wie bei einem Puzzle, wo jenes CML Triplet einen kleinen Teil vom Ganzen ausmacht. Man platziert es erstmal irgendwo und nimmt sich das nächste Puzzle-Teil her. Dies kann z.B. ein völlig anderer Aspekt sein, der mit dem "Button-Klick" gar nix zu tun hat.
Diese Freiheit hat man nur deshalb, weil alle Triplets atomar und völlig unabhängig voneinander sind. Am Ende hat man aber ein "fertiges" Puzzle bzw. eine funktionale Software, welche das tut, was sie soll.

Durch Schnittstellen werden die Dinge miteinander verknüpft!
Das ist sogar ganz wichtig, bei diesem Konzept! Die Mittelmänner erwarten ausschließlich Schnittstellen als Konstruktor-Argument! Niemals eine konkrete Klasse! Dies wäre ein fataler Bruch bei der MPT und die Flexibilität wäre dahin.
Vielmehr sollte man den Vorteil darin sehen, dass eine Klasse beliebig viele Schnittstellen implementieren kann. (Zu diesem Thema wird es auch noch einen extra Beitrag geben, weil die Schnittstellen so wichtig sind.)

So kurze Datenwege, wie möglich!
Theoretisch dürften hier die transportierten Daten (siehe TFR und CPS) auf kürzestem Wege beim Ziel ankommen, weil das jeweilige Objekt direkt sagt, was es benötigt. (z.B. beim CPS)
Somit könnte das angeforderte "Ding" direkt übermittelt werden, ohne erst über zig Ecken wandern zu müssen. Wie gesagt, theoretisch! ;-)


So, das wären spontan die Vorteile. Das ganze hat leider auch einen offensichtlichen Nachteil:

SEHR AUFWÄNDIG!
Für jedes Triplet muss man zwei Schnittstellen anlegen und den Mittelmann vollständig implementieren.
Zudem werden da auch viele einzelne kleine Quellcodedateien anfallen.
Ja, das ödet wirklich sehr an und wäre zurecht ein Grund zu sagen: "OMG, da bleibe ich lieber bei quick & dirty!"
Da bleibt momentan nix anderes übrig, als in den sauren Apfel zu beißen oder es zu lassen.
(Ich habe da aber schon ein privates Projekt im Sinn, welches eine Art RAD-Tool werden soll, um mir diese Arbeit abzunehmen. Natürlich möchte ich dieses Tool konsequent mit der MPT ins Leben rufen. Das wäre dann auch gleich die beste Erprobung für die Praxistauglichkeit der MPT.)

Soviel dazu. Bis zum nächsten Artikel! :-)

"Client - Provider - Server" im Detail

Im letzten Beitrag habe ich erklärt, wie das Transmitter-Frequency-Receiver Triplet (kurz TFR) funktioniert.

Nun ist das letzte Triplet der "Micro-Pattern-Trilogie" dran: das Client-Provider-Server Triplet (kurz CPS).

Wie kam ich auf das CPS Triplet? Das ergab sich aus der Tatsache, dass es neben einer passiven Datenübertragung auch eine Aktive geben muss. Einerseits würde das Konzept, ohne die Möglichkeit der Datenübertragung auf Abruf, unvollständig erscheinen. Zum Anderen ist dies sogar unabdingbar, wenn man konsequent ausschließlich mit dieser "Micro-Pattern-Trilogie" seine Software entwickelt. Was ich damit meine, wird in den zukünftigen Artikeln ersichtlicher werden. Es sei nur kurz erwähnt, dass es damit zu tun hat, wie man derartige Software zu entwickeln beginnt. (Das werde ich aber in anderen Beiträgen ausführlich erklären und würde hier zu weit führen.)

Bei der Namensfindung orientierte ich mich auch wieder am realen Beispiel:
Ich (der Client) suche etwas bei Google (der Server). Dabei wandern meine Suchanfragen und meine persönlichen Suchergebnisse von Google über den Provider.

Damit sollte die Sache eigentlich schon klar sein.
Hier nun die Präsentation:



So, nun sind alle Triplets in ihrer Funktionsweise erklärt und wir tasten uns langsam aber sicher zu den praktischen Beispielen vor.

Samstag, 17. April 2010

"Transmitter - Frequency - Receiver" im Detail

Im letzten Beitrag habe ich erklärt, wie das Caller-Mediator-Listener Triplet (kurz CML) funktioniert.

Nun möchte ich erklären, wie das Transmitter-Frequency-Receiver Triplet (kurz TFR) funktioniert.
Wie bin ich überhaupt darauf gekommen? Der Gedankengang war im Grunde recht einfach. Da das CML Triplet keine Daten übertragen darf, musste etwas her, was dies ausdrücklich spezifiziert.
Ich dachte zuerst an eine Möglichkeit, Daten passiv empfangen zu können. Als Beispiel sei hier die Statusleiste genannt, welche bei fast allen GUI-Anwendungen existiert. Diese Statusleiste fordert keine Daten an, sondern empfängt diese automatisch, wenn sich der Status geändert hat. (Meist ist dies ja ein einfacher String.)

Oder ein anderes Beispiel:
Der Benutzer gibt z.B. in einer TextBox beliebigen Text ein, welcher gleichzeitig in mehreren Ansichten aktualisiert werden soll.

Mit diesem Beispiel kam mir letztlich die Idee:
Es gibt einen Sender, welcher Daten aussendet. Gleichzeitig kann es mehrere Empfänger geben, welche diese Daten empfangen.
So etwas kennt jeder aus dem realen Leben: Fernsehsatelliten senden ein Fernsehsignal in Richtung Erde und diverse Sat-Empfänger können dieses Signal empfangen.
Es war sofort klar, dass die Schnittstelle für den Empfang schlicht Receiver heißen musste.
Die Schnittstelle für den Sender wollte ich auch erst "Sender" nennen, jedoch gab es diese Bezeichnung schon im .NET Framework. Außerdem klang es nicht so "cool".
Einmal kurz gegoogelt und ich fand den Begriff Transmitter. Dieser passt auch besser zum Rundfunk-Beispiel.
Nun brauchte ich noch einen geeigneten Name für den Mittelmann.
Was verbindet beide Dinge miteinander? Wirklich spontan fiel mir sofort der Äther ein. Ich gebrauchte natürlich den englischen Begriff Aether. Tatsächlich blieb ich für einige kurze Experimente bei diesem Begriff, aber mich störte er immer mehr, weil man ihn heute im Grunde nur noch in der Esoterik antrifft.
Ich überlegte und überlegte, aber mir wollte keine Alternative einfallen. Dann nahm ich mir in Gedanken nochmal den Vergleich mit dem Rundfunk her. Was geschieht also, wenn man z.B. beim Sat-Receiver oder beim Radio den Sender wechselt? Klar, man ändert die Frequenz. Bingo! Das war der gesuchte Begriff! Und er passte auch genau zu dem Presenter-First Konzept. Auch hier entschied ich mich letztlich für die englische Bezeichnung: Frequency.

Nach der langen Einleitung kommt nun die versprochene Präsentation:


So, dieses war der zweite Streich und der Dritte folgt sogleich!

(Mal sehen, ob ich es bis morgen Abend schaffe. Dann hätten wir die graue Theorie hinter uns und es folgen dann endlich praktische Beispiele und Überlegungen für den praktischen Einsatz.)