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.)

Dienstag, 11. Mai 2010

Die Mittelmänner entwickeln sich!

Ich bin ja so glücklich über das, was ich heute erreicht habe! Aber schön der Reihe nach:

Die Mittelmänner entwickeln sich weiter!

Wie kann man das verstehen? Ganz einfach: Weißt Du noch, als ich in meinen Präsentationen (z.B. diese hier) gesagt habe, dass die Mittelmänner selber von Außen keine Zugriffsmöglichkeiten haben (sollen)?
Ich habe heute meine Meinung geändert! (Ich habe aber auch nie gesagt, dass die Micro-Pattern-Trilogie bereits vollendet ist.) ^_^

Ich bin bei Ralf Westphals Blog über diesen Beitrag gestolpert: Asynchrone Kommunikation mit EBCs statt “Async-Pattern”

Ralf hat da absolut Recht, wenn er sagt, dass die Dinge, wie z.B. Threads, nix in der Businesslogik zu suchen haben! Ich hatte aber auch am Anfang den Fehler gemacht, als ich mal diesbezüglich experimentiert hatte. Ich hatte aber auch keine Idee, wie ich es denn am besten anders anstellen könnte.

Und heute grübelte ich etwas über diesen Fakt nach. Die natürliche Trennung zwischen den Objekten stellen ja die Mittelmänner dar. Also ist es doch naheliegend, wenn man an diesem Connector festlegt, ob der Nachrichtenfluss asynchron oder synchron verlaufen soll und nicht in den Businessmodellen!

Guter Stil ist es ja, wenn man ein Framework so entwirft, wie man es später auch auf einfache Weise verwenden möchte. So kam ich auf folgende Idee:

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();

   new BerechneMediator(
    form1,
    berechneModel).Asynchronize();

   new ErgebnisFrequency(
    berechneModel,
    form1).Synchronize();

   Application.Run(form1);
  }
 }
}

Hier nochmal die relevante Stelle:

new BerechneMediator(
 form1,
 berechneModel).Asynchronize();

new ErgebnisFrequency(
 berechneModel,
 form1).Synchronize();

Wie man sieht, haben die Mittelmänner nun doch noch öffentliche Methoden bekommen.
Die "form1" ist der Auslöser für den "berechne"-Vorgang. Das "berechneModel" führt die Berechnung durch. Je nachdem, wie umfangreich die Berechnung ist, kann dies eine gewisse Zeit dauern. Deshalb wollen wir diesen Vorgang asynchron durchführen. Das hat zur Folge, dass der Nutzer in der Zwischenzeit ungehindert weiter mit der Anwendung arbeiten kann.

Wenn das "berechneModel" fertig ist, wird das Ergebnis einfach an die Form zurück gesendet. Aber Vorsicht! Es muss synchronisiert werden, weil die "form1" ein GUI ist. Tut man dies nicht, wird eine Exception geworfen!
Mit dem Methodenaufruf "Synchronize" ist das Thema aber schon gegessen.
So einfach kann die asynchrone/synchrone Nachrichtenverarbeitung sein. Und übersichtlich ist das ganze auch noch.
Ist das geil, oder ist das geil? ^_^

Und wie sieht die Sache intern aus?
Der Übersichtlichkeit halber, zeige ich hier nur mal den "BerechneMediator":

using System;
using System.Collections.Generic;
using System.Text;

namespace NewAsyncTest
{
 public interface IAmBerechneCaller
 {
  event EventHandler Berechne;
 }

 public interface IAmBerechneListener
 {
  void Berechne();
 }

 public class BerechneMediator : Middleman
 {
  private readonly IAmBerechneCaller caller;
  private readonly IAmBerechneListener listener;

  public BerechneMediator(
   IAmBerechneCaller caller,
   IAmBerechneListener listener)
  {
   this.caller = caller;
   this.listener = listener;

   this.caller.Berechne += Do(caller_Berechne);
  }

  void caller_Berechne(object sender, EventArgs e)
  {
   listener.Berechne();
  }
 }
}

Kein Witz! So sieht das wirklich aus. Mein Ziel war es ja, dass ich meine Pattern genauso weiterverwenden kann, wie ich es bisher getan habe.
Man sieht erst auf den zweiten Blick, was sich geändert hat. JEDER Mittelmann muss ab sofort von der abstrakten Klasse "Middleman" erben, wenn er die komfortablen synch/asynch Features nutzen will.
Wenn er diese geerbt hat, muss er beim Zuweisen des Event-Handlers die geschützte "Do"-Methode verwenden. Mehr ist hierbei absolut nicht zu tun.

Sehen wir uns nun die abstrakte Basisklasse "Middleman" an. Denn nur dort befindet sich die gesamte Infrastruktur.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;

namespace NewAsyncTest
{
 public abstract class Middleman
 {
  private bool isAsynchronizer = false;
  private bool isSynchronizer = false;
  private AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);

  public void Asynchronize()
  {
   isSynchronizer = false;
   isAsynchronizer = true;
  }

  public void Synchronize()
  {
   isSynchronizer = true;
   isAsynchronizer = false;
  }

  public void NormalMode()
  {
   isSynchronizer = false;
   isAsynchronizer = false;
  }

  protected EventHandler Do(EventHandler customMethod)
  {
   return new EventHandler(
    delegate
    {
     if (isAsynchronizer)
     {
      ThreadPool.QueueUserWorkItem(delegate { customMethod(this, EventArgs.Empty); });
     }
     else if (isSynchronizer)
     {
      asyncOp.Post(new SendOrPostCallback(
       delegate
       {
        customMethod(this, EventArgs.Empty);
       }), null);
     }
     else
     {
      customMethod(this, EventArgs.Empty);
     }
    });
  }
 }
}

Sieht kompliziert aus, ist es aber gar nicht. ;-)
Es sind die anonymen delegates, die das Ganze etwas wüst aussehen lassen. *lach*
Über die 3 öffentlichen Methoden werden lediglich Statusflags gesetzt. (Ja, es sind 3 und nicht 2 Methoden, weil ich noch eine Methode wollte, mit der ich die Flags wieder zurücksetzen kann.)

In der geschützten "Do" Methode wird lediglich ein neuer EventHandler zurückgegeben. Und in diesem Handler steckt eine Kontrollanweisung, die einfach prüft, welche Rolle der "Mittelmann" im Moment spielt. Ist er ein "Asynchronizer", wird die "customMethod" einfach in den ThreadPool gesteckt. Und nach mir die Sintflut. *g*
Ist er ein "Synchronizer", wird die "customMethod" in die "asyncOp" gesteckt. (Dieses Ding sorgt voll automatisch dafür, dass ein beliebiger Thread mit dem GUI-Thread synchronisiert wird.)
Wenn er letztlich nix von Beiden ist, dann soll die "customMethod" ganz normal aufgerufen werden.

Und weil diese Prüfung bei JEDEM ausgelösten Event durchgeführt wird, könnte man quasi zur Laufzeit der Anwendung das Verhalten ändern.

Also ich finde das richtig geil! Da hier in der grundlegenden Struktur absolut nicht viel geändert werden muss, habe ich das heute auch in sehr kurzer Zeit in meinem Softwareprojekt auf Arbeit aktualisiert. Da habe ich nämlich auch einen Task, der am besten asynchron ablaufen müsste. Das ist jetzt ein Kinderspiel! :-D
Es ist so geil! ^_^

Das soll es auch schon gewesen sein.
Ich habe hier auch ein Visual Studio 2008 Projekt, wo Du das mal ausprobieren kannst: KLICK [rar-Archiv]

1. MPT Beispiel - Teil 2

Nun kommt, wie vesprochen, der 2. Teil meines ersten MPT Beispiels.
Und ich bin direkt froh darüber, dass ich gerade in diesem Teil auf die "Mittelmänner" eingehe! Denn erst heute habe ich einen effektiven Weg gefunden, um ganz einfach die asynchrone und synchrone Nachrichtenverarbeitung zu verwenden. Dazu aber später mehr. (Das wird dann ein extra Beitrag werden.)

Beim letzten Mal hatte ich ja gezeigt, wie einfach das Hauptfenster intern aufgebaut ist.

Heute möchte ich zeigen, was die Komponenten zusammenhält:

Die Mittelmänner

Hier kommt erstmal wieder das Listing des Quellcodes, den ich nachher ausführlicher erklären möchte:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

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

   Form1 form1 = new Form1();
   AddTextViewModel addTextViewModel =
    new AddTextViewModel(
     new TextViewManager(
      form1));
   
   new AddTextViewMediator(
    form1,
    addTextViewModel);

   new TextViewFrequency(
    addTextViewModel,
    form1);

   Application.Run(form1);
  }
 }
}

Ja, das ist auch schon alles.
Los geht's!

Form1 form1 = new Form1();
AddTextViewModel addTextViewModel =
 new AddTextViewModel(
  new TextViewManager(
   form1));

Ich muss es gleich jetzt sagen: Was du in diesem Ausschnitt siehst, ist Mist! Es funktioniert zwar, aber es ist strukturell einfach nicht zumutbar, wenn du mit den Micro-Pattern entwickelst! Dass ich es so programmiert habe, lag eigentlich nur an zwei Faktoren:
1. wollte ich die Idee nur mal schnell testen, um zu sehen, ob das so funktioniert, wie ich es mir gedacht habe.
2. hing ich gedanklich doch noch sehr an dem originalen "Presenter Fist" Prinzip. Denn dort ist es gestattet, dass konkrete Objekte eigene Abhängigkeiten haben.

Ich will dennoch erklären, was der Code zu bedeuten hat.
In Zeile 18 wird einfach nur das Hauptfenster erzeugt, ist aber noch nicht sichtbar.
In der nächsten Zeile wird das "AddTextViewModel" erzeugt. Dieses erwartet als Konstruktor-Argument ein Objekt, welches sich als "IAmTextViewFactory" bezeichnet. Der "TextViewManager" fungiert als diese "IAmTextViewFactory".
Der "TextViewManager" erwartet ein Objekt, welches sich als "IAmTextTransmitter" bezeichnet. In diesem Falle ist ja das Hauptfenster eben dieser "TextTransmitter".
Die Internas dieses Konstrukts zeige ich im nächsten Teil.

Wie man sieht, ist sogar diese kleine Hierarchie nicht mehr leicht nachvollziehbar. Zudem sind die Objekte wieder miteinander gekoppelt und somit voneinander abhängig. Sowas geht echt gar nicht! Kein Objekt sollte von einem anderen Objekt abhängig sein ... ausgenommen, die "Mittelmänner". Aber die haben selber keine Businesslogik, sondern sind nur für die Infrastruktur da.

Der nächste Abschnitt:

new AddTextViewMediator(
 form1,
 addTextViewModel);

new TextViewFrequency(
 addTextViewModel,
 form1);

So sollte die gesamte Software zusammengesetzt sein! Das ist die saubere Anwendung vom MPT Konzept.
Die Mittelmänner können einfach in den "Raum" hinein erstellt werden.

Sehen wir uns einfach mal an, wie die "Mittelmänner" im Inneren aussehen:

Der Mediator:

using System;
using System.Collections.Generic;
using System.Text;

namespace PFDynamic
{
 public class AddTextViewMediator
 {
  private readonly IAmAddTextViewCaller caller;
  private readonly IAmAddTextViewListener listener;

  public AddTextViewMediator(
   IAmAddTextViewCaller caller,
   IAmAddTextViewListener listener)
  {
   this.caller = caller;
   this.listener = listener;

   this.caller.AddTextView += new EventHandler(caller_AddTextView);
  }

  void caller_AddTextView(object sender, EventArgs e)
  {
   listener.DoAddTextView();
  }
 }
}

Die Frequency:

using System;
using System.Collections.Generic;
using System.Text;

namespace PFDynamic
{
 public class TextViewFrequency
 {
  private readonly IAmTextViewTransmitter transmitter;
  private readonly IAmTextViewReceiver receiver;

  public TextViewFrequency(
   IAmTextViewTransmitter transmitter,
   IAmTextViewReceiver receiver)
  {
   this.transmitter = transmitter;
   this.receiver = receiver;

   this.transmitter.SendTextView += new EventHandler(transmitter_SendTextView);
  }

  void transmitter_SendTextView(object sender, EventArgs e)
  {
   receiver.ReceiveTextView(
    transmitter.ReturnTextView());
  }
 }
}

Soviel gibt es da im Grunde nicht zu sagen, denke ich. Wer C# kann, sollte den Quellcode hier problemlos verstehen. Es ist im Grunde nur Wrapper-Code. Und diesen Code muss man bei jedem Triplet erstellen. Und das langweilt, weshalb hier unbedingt ein RAD-Tool her muss! ^_^

Das soll es für diesen Beitrag gewesen sein.

Jetzt schreibe ich gleich noch den nächsten Beitrag mit den Neuigkeiten, die sich in letzter Zeit bei dem MPT Konzept ergeben haben.

Bis dann! :-)

Montag, 10. Mai 2010

Ich mache es doch anders ...

Vor ein paar Tagen hatte ich ja geschrieben, dass die Fortsetzung des MPT-Beispiels etwas warten muss, weil ich das RAD-Tool aktiv entwickeln möchte.
Ich werde es aber doch so machen, dass ich erst noch das MPT-Beispiel komplett erkläre. Das ist so besser, weil dann erstmal eine Basis dafür existiert, wie die Sache in der Praxis aussieht.

Umso spannender (ich hoffe es jedenfalls *g*) wird es dann nach dem Beispiel werden!
Ich habe nämlich vor, den Fortschritt des RAD-Tools per Video zu protokollieren. Es soll aber keine Schritt-für-Schritt Anleitung werden, sondern ich möchte nur kurz die Features vorstellen und erklären, wie diese funktionieren. Das werde ich sicher auch mal mit Diagrammen veranschaulichen.
In jedem Beitrag werde ich dann auch noch den Versions-Hash von dem entsprechenden Git-Commit hinterlassen, damit man später irgendwann mal leicht nachvollziehen kann, zu welcher Version das Video gehört.
Ich denke, das ist mal was anderes.
Ich, persönlich, liebe es ja, zu verfolgen, wie eine Anwendung wächst und an Features gewinnt. (Das war z.B. auch ein Grund, weshalb ich schon möglichst zeitig zu Google Chrome gegriffen habe. Da waren ja, verglichen mit Firefox, noch kaum Features drin. Oder die Entwicklung von Blender verfolge ich z.B. auch schon, seit es noch zu "Not a Number" gehörte. Ja, einer Anwendung beim Wachsen zuzusehen ist echt aufregend!)

Momentan ist "Atomix" ja noch in einem sehr frühen (und nicht wirklich brauchbaren) Stadium. Aber es besitzt dennoch schon genug, um diesen ersten Iterationsschritt per Video vorzustellen. Ich kann z.B. Dateien laden und diese in einer Listbox anzeigen, welche sich in einem eigenen Toolfenster befindet. Über einen Menüpunkt kann ich dieses Fenster beliebig öffnen und schließen.
Das zeige und erkläre ich aber alles in dem Video genauer. Es ist aber alles unter Zuhilfenahme der MPT entstanden.
"Atomix" wird auch eher evolutionär wachsen. In der ersten Iteration werden die Features noch statisch bzw. spezifisch sein, um zunächst festzustellen, ob es so funktioniert. In der nächsten Iteration werden die Features dynamischen Charakter haben bzw. abstrakter sein. So ist es z.B. auch momentan bei der Dateiansicht. Sie befindet sich momentan im ersten Iterationsschritt. (Genaueres dazu später in dem Video.)


Dann muss ich unbedingt noch etwas berichten!

Heute wurde ich von Ralf Westphal per eMail kontaktiert und erhielt ein interessiertes Feedback. Er verwies mich auch auf seine Idee der sogenannten "Event-based Components". Unsere Ideen sind im Grunde identisch! Das heißt, wir beide streben ein Konzept an, womit man auf einfache Weise durch völlige Entkopplung der Aspekte seine Anwendung agil und flexibel entwickeln kann.
Man sollte sich seine Ideen dazu wirklich mal ansehen! Gerade seine Grafiken sind sehr anschaulich.
Was auf den ersten Blick auffällt, ist, dass er regen Gebrauch der neuen C# Features macht.
Ich halte mich dabei noch an die Version 2.0, weil ich auf Arbeit auch noch mit Visual Studio 2005 Professional auskommen muss. Und mein Konzept möchte ich in erster Linie für die Arbeit nutzen. Daher ist das etwas problematisch, mich zu sehr auf die neuen Features einzulassen. Man ist dann schnell davon verwöhnt.
(So brachte mich z.B. die erste richtige Konfrontation mit C# direkt von C++ ab. C/C++ fasse ich nicht mehr freiwillig an. Nur, wenn es wirklich sein muss.)

Zudem bin ich überrascht, dass solche Ideen zur Abwechslung mal aus dem deutschsprachigen Raum kommen. Damit hätte ich absolut nicht gerechnet!

Wir wollen auf alle Fälle weiterhin Kontakt halten und Ideen austauschen.

Aber ich bin wirklich froh, dass es noch mehr Leute gibt, die sich über die Zukunft der Softwareentwicklung Gedanken machen! :-)

Samstag, 8. Mai 2010

Das Rad neu erfinden? Nein!

Heute Früh, zwischen 2 und 3 Uhr bin ich plötzlich aufgewacht und konnte nicht mehr einschlafen. Ohne Witz!
Mir gingen die ganze Zeit Ideen für weitere Features meiner begonnenen "Atomix" Anwendung durch den Kopf. Sind die Ideen mit dem MPT Konzept umsetzbar? Die Gedankenexperimente gingen los und ich kam immer zu dem selben Ergebnis: Natürlich ist das umsetzbar! Unter Zuhilfenahme der MPT ist das sogar sehr einfach, da man sich ja keine Gedanken über die Objektabhängigkeiten machen muss. Man geht ja bei der Implementation einfach davon aus, dass man die benötigten Daten einfach von irgendwoher bekommt. Und dann nutzt man einfach diese Daten für sein gewünschtes Feature.
Früher habe ich mir eher den Kopf darüber zerbrochen, wie ich die Objekte am Besten miteinander verkopple, um zu dem gewünschten Feature zu kommen:
Injiziere ich Objekt "A" in Objekt "B", oder doch besser Objekt "B" in Objekt "A"? Und was ist, wenn ich in Objekt "A" nur einen Teil der Daten von Objekt "B" benötige? Vielleicht benötige ich nur einen bestimmten String von Objekt "B"? Weshalb also Objekt "A" vom kompletten Objekt "B" abhängig machen? Ich kann es aber auch nicht anders machen, weil ich z.B. den String irgendwann dynamisch zur Laufzeit benötige. Also muss ich es doch so machen.
Und wenn ich in Objekt "B" noch mit Objekt "A" hantieren will? [usw. usf.]
Diese Überlegungen entfallen bei der MPT völlig, da hier im Grunde alle Objekte auf der selben Ebene stehen. Und kein Objekt interessiert sich dafür, wo es seine Daten herbekommt oder wo seine eigenen Daten hingehen. Man benötigt eine bestimmte Datenstruktur für Irgendwas Bestimmtes? Passende Schnittstelle dafür implementieren und seine Idee fortsetzen.

Dadurch sind massenhaft Features durch meinen Kopf gegangen. Das hat kaum ein Ende genommen. Echt schlimm, wenn man dabei durch nix gebremst wird ... höchstens seine eigene Fantasie. Und ehe ich mich versah, ging schon wieder die Sonne auf. *g*

Aber dennoch kam ich an einen Punkt, der mich heute den ganzen Tag stark beschäftigt hat:
"Atomix" wird auch Quellcode parsen müssen! Soll ich da anfangen, einen eigenen Parser zu entwickeln oder eine existierende Lösung suchen?

Ich dachte tatsächlich erst daran, einen eigenen Parser zu entwickeln. Schnell mal nach der offiziellen C# Spezifikation gegoogelt und den Standard ECMA-334 gefunden. "Rein geguckt und zam gezuckt!", wie es so schön heißt. ^_^
Dass C# komplex ist, ist klar. Aber dieses Dokument hat es schon in sich. Eben eine richtige Spezifikation.

Ich hielt dennoch an dem Gedanke fest, einen eigenen Parser zu entwickeln. Aber mir fehlte ein bisschen der konkrete Ansatz, wie man sowas programmiert. Also nochmal gegoogelt und ich fand das: "Writing a parser: overview".

Nun war die dort erfundene Beispielsprache ja recht einfach. Einfacher, als etwa C#. Und dennoch ist der Parser für jene einfache Sprache so komplex. Wie würde der erst bei C# aussehen? So einen Parser hat man jedenfalls nicht mal so eben nebenbei programmiert.

Nun will ich ja im Grunde nur ein RAD-Tool entwickeln, welches mir ein bisschen Arbeit abnehmen soll. Und der dafür benötigte Parser ist da eher ein notwendiges Übel. Der soll bei der Entwicklung nicht mehr Aufmerksamkeit bekommen, als meine eigentliche Anwendung.
So kommen wir wieder auf die Überschrift dieses Beitrages zurück. Ist es notwendig, das Rad neu zu erfinden? Also in dem Falle, sicher nicht! Ich kenne mindestens ein Projekt, welches einen Parser verwendet, wie ich ihn benötige: "SharpDevelop".
Ich lud mir den Sourcecode von SharpDevelop runter und stöberte darin nach etwas, was "Parser" hieß. Ich hatte auch etwas gefunden, aber es war nicht direkt das, was ich erwartet hatte.
Also googelte ich nochmal, weil ich wissen wollte, wie man den Parser von SharpDevelop für eigene Projekte nutzen kann. Tatsächlich fand ich auch etwas: "NRefactory".
Das sieht sehr vielversprechend aus und scheint einfach verwendbar zu sein. (Und wenn man einmal weiß, wonach man suchen muss, findet man es auch gleich im Quellcode von SharpDevelop wieder. *g*)
Ich fand auch noch eine Auflistung von verschiedenen Parsern bei Google: "Parser for C#".

Hmmm, dort fiel mir auch die "Common Compiler Infrastructure: Metadata API" ins Auge. Das werde ich mir vielleicht auch mal näher ansehen.
Aber theoretisch dürfte für meine Zwecke die normale "System.Reflection" ausreichen. Mal gucken.

Tja, nun hätte sich im Grunde die Sache mit dem Parser erledigt und ich kann mich wieder voll und ganz auf meine eigentliche Anwendung konzentrieren. :-)

Mittwoch, 5. Mai 2010

Was gibt es Neues?

Zunächst mal: Keine Angst! Das MPT-Beispiel ist nicht in Vergessenheit geraten. Es muss jedoch erstmal etwas warten. (Warum, erkläre ich weiter unten.)

Auf Arbeit habe ich momentan die Zeit, meine Softwareprojekte aufzuräumen. (Das heißt, ich nehme mir die Zeit einfach, wenn sonst nix zu tun ist. *g*) Und da dachte ich mir so, dass ich mein letztes (noch aktuelles) Projekt restrukturiere. Gerade, weil ich weiß, dass da später noch mehr Features implementiert werden sollen. Und bei der alten Struktur wäre das "Frickeln" vorprogrammiert. ;-)

Also entschied ich mich, die eher frickelhafte (aber funktionale) Software in eine ordentliche MPT umzuformen. Ganz einfach ist das nicht, da in der alten Struktur viele verschiedene Aspekte in einer einzigen Klasse zusammengefasst sind. Das Meiste von der Business-Logik sammelte sich leider in der GUI-Klasse an. (Wenn es schnell gehen soll und man noch den Überblick behält, kann man schon mal diesen Schritt wählen. Denn Merke: Der Kunde interessiert sich am Ende nur für das Tool und nicht über dessen interne Struktur. There is no perfect Design!)
Aber man tut sich selber einen großen Gefallen, wenn man die Aspekte seiner Software voneinander trennt!
Tja, was soll ich sagen? Ich freue mich immer wieder, dass das MPT Konzept tatsächlich so funktioniert, wie ich es mir gedacht habe. Nebenbei entdecke ich Möglichkeiten, wie ich es noch optimieren kann, um mir etwas Arbeit abzunehmen und den Überblick zu behalten.

Dabei kommen wir gleich zum nächsten Punkt: Es muss leichter fallen, Software mit dem MPT Konzept zu erstellen!

Dafür muss unbedingt ein RAD-Tool her! Da es sowas für die MPT noch nicht gibt, ist mein Einsatz gefragt. *g*
Vergangenes Wochenende hatte ich schon mit der Entwicklung begonnen. Der Projektname lautet "Atomix". (Der Name hat sich so ergeben, weil man mit dem Tool seine Software so erstellt, wie wenn man einzelne Atome miteinander verbindet.) Ich möchte mal kurz erklären, wie ich mir dieses Tool vorstelle:
Es soll von Anfang an mit dem MPT Konzept entwickelt werden. Ich möchte es so zeitig wie möglich produktiv einsetzen können. Das heißt also, die Features haben Vorrang und das Design kommt später. Es soll schließlich in erster Linie ein Werkzeug sein und seinen Zweck erfüllen. (Das Design sollte sich aber später leicht aufpeppen lassen. Der völligen Entkopplung sei Dank! *g*)

Dieses Tool soll vorerst nicht die gewohnte Entwicklungsumgebung ersetzen, sondern diese ergänzen. (Aber nicht in Form eines Plug-Ins!)
Im Vordergrund steht die MPT-Struktur der zu entwickelnden Software. Aus diesem Grund wird diese in einer grafischen Form dargestellt werden. Wie man sich das ungefähr vorstellen kann, soll folgende Grafik zeigen:

So könnte die grafische Repräsentation mal aussehen.
(zum Vergrößern auf's Bild klicken)

Dieses Diagramm hatte ich mit yEd entworfen und zeigt den funktionalen Beginn meines ersten "Atomix"-Versuches. (Dies verfolge ich aber nicht weiter, weil ich mich hierbei noch zu sehr an der Idee des "Presenter First" orientiert hatte. Das heißt, hier haben die konkreten Objekte selber feste Abhängigkeiten. Man achte auf die zusammenhängenden weißen Objekte mit der dicken Linie. Das darf nicht sein, da es ein Stilbruch bei der MPT ist.)

Was man aber gerade an diesem Diagramm sehr gut sehen kann, ist die organische Form, welche die Software letztlich hat. Sie ist nicht starr und steif, sondern anpassbar und flexibel.

Bei meinem Tool steht also die Grafik im Mittelpunkt. Wenn man dann z.B. auf eines der Elemente klickt, soll ein Fenster aufpoppen, wo der zugehörige Quellcode drin steht. Dazu inspiriert hat mich der neue Ansatz der sogenannten "Code Bubbles".
Man soll dann nur den Quellcode sehen, der in dem Moment wirklich relevant ist.
Hinzufügen von neuen Triplets soll mit wenigen Mausklicks möglich sein. Dabei soll sich stehts der Quellcode automatisch mit anpassen. Die Grafik soll sich auch aktualisieren, wenn ich z.B. in Visual Studio den Quelltext ändere.
Die einzelnen Elemente sollen durch einfaches Drag & Drop austauschbar sein, sodass man seine Software im Grunde durch wenige Klicks verändern kann.

Das sollen erstmal die groben Ideen sein, die mir spontan eingefallen sind.
Aber alles schön der Reihe nach. *g*

Naja, und dafür muss erstmal die Fortsetzung von meinem MPT Beispiel etwas zurückstehen.