Dieses Blog durchsuchen

Montag, 31. Mai 2010

Mittelmänner bleiben, wie sie sind!

In meinem letzten Beitrag war ich ja völlig begeistert von der Idee, die Infrastruktur der Asynchronisation und Synchronisation in die Mittelmänner zu verschieben.

Davor will ich aber an dieser Stelle ausdrücklich abraten!

Ralf Westphal hatte mich davor gewarnt, die "Vermittler" mit mehr Aufgaben zu beladen, als sie eigentlich tun sollten. Und da hat er absolut Recht!

Warum habe ich aber mit meiner Grundidee gebrochen und die Vermittler mit der Synch/Asynch-Angelegenheit belastet?
Ganz einfach: Ich hatte einen Denkfehler. *g*

Ich hatte einfach zu kompliziert gedacht. Das möchte ich mal genauer erklären:
Mein Gedanke ging in die Richtung, wie es auf dem folgenden Bild dargestellt ist.

Für den Asynch/Synch Aspekt werden eigene Mittelmänner und Schnittstellen definiert. (Zum Vergrößern auf Bild klicken.)

Meine Idee war es, für die Asynchronisation und Synchronisation auch entsprechend eigene Mittelmänner und Schnittstellen zu definieren. Das erschien mir aber viel zu unflexibel und zu aufwändig, weshalb ich mich für den unschönen Hack entschied, die Mittelmänner zu modifizieren. Zudem sah ich hierin schon fast das Todesurteil meiner MPT. Denn wenn ich mit der MPT nicht ebenso einfach solche flexiblen Konstrukte realisieren kann, wie es Ralf mit seinen EBCs dort gezeigt hat, so wäre die MPT schlicht unbrauchbar.

Und ich war wirklich schon nahe dran, die MPT zu vergessen und mich den EBCs zuzuwenden.

Nun, ich weiß nicht, warum ich erst letzten Freitag beim Autofahren auf die entscheidende Idee gekommen bin, welche die MPT ebenso flexibel macht, wie die EBCs. Dabei ist es im Nachhinein betrachtet schon fast peinlich, dass ich so umständlich gedacht habe.

Die Lösung zum Problem zeigt die folgende Grafik:

Hier werden einfach die bestehenden Mittelmänner und Schnittstellen verwendet. (Zum Vergrößern auf Bild klicken.)

Anstatt für die Asynch/Synch Aspekte eigene Mittelmänner und Schnittstellen zu implementieren, kann ich doch auch einfach die bestehenden Konstrukte verwenden. Denn die Asynch/Synch Modelle benötigen doch gar keine andere Signatur. Das heißt, es sind ja keine Adapter bzw. Translator, welche z.B. einen Text aufnehmen und eine Zahl zurückgeben. Oder die ein Trigger-Event aufnehmen und verschiedene konkrete Events auslösen.
Das war von Anfang an mein Denkfehler dabei.
Die Asynch/Synch Modelle sind doch in dem Falle einfach nur "Repeater". Sie können doch gleichzeitig der "Caller und Listener" oder "Transmitter und Receiver" sein. Das verbietet niemand.
Und das beste hierbei ist eben wieder der Vorteil der konkreten Trennung der Objekte. Die "MainForm" und das "BerechneModel" wissen nix von einer asynchronen oder synchronen Verarbeitung. Für sie ist alles so, wie immer. Und das ist ja auch der Sinn und Zweck bei der MPT (und auch bei Ralfs EBCs)! Man soll die Infrastruktur der Anwendung verändern können, OHNE die bestehende Business-Logik auch nur minimal zu verändern.
Was mir bei dem zweiten Bild besonders gefällt, ist die kettenartige Struktur, die man dadurch erhält. Man könnte hier sogar beliebig viele Zwischenglieder einfügen, denn der Input und Output ist für die Endknoten immer der Selbe!
Die Asynchronisaton und Synchronisation spielt sich nur in den jeweiligen Modellen und nirgendwo anders ab!
Und DAS ist wirklich genial! ;-)
(Heute hatte ich das auf Arbeit auch gleich in meinem Projekt umgeändert. Und es funktionierte anstandslos.)

Wie sieht nun der Quellcode dazu aus? Ich freue mich, sagen zu können, dass es fast so aussieht, wie bei Ralfs Asynch/Synch-Beispiel! :-)

So wird das zusammengesteckt:
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace NewAsyncTest
{
 static class Program
 {
  /// <summary>
  /// Der Haupteinstiegspunkt für die Anwendung.
  /// </summary>
  [STAThread]
  static void Main()
  {
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);

   Form1 form1 = new Form1();
   BerechneModel berechneModel = new BerechneModel();
   Asynchronizer asynchronizer = new Asynchronizer();
   Synchronizer synchronizer = new Synchronizer();

   new BerechneMediator(
    form1,
    asynchronizer);

   new BerechneMediator(
    asynchronizer,
    berechneModel);

   new ErgebnisFrequency(
    berechneModel,
    synchronizer);

   new ErgebnisFrequency(
    synchronizer,
    form1);

   Application.Run(form1);
  }
 }
}

So sieht der Asynchronizer aus:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace NewAsyncTest
{
 public class Asynchronizer :
  IAmBerechneListener,
  IAmBerechneCaller
 {
  public void DoBerechne()
  {
   ThreadPool.QueueUserWorkItem(
    delegate
    {
     Say.Carefully(Berechne, this);
    });
  }

  public event EventHandler Berechne;
 }
}

So sieht der Synchronizer aus:
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Threading;

namespace NewAsyncTest
{
 public class Synchronizer :
  IAmErgebnisReceiver,
  IAmErgebnisTransmitter
 {
  private AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);

  int ergebnis;
  public void ReceiveErgebnis(int ergebnis)
  {
   asyncOp.Post(new SendOrPostCallback(
    delegate
    {
     this.ergebnis = ergebnis;
     Say.Carefully(SendErgebnis, this);
    }), null);
  }

  public event EventHandler SendErgebnis;

  public int ReturnErgebnis()
  {
   return ergebnis;
  }
 }
}

Ein entsprechendes RAR-Archiv, mit dem Beispielprogramm, kann man sich auch wieder herunterladen: KLICK

Tja, keine Ahnung, aber beim Autofahren kamen mir bisher immer die besten Einfälle. *g*

Ich werde wohl im nächsten Beitrag mal die MPT mit den EBCs vergleichen. (Wobei ich die EBCs nicht als "Konkurrenz" betrachte. Das erkläre ich aber, wenn es soweit ist.)

Keine Kommentare:

Kommentar veröffentlichen