Think First Development

Erst denken, dann programmieren

Durchsuche Beiträge in Architektur

Disclaimer: Diejenigen, die sich gerne unvorbelastet an die FizzBizz Coding Kata heranwagen möchten, sollten hier besser nicht weiterlesen. Der Artikel wird verschiedene Lösung zeigen ;)

Einleitung

Unser firmeninternes Coding Dojo hatte eine relativ einfache Kata: FizzBuzz. Zwar ist die Aufgabe einfach und überschaubar, aber dafür sind die Erweiterungen vielfältig. Bei dieser Kata konnte man das Prinzip von Unit Test und Test Driven Development verdeutlichen. Erweitert man die Implementierung, kann die Implementierung der alten Anforderungen direkt getestet werden. Man merkt also, ob man über die neue Anforderung die alten Anforderungen vergessen hat. Als Organisator habe ich mir in Vorfeld Gedanken über die mögliche Implementierung gemacht, da ich einer der beiden Programmierer war. Bei der Kata musst ich dafür Sorgen, dass die Schnittstellen/Vorgaben so definiert sind dass der Test keine weiteren Vorgaben für die Implementierung macht als die Schnittstelle. Folgendes Klassengerüst war also vorgegeben:


public class FizzBuzzer
{
    public IEnumerable<string> FizzBuzz(int maxZahl)
    {
        throw new NotImplementedException();
    }
}

Das Pair-Programming-System ist auch in ein verändertes Ping-Pong geändert worden: Ich habe einen Test geschrieben, mein Co-Programmierer hat den Test implementiert und ich habe den nächsten Test geschrieben.

Dojo Lösung

Die Lösung im Dojo war die einfachste aller Möglichkeiten…eine Implementierung der Methode und das wars. Selbst als die Erweiterungen kamen wurde der FizzBuzzer höchstens um zwei Konstruktor-Parameter erweitert. Und damit die Tests laufen gabe es einen Default-Konstruktor mit dem Standard “3″ und “5″.


public class FizzBuzzer
{
    #region Felder
    private int _n;
    private int _m;
    #endregion

    #region Konsturktor: (), (n, m)
    public FizzBuzzer() : this(3,5)
    {
    }

    public FizzBuzzer(int n, int m)
    {
        _n = n;
        _m = m;
    }
    #endregion

    public IEnumerable<string> FizzBuzz(int maxNumber)
    {
        //Plausibilitäts prüfung
        if (maxNumber < 1)throw new ArgumentException("Nur Zahlen größer 0 sind erlaubt");
        if (maxNumber > 100000) throw new ArgumentException("Nur Zahlen kleiner oder gleich 100000 sind erlaubt");

        //Liste anlegen
        List<string> words = new List<string>();

        //Alle Zahlen durchlaufen und prüfen
        for (int curNumber = 1; curNumber <= maxNumber; curNumber++)
        {
            //Zahl durch n und m teilbar?
            if ((curNumber % _n) == 0 && (curNumber % _m) == 0)
                words.Add("FizzBuzz");
            else if ((curNumber % _m) == 0)
                words.Add("Buzz");
            else if ((curNumber % _n) == 0)
                words.Add("Fizz");
            else
                words.Add(curNumber.ToString());
        }

        //Ergebniss zurückliefern
        return words;
    }
}

Das ist natürlich nur eine von vielen Varianten einen FizzBuzz zu implementieren. Der Ansatz bei dieser Implementierung ist eine Klasse mit einer Funktion, die alle Varianten enthält. Versucht man eine Erweiterung mit beliebig vielen Fizz-Buzz-Zahlen und Wörtern, würde man die Zahl/Wort Kombination wahrscheinlich in einem Dicitonary ablegen und den zusammengesetzten String (FizzBuzz) für jeden Key des Dictionarys erweitern. Diese Lösung funktioniert nur, weil der Alorithmus zum Ermitteln des FizzBuzz gleich ist und nur durch eine Zahl parametrisiert ist. Ändert sich der Algorithmus oder ist der Algorithmus zum Zeitpunkt der Implementierung unbekannt, ist diese Lösung ungünstig oder funktioniert gar nicht mehr.

Delegaten als Parametrierung

Die Lösung, über die Werte zu Parametrieren reicht also nicht aus, da auch der Algorithmus und die Anzahl der Funkionen (Delegaten)variabel sein kann. Damit muss es eine Lösung geben, mit der ich Funktionen übergeben kann. Und von diesen Möglichkeiten gibt es in C# viele.

Schnittstellen

Der Klassiker ist eine Schnittstelle IFizzBuzz oder eine abstrakte Basisklasse mit unterschiedlichen Implementierungen für die verschiedenen Algorithmen. Mehr oder weniger parametrisierte Implementierungen dieser Schnittstelle werden an eine “Add” Methode des FizzBuzzers übergeben.


public class InterfaceFizzBuzzer
{
    private List<IFizzBuzz> buzzList = null;

    public InterfaceFizzBuzzer()
    {
        buzzList = new List<IFizzBuzz>();
    }

    public void Add(IFizzBuzz aBuzz)
    {
        buzzList.Add(aBuzz);
    }

    public IEnumerable<string> FizzBuzz(int maxNumber)
    {
        IList<string> result = new List<string>();

        for (int i = 1; i <= maxNumber; i++)
        {
            // Result ermitteln
            var numberResult = string.Empty;
            foreach (var item in buzzList)
                currentRes += item.GetForNumber(i);

            // Ergebnis hinzufügen
            if (string.IsNullOrEmpty(numberResult))
                result.Add(i.ToString());
            else
                result.Add(numberResult);
        }

        return result;
    }

Die Schnittstelle mit einer entsprechenden Implementierung sieht dann wie folgt aus:


public interface IFizzBuzz
{
    string GetForNumber(int number);
}
class SimpleFizzBuzzImpl : IFizzBuzz
{
    private int _baseNumber;
    private string _word;

    public SimpleFizzBuzzImpl(int baseNumber, string word)
    {
        _baseNumber = baseNumber;
        _word = word;
    }

    public string GetForNumber(int number)
    {
        if (number % _baseNumber == 0) return _word;
        return string.Empty;
    }
}

Lamba Expressions

Lamba Expressions finde ich für kleinen Aufgaben genial. Man kann Logik an eine Funktion übergeben, ohne dafür entsprechende Schnittstellen, Delegaten oder abstrakte Basisklassen zu definieren. Der besondere Charme liegt dabei in der Übergabe in Logik, die nur einmal benutzt wird, wie bei “where” in LINQ. Die Lamba-Version des FizzBuzzer hat auch eine “Add” Methode, nimmt aber einen Parameter vom Typ Func<int, string> statt des Interfaces an.


public class LambdaFizzBuzzer
{
    private List<Func<int, string>> buzzList = null;

    public LambdaFizzBuzzer()
    {
        buzzList = new List<Func<int, string>>();
    }

    public void Add(Func<int, string> aBuzz)
    {
        buzzList.Add(aBuzz);
    }

    public IEnumerable<string> FizzBuzz(int maxNumber)
    {
        for (int i = 1; i <= maxNumber; i++)
        {
            var currentRes = string.Empty;
            foreach (var item in buzzList)
                currentRes += item.Invoke(i);
            yield return string.IsNullOrEmpty(currentRes) ? i.ToString() : currentRes;
        }
    }
}

Die Verwendung des FizzBuzzer ist dann entsprechend mit Lambda-Expressions.


LambdaFizzBuzzer fb = new LambdaFizzBuzzer();
fb.Add(i => (i % 3 == 0) ? "Fizz" : string.Empty);
fb.Add(i => (i % 5 == 0) ? "Buzz" : string.Empty);

Fazit

Es gibt unterschiedliche Varianten, Code als Parameter an eine Methode zu übergeben. Interfaces und Lambda Expressions habe ich hier gezeigt. Delegaten und abstrakte Basisklasse sind hier zwar nicht vorgestellt, unterscheiden sich aber konzeptionell nicht großartig von Interfaces und Lambda Expressions. Der architekturelle Unterschied oder Frage ist, ob man den Code wiederverwenden möchte und die Algorithmen Teil der Architektur sind oder ob es “flüchtiger Code” wie Lambda Expressions sind.

Dieses mal ein etwas ausführlicher Blog zum Thema meines Vortrages bei bonn-to-code. Meine viel zu kurzen 20 Minuten hatten eine volle Agenda mit verschiedenen Varianten des Observer Pattern bis hin zu einem Message Dispatcher ;) . Log geht’s!

 

Events statt Attach/Detach

Das Observer Pattern nach GOF war den meisten (oder allen) Zuhörern bereits bekannt, also konnte direkt mit der ersten .NET Modifikation begonnen werden. Die Logik des Attach und Detach von Beobachtern lässt sich wunderbar durch Events und EventHandler ersetzen. Events implementieren genau diese Funktionalität: Eine Liste von Funktionen zu verwalten und letztlich diese aufzurufen. Durch diesen Umbau ist der Code deutlich schlanker geworden und der Verwaltungscode wurde aus dem Subjekt entfernt.

Statusänderungen im größeren Kontext

Das Observer Pattern bietet eine sehr gute Möglichkeit, Statusänderungen an betroffene Ansichten durchzureichen. Dabei wird das Hollywood Prinzip (“Don’t call us, we call you”) beachtet, nachdem das Subjekt die Beobachter informiert und nicht die Beobachter regenmäßig das Subjekt überprüfen. Ein Problem entsteht, wenn mehrere Subjekte zu unterschiedlichen Zeitpunkten angezeigt werden sollen. Ein gutes Beispiel ist eine Liste aus Subjekten mit verschiedenen Vorschau-Ansichten. Nun wird es schwierig, das neue Subjekt an die unbekannte Anzahl an Ansichten zu senden.

Die Lösung hierfür ist ein SubjectNotifier mit statischen Events zum Verteilen von Nachrichten an alle Beobachter. Jeder Beobachter kann mittels SubjectStateChanged eine auf Statusänderung und mittels SubjectSelected auf eine Subjektänderung reagieren. Über die Raise* Methoden können beliebige Ansichten die entsprechenden Event auslösen, falls sich der Status oder das Subjekt geändert hat.

Fazit

Diese Variante, Statusänderungen eines Subjekts über einen SubjectNotifier zu publizieren, hat mehrere Vorteile. Alle Subjekte (auch neue Instanzen) können über einen zentralen Dispatcher publiziert werden. Es erweitert das Hollywood Prinzip aus dem klassischen Observer Pattern insoweit, dass nicht nur der Beobachter mit DEM Subjekt verbunden ist, sondern das der Beobachter noch nicht einmal das Subjekt kennt sondern von irgendwelchen Subjekten benachrichtigt wird. Ein weiterer Vorteil ist, dass das Subjekt absolut keine Logik zum Verteilen und Verwalten von Statusänderungen enthält. Das Subjekt ist unabhängig von interner Verwaltungslogik die nur einzelne Applikationen betrifft und z.B. in Konsolenanwendung nicht benötigt wird.

… dieses war der erste Streich, doch der zweite folgt sogleich!

Nachdem ich mich die letzte Woche mit PostSharp beschäftigt habe schreibe ich hier nun mein Ergebnis: AOP mit PostSharp ist einfach genial. So einfach möchte ich es auch nicht machen, also etwas mehr Informationen.

PostSharp ist ein Framework zum Implementieren von Aspekt Orientierter Programmierung (AOP). Das Ziel ist eine nachträgliche und separate Implementierung von Aspekten in eine Applikation. Klassische Beispiele hierfür sind Logging und Security. PostSharp ist so implementiert, dass die Aspekte mittels Attribut an Methoden, Eigenschaften usw. gehangen werden. Der Post-Compiler von PostSharp ändert nach dem Kompilieren durch Visual Studio die Assembly. PostSharp kann hier heruntergeladen werden. Zum Abschluss noch ein interessanter Hinweis. PostSharp kopiert die Assemblies in das Ausgabeverzeichnis die auf dem Client benötigt werden. PostSharp muss auf den Client nicht installiert werden.

Funktionsweise von PostSharp (Bild: PostSharp)

Das folgende Beispiel zeigt eine DoSomething Methode, welche über einen Logging-Aspect nachträglich geloggt wird. Der Code der LoggingAspect Klasse zeigt die Implementierung der Aspects über das PostSharp Framework. Dem Projekt müssen die Referenzen auf die Assemblies “PostSharp.Laos” und “PostSharp.Public” hinzugefügt werden.

    public class Program
    {
        public static int Main(string[] args)
        {
            DoSomething();

            Console.Read();
            return 0;
        }

        [LogAcpect()]
        private static void DoSomething()
        {
            Console.WriteLine("Hello World");
        }
    }

    [Serializable()]
    public class LogAcpect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Calling: {0} ---", eventArgs.Method);
        }

        public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Leaving: {0} ---", eventArgs.Method);
        }

        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Success: {0} ---", eventArgs.Method);
        }

        public override void OnException(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Exception: {0} @ {1} ---", eventArgs.Exception.Message, eventArgs.Method);
        }
    }

An dieser Stelle noch einen paar Hinweise. Die Klasse LogAspect muss als Serializable deklariert werden, ansonsten gibt es beim Starten einen Fehler. Weiterhin ist die Klasse LogAspect eine Attribut-Klasse und kann wie eine solche mit Eigenschaften und überladenen Konstruktoren implementiert werden. Der folgende Code zeigt eine disassemblierte Version der Funktion. Hier sieht man genau, welche Methoden an welcher Stelle aufgerufen werden und das man mittels FlowBehaviour eine Methode kontrolliert abbrechen kann und das eine Exception in der Aspect-Klasse nach “oben” durchgereicht wird.

private static void DoSomething()
{
    MethodExecutionEventArgs ~laosEventArgs~1;
    try
    {
        ~laosEventArgs~1 = new MethodExecutionEventArgs(~PostSharp~Laos~Implementation.~targetMethod~1, null, null);
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnEntry(~laosEventArgs~1);
        if (~laosEventArgs~1.FlowBehavior != FlowBehavior.Return)
        {
            Console.WriteLine("Hello World");
            ~PostSharp~Laos~Implementation.LogAcpect~1.OnSuccess(~laosEventArgs~1);
        }
    }
    catch (Exception ~exception~0)
    {
        ~laosEventArgs~1.Exception = ~exception~0;
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnException(~laosEventArgs~1);
        switch (~laosEventArgs~1.FlowBehavior)
        {
            case FlowBehavior.Continue:
            case FlowBehavior.Return:
                return;
        }
        throw;
    }
    finally
    {
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnExit(~laosEventArgs~1);
    }
}

Die Implementierung eines Security-Aspect hängt von den Bedürfnissen der Applikation ab. In meinem aktuellen Projekt haben wir in einer Datenbank einem Benutzer eine oder mehrere Funktionen zugewiesen. Der SecurityAspect prüft gegen eine IFunctionProvider-Schnittstelle, ob dem Benutzer die entsprechenden Funktion zugeordnet wurde. Die Funktion wird über den Konstruktor der SecurityAspect-Attibutes angegeben. Besteht der Anwender die Sicherheitsabfrage nicht, wird eine SecurityException geworfen. Diese Exception kann mittels Try-Catch abgefangen werden. Eine solche Implementierung stellt sicher, dass Methoden nur dann ausgeführt werden, wenn die Sicherheitsrichtlinien erfüllt werden. Die GUI stellt selbstverständlich a priori sicher, dass eine solche Methode nicht aufgerufen werden kann, wenn der Anwender keine hinreichenden Berechtigungen besitzt.

        [SecurityAcpect(FunctionEnumeration.DoSomething)]
        private static void DoSomething()
        {
            Console.WriteLine("Hello World");
        }

Die aspektorientierte Programmierung ist nur ein Aspekt des Postsharp Frameworks. Die Stärke des Frameworks liegt in der, wie auch immer gearteten, nachträglichen Modifizierung des Codes.

Powered by WordPress Web Design by SRS Solutions © 2010 Think First Development Design by SRS Solutions