Archiv für den Monat Juli 2011

Generische Factory

Heute will ich mal wieder aus einem Beispiel aus meinem Projektalltag ansprechen.

Es geht um Kalendereinträge in SharePoint. Wir haben 4 verschiedene Typen von Einträgen: Urlaub, Krank, Außer Haus, Im Haus. Im vorliegenden Fall müssen die Typen Urlaub und Krank exportiert werden. Generell ist es so, dass alle Typen bis auf 1-2 Felder die gleichen Eigenschaften besitzen. Ergo arbeite ich mit Vererbung. Die Base Class nennt sich CalendarEntry, von der die zwei Kindsklassen HolidayEntry und SicknessEntry erben (reine Data Transfer Objects).

Da ich die Daten untypisiert von SharePoint erhalte, habe ich mir eine generische Factory gebaut, die mir die 2 besagten Klassen instanziiert und füllt. Das Interface dazu sieht wie folgt aus:

   1: public interface ICalendarEntryFactory<T> where T:CalendarEntry, new()

   2: {

   3:     IEnumerable<T> Build(SPListItemCollection calendarEntries);

   4: }

Die implementierende Klasse enthält also eine Methode namens Build, die mir aus den Kalendereinträgen von SharePoint eine Liste von typisierten Objekten (IEnumberable deswegen, weil es allgemeiner ist als List<T>) zurückgibt. Um welche Objekte es sich handelt, die die Factory erzeugt, wird beim Instanziieren der Factory angegeben, allerdings habe ich den Objekttyp auf CalendarEntry oder eine Ableitung davon eingeschränkt (über where T:CalendarEntry in Zeile 1). Außerdem muss das Objekt (also mein DTO), welches die Factory später erzeugen können soll, einen parameterlosen, öffentlichen Konstruktor haben. Dies gewährleiste ich über das new() in Zeile 1.

Die Klasse, die nun dieses Interface implementiert, sieht wie folgt aus:

1: public class CalendarEntryFactory<T> : ICalendarEntryFactory<T>

where T : CalendarEntry, new()

   2: {

   3:     private readonly IRulesEngine<T> _rulesEngine;

   4:  

   5:     public CalendarEntryFactory(IRulesEngine<T> rulesEngine)

   6:     {

   7:         _rulesEngine = rulesEngine;

   8:     }

   9:  

  10:     public IEnumerable<T> Build(SPListItemCollection calendarEntries)

  11:     {

  12:         ...

  13:     }

  14: }

Noch kurz einen Hinweis zu dem Konstruktorparameter rulesEngine in Zeile 5: Da DTOs niemals Logik enthalten sollten und die Factory, wie der Name schon sagt, lediglich die Funktion erfüllt, dass es Objekte erzeugen kann, benötige ich noch ein Regelwerk, welches die erzeugten DTOs prüft, ob diese korrekt sind. Nur, wenn diese korrekt sind, werden sie der Liste, welche in der Build-Methode zurückgegeben wird, hinzugefügt. Meiner Meinung ist das sehr sinnvoll, um eine stärkere Code Kohäsion zu erhalten. Das entspricht auch den Clean Code Prinzipien Single Responsibility Principle und Seperation of Concerns. Die RulesEngine ist wie man sieht ebenfalls generisch gehalten. Logischerweise mit dem gleichen Typparameter T!

Ein Beispiel, was ich unter anderem mit der RulesEngine prüfe: Ist der Urlaubseintrag vom Typ JAZ (= Jahresarbeitszeit, ohne näher zu erklären, was JAZ bedeutet), so handelt es sich um einen Eintrag, der nicht in das Lohnprogramm exportiert werden darf.

Aber zurück zu der Build-Methode, die mir generisch die Objekte erzeugen soll: Da die Objekte teilweise unterschiedliche Eigenschaften haben (nämlich die, die in den ableitenden Klassen selbst definiert sind), muss ich in der Build-Methode eine Fallunterscheidung machen. Der Code sieht wie folgt aus:

   1: var entriesResult = new List<T>();

   2: T entry;

   3:  

   4: foreach (SPListItem calendarEntry in calendarEntries)

   5: {

   6:     entry = new T();

   7:  

   8:    foreach (SPListItem calendarEntry in calendarEntries)

   9:    {

  10:         //erledige Zuweisungen, die bei allen Eintragstypen gleich sind

  11:  

  12:         if (entry.GetType().Equals(new HolidayEntry().GetType()))

  13:         {

  14:             //erledige spezielle Zuweisungen für den Eintragstyp Urlaub

  15:         }

  16:         else if (entry.GetType().Equals(new SicknessEntry().GetType())) {

  17:             //erledige spezielle Zuweisungen für den Eintragstyp Krank

  18:         }

  19:  

  20:         //erledige Anweisungen, die bei allen Eintragstypen gleich sind

  21:         if (_rulesEngine.Validate(entry)) entriesResult.Add(entry);

  22:    }

  23: }

 

In Zeile 6 sieht man, warum ich bei meinem generischen Constraint dieses “New()” angegeben hatte: Ich muss zur Laufzeit von dem Typ, für den die Factory instanziiert wurde, ein Objekt erzeugen können. Die eigentliche Problematik tritt auf, wenn man die Fallunterscheidung durchführen will (Zeile 12 und 16):

Eine Prüfung auf den Namen des Typs wollte ich nicht durchführen, das ich dann den Namen hardcodiert als Zeichenfolge hätte hinterlegen müssen. Deshalb Instanziiere ich mir in den Zeilen 12 und 16 ein leeres Objekt und hol mir dessen Typ. Der Vorteil ist, dass Refactoring Tools und natürlich auch der Compiler das erkennen. Statt der If-Else-Verschachtelung wollte ich eigentlich eine Switch-Anweisung nehmen (macht natürlich v.a. dann Sinn, wenn man viele solcher Typen hat), allerdings ist hier das Problem, dass der Compiler eine Konstante in den Case-Anweisungen erwartet. Das ist leider nicht gegeben, da ja erst zur Laufzeit bekannt ist, was “entry.GetType()” eigentlich ist.

Peter Hallam, Entwickler bei MS, hatte den Hintergrund dazu bereits 2005 gebloggt. Das macht natürlich Sinn, dass man kein switch auf einen Typen anwenden kann.

Wer allerdings keines der genannten Szenarien aus zuvor erwähntem Blogeintrag bei sich in der Anwendung vorliegen hat, kann sich die Klasse aus dem Blogeintrag switching on types hernehmen, sodass er nicht mit if-else-Verzweigungen arbeiten muss.

Werbung

Sicherer Umgang mit Facebook

Weil immer mehr Anfragen aus geschäftlichem und privaten Umfeld kommen, das Thema politisch (vgl. auch diesen Artikel) inzwischen ein Dauerbrenner ist und wir mit unserer Social Media Gruppe genau hier ansetzen und diskutieren müssen, habe ich mich entschlossen ein paar Referenzen aufzulisten, die euch helfen sollen euer Facebook-Profil sinnvoll zu konfigurieren.

Außerdem will ich euch 3 Tipps geben:

Legt euch einen privaten und einen geschäftlichen Account an. Das hab ich nie gesagt, weil das den Richtlinien von Facebook widerspricht. Deswegen an dieser Stelle der Aufruf an Facebook, dass sie diese Klausel rausnehmen (@Facebook: Es gibt technische Möglichkeiten um eure Statistik trotzdem bereinigt zu halten). Hat im Übrigen noch einen riesigen Vorteil: Wenn ihr den Link zum Business Account in eurer Bewerbung angebt, kommt der zukünftige Arbeitgeber nicht auf die Idee selbst nach euch zu googeln…

Legt euch Gruppen für eure Freunde an und teilt sie in diese ein. Ich habe beispielsweise 5 Gruppen (niedrig, unter normal, normal, über normal, hoch), welche entsprechende Berechtigungen auf meine Inhalte haben. Standardmäßig werden alle von mir veröffentlichten Inhalte nur für die obersten 3 Gruppen freigegeben (auch diese Standardeinstellung ist einfach zu konfigurieren). Außerdem dürfen auch nur diese 3 Gruppen auf meine Pinnwand posten.

Gekürzte URLs: Wie der ein oder andere weiß, gibt es Dienste im Netz, die aus langen Adressen kurze machen, z.B. http://goo.gl. Das ist  zwar eine nette und sinnvolle Funktion, allerdings ist es auch gefährlich, weil man nicht weiß, welche Seite sich dahinter verbirgt. Deswegen solltet ihr, wenn ihr Zweifel habt, prüfen, wohin diese gekürzte URL führt. Für Chrome gibt es die Extension http://goo.gl/fIeyQ (<- das ist so eine gekürzte URL) und für Firefox http://goo.gl/66Hgq. Generell könnt ihr aber einfach auch auf die Seite http://unshort.me/ gehen und dort die gekürzte URL eingeben.

%d Bloggern gefällt das: