Schlagwort-Archive: Patterns

Die großen 4: Pfadfinderregel, Wirtschaftlichkeit, Clean Code, SOLID Principles

Was ist damit gemeint? Gemäß der Pfadfinderregel soll ein Entwickler Code immer besser hinterlassen, als er ihn vorgefunden hat. Clean Code oder guter Code ist häufig dann erreicht, wenn das Mindestmaß an essentiellen Code Prinzipien umgesetzt ist. Das sind die sogenannten SOLID Principles. Jedoch ist guter Code kein Selbstzweck, sondern dient dem größeren Ziel der Wirtschaftlichkeit.

Im folgenden Video zeige ich an einem Praxisbeispiel, wie ich bei einem bestehendem, eher unwichtigem Projekt vorgegangen bin. Timeboxed in 1h so viel refaktorisieren und den Code verbessern wie möglich. Dabei gehe ich auf Prinzipien wie DRY und OCP ein und zeige Techniken wie DI, sowie Tools wie den IoC Container Castle Windsor.

Feedback nehme ich wie immer gerne mit. Wenn ihr mehr von solchen Videos sehen wollt, schreibt mir das in die Kommentare, damit ich weiß: Hier lohnt es sich mehr zu machen.

Zum YouTube Video

Durch Klicken auf das Bild geht es zum Video

Flexibles Bootstrapping durch Composition

In diesem Webcast zeige ich unseren Kompositions-Ansatz für das Bootstrapping. Die Interfaces sind schmal, die Implementierungen überschaubar, das Ganze ist flexibel und lässt sich gut testen.

Hier der gezeigte Code:

   1: public class BootstrapperContext

   2: {

   3:     public BootstrapperContext()

   4:     {

   5:         AppConfig = new ApplicationConfig();

   6:         Container = new WindsorContainer();

   7:     }

   8:  

   9:     public IWindsorContainer Container { get; private set; }

  10:     public ApplicationConfig AppConfig { get; private set; }

  11: }

   1: public interface IAmABootstrapperAction

   2: {

   3:     void Execute(BootstrapperContext context);

   4: }

 

   1: public interface IAmABootstrapperComposition

   2: {

   3:     IEnumerable<IAmABootstrapperAction> Actions { get; }

   4: }

 

   1: public class BootstrapperExecutor

   2: {

   3:     public static void StartupApplication(IAmABootstrapperComposition bootstrapperComposition)

   4:     {

   5:         var exceptionMessage = "Beim Starten der Anwendung ist ein Fehler aufgetreten. Bitte den Support kontaktieren.\n\n";

   6:  

   7:         if (bootstrapperComposition.Actions == null || !bootstrapperComposition.Actions.Any())

   8:         {

   9:             throw new BootstrapperException(exceptionMessage, new ArgumentOutOfRangeException("Auf dem Bootstrapper waren keine Actions definiert"));

  10:         }

  11:  

  12:         var context = new BootstrapperContext();

  13:  

  14:         var time = TimedAction.Run(() =>

  15:                                    {

  16:                                        foreach (var action in bootstrapperComposition.Actions)

  17:                                        {

  18:                                            var actionName = action.GetType().Name;

  19:                                            SiAuto.Main.LogMessage(string.Format("{0} gestartet", actionName));

  20:  

  21:                                            var timeTaken = TimedAction.Run(() => { ExecuteAction(action, context, exceptionMessage); });

  22:  

  23:                                            SiAuto.Main.LogMessage(string.Format("{0} in {1} erfolgreich durchgeführt",

  24:                                                                                 actionName,

  25:                                                                                 timeTaken.Format())

  26:                                                );

  27:                                        }

  28:                                    });

  29:     }

  30:  

  31:     private static void ExecuteAction(IAmABootstrapperAction action, BootstrapperContext context, string exceptionMessage)

  32:     {

  33:         try

  34:         {

  35:             action.Execute(context);

  36:         }

  37:         catch (Exception ex)

  38:         {

  39:             SiAuto.Main.LogException(string.Format("Fehler beim Bootstrapping: {0}", ex.GetFullExceptionMessage()), ex);

  40:             throw new BootstrapperException(string.Format("{0}{1}", exceptionMessage, ex.GetFullExceptionMessage()), ex);

  41:         }

  42:     }

  43: }

 

Welchen Ansatz verfolgt ihr?

Fragen oder Feedback könnt ihr mir gerne als Kommentar hinterlassen.

Precondition Methoden sind Gift für die API

Ich sehe öfters eine API, bei der ich vor dem eigentlichen Aufruf der gewünschten Methode bzw. nach dem Erzeugen des Objekts zuerst eine Initialize oder Configure Methode aufrufen muss.

   1: var foo = new Foo();

   2: foo.Initialize(Bar bar);

   3: foo.Execute();

Das finde ich persönlich unschön, schließlich muss der Aufrufer somit die Komponente gut kennen. Besser wäre, wenn nach der Erzeugung nur die Initialize Methode zur Verfügung stünde. Erst nach dem Aufruf selbiger sollte es möglich sein die Execute Methode aufzurufen.

Hier zwei Beispiele, wie sich das technisch lösen lässt:

   1: public class Foo

   2: {

   3:     private Foo() {}

   4:  

   5:     public static Foo Initialize(Bar bar)

   6:     {

   7:         //do your stuff

   8:         return new Foo();

   9:     }

  10:  

  11:     public void Execute()

  12:     {

  13:         //do something

  14:     }

  15: }

Auf Foo wird dann wie folgt zugegriffen (beachte: this ist in diesem Fall eine Instanz von Bar):

   1: var foo = Foo.Initialize(this);

   2: foo.Execute();

 

Hier eine Lösung für die Verwendung in einem IoC Container. Statt Foo und Bar mit kleinen Änderungen.

Interface1:

   1: public interface ISetupParallelization

   2: {

   3:     IRunParallelization SetupWith(ParallelizationConfig config);

   4: }

Interface2:

   1: public interface IRunParallelization

   2: {

   3:     ParallelizationStatus Run(Queue<dynamic> jobs);       

   4: }

Implementierung beider Interfaces in einer Klasse:

   1: public class CalculationParallelizer : ISetupParallelization, IRunParallelization

   2: {

   3:     public CalculationParallelizer()

   4:     {

   5:     }

   6:  

   7:     public ParallelizationStatus Run(Queue<dynamic> jobs)

   8:     {

   9:         //do something        

  10:     }

  11:  

  12:     public IRunParallelization SetupWith(ParallelizationConfig config)

  13:     {

  14:         _config = config;

  15:         return this;

  16:     }    

  17: }

 

Im vorliegenden Fall würde im Container nur der Service ISetupParallelization registriert werden. Wenn dir der Beitrag gefallen hat, dann vote dafür und/oder hinterlasse einen Kommentar. Hier geht es zu Videos zum Thema von IoC.

Clean Code Series – Episode 1

Hier der Blogeintrag zu dem entsprechenden Video. Es geht um ein Frage eines Bekannten, der SAP Lösungen entwickelt und der von mir Vorschläge für einen Ansatz einer Problemdomäne aus seiner Praxis haben wollte.

Hier die Modalitäten:

Ausgangssituation:

· Wir haben eine Entität, beispielsweise eine Liste von Aktivitäten

· Diese Entität filtert abhängig vom angemeldeten User einige Aktivitäten und liefert den Rest an den Aufrufer zurück

· Der Filtermechanismus und die eigentliche Liste sind auf zwei Klassen aufgeteilt, die beide dasselbe Interface implementieren

· Beide Klassen hängen nur in Form einer Aufrufhierarchie voneinander ab und sind in Reihe geschaltet (Decorator)

· Der Filtermechanismus hat Abhängigkeiten zu einer Datenbankzugriffsklasse

Das schreit ja förmlich nach IoC: Zusammenbauen einer Aufrufhierarchie über Decorator, dazu ggf. weitere Abhängigkeiten auflösen.

Schön. Aber leider ist es eine schlechte Idee, konkrete Entitäten über einen IoC Container zu erzeugen.

Was also tun? Factories bauen?

Ich denke nicht, wäre es nicht geschickter, auf die Klassen das Prototype-Pattern anzuwenden?

Der IoC erzeugt dann lediglich einen Prototypen, der von den Erzeugern konkreter Arbeitslisten als Vorlage genutzt würde. Instanziierung dann folglich über prototype->clone( ).

Was denkst du?

Meine Antwort:

Für mich ist der Ansatz nicht gut modelliert. Könnt ihr das nicht in einer Klasse abbilden? Aus Sicht des Domain Driven Designs könnte man das vielleicht so implementieren:

Ihr habt eine Klasse Activities, welcher ihr beim Erzeugen die Liste alle möglichen Activities übergeben müsst. Im Konstruktor könnt ihr nach Belieben noch eine Validierung vornehmen, z.B. ob die Liste überhaupt Elemente enthält bzw. nicht null ist.

Die Gesamtmenge speichert ihr in einer privaten Variablen. Zusätzlich legt ihr ein readonly Feld für den Zugriff von außen an. Danach benötigt die Klasse nur noch Filtermöglichkeiten. In .NET würde man nur eine Methode schreiben, nämlich ApplyFilter und übergibt an diese einen Lambda Ausdruck. […] Übrigens: Wie du intern die Datenhaltung betreibst, ist dir überlassen. Ich habe es beispielsweise mit einer Liste gemacht, die wesentlich spezieller ist als Enumerable. Da könntest du sogar den Spaß in eine Textdatei wegspeichern und bei Aufruf immer wieder auslesen. Hier ist die klare Datenkapselung eingehalten. Nach außen hin sieht man eine Veröffentlichung auf Basis der sehr generischen IEnumerable Implementierung, intern verwende ich etwas sehr Spezielles.

Nach kurzer Diskussion, was ABAP hier an Möglichkeiten bietet, habe ich folgenden Vorschlag gemacht:

Clean Code Series–Episode 1

Was meint ihr?

Einführung in IoC Container

In diesem Webcast biete ich einen kleinen Einstieg zur Verwendung von IoC Containern. Diese Frameworks helfen bei der Umsetzung des Inversion of Control Principle unter Einsatz von Dependency Injection. Damit ist es möglich gemeinsam an an einem Feature zu arbeiten und so den Work in Progress (WiP) zu minimieren.

  • Alle Blogeinträge zu Castle.Windsor samt den im Video gezeigten Code Beispielen gibt es hier.
  • Die Projekte aus dem Webcast habe ich vorübergehend hier zur Verfügung gestellt.

 

Modularisierung durch Dependency Injection

Workshop Test Driven Development

Nach dem DDD Workshop nahm ich ebenfalls bei Dennis an der TDD Session teil. Persönlich habe ich hier einige Erfahrungen. Angefangen bei dem praktischen Einsatz in unserem ERP System über betriebsinterne Coding Dojos bis hin zu Katas in der Karlsruhe User Group.

In dem Kontext brachte mir der Workshop leider nicht weiter. Meine eigentlichen Erwartungen waren dahingehend gerichtet:

  • Besonders gute TDD/BDD Frameworks kennen lernen
  • Best Practices für konkrete Spezialfälle (Parallelisierung, Zeitstempel, Datenbankzugriff) exerzieren

Allerdings konnte ich den Einblick bekommen, dass es (leider!) immer noch Entwickler gibt, die vom Arbeitgeber kein ReSharper zur Verfügung gestellt bekommen oder mit Windows XP arbeiten müssen. Meiner Ansicht nach ein absolutes No-Go! Hier lässt sich nur hoffen, dass ein Umdenken in den verantwortlichen Stellen noch stattfinden wird.

Workshop – Eine Zeitreise mit dem Desktop Teil 2

Nachdem ich in Teil 1 die aktuell zur Verfügung stehenden Technologien angesprochen habe, soll es nun mehr um Patterns und Clean Code Prinzipien schlechthin gehen.

Während WinForms mit seinem Code Behind in keinster Weise den Anforderungen an die Evolvierbarkeit von Code genügt, wurde mit WPF von Anfang an eine leichte Abwandlung des MVC/MVP Patterns “vermarktet”: Das Model View ViewModel Pattern.

Frank und Silvio stellen hierbei das Seperation of Concerns Principle in den Vordergrund. Besonders gut gefallen hat mir in diesem Kontext auch deren Haltung zu Domain Driven Design. Wie ich, haben sie eine von der ursprünglichen Lehre Evans abweichende Meinung bgzl. der Vermischung von Daten und Logik im selben Objekt. Ralf Westphal hat dies in den vergangenen 6 Monaten ebenfalls in einer 3-teiligen Serie in der dotnetpro (in den Heften 4, 6 und 7 2012) thematisiert.

Anmerkung: Auf dem im Juli stattgefundenen .NET Open Space Süd kam im Übrigen selbige Diskussion. Deshalb an dieser Stelle meine Aufforderung an die Community hierüber vermehrt zu bloggen.

Leider hat MVVM auch diverse Nachteile, darunter das häufige Wiederholen von gleichem bzw. ähnlichem Code. Nachdem kurz über diverse MVVM Frameworks gesprochen wurde, stellten die Referenten eine von ihnen entwickelte im Produktiveinsatz befindliche Lösung vor, die mit ca. 1000 Codezeilen recht überschaubar ist. Laut Aussagen haben sie sich dafür die Schmerzstellen in den eigenen Projekten angeschaut und das Beste für sich aus den verschiedenen Frameworks herausgepickt.

Den Code werde ich, nachdem die Zwei das Feedback des Workshops eingearbeitet haben, mit deren freundlicher Erlaubnis hier online stellen.

%d Bloggern gefällt das: