Archiv für den Monat März 2011

All about Scrum

 

Hier ein paar gute Scrum Artikel:

Advertisements

Twitter/Facebook gehört ins ERP-System

 

Ich habe diese Woche eine kleine TestApp geschrieben, um zu prüfen wie viel Aufwand eine Twitter Implementierung in unser hauseigenes ERP-System bedeutet. Wieso habe ich das gemacht? Meiner Meinung nach gibt es eine begrenzte Anzahl an Anwendungen, die ein Anwender täglich mehrfach verwenden kann und will. Die Anzahl bewegt sich zwischen 3 und 4 an der Zahl. Ich setze voraus, dass das ERP-System und der Emailclient (in der Regel Outlook) in jedem Fall hierzu zählen. Hinzu können je nach interner IT Landschaft noch ein Intranet (z.B. SharePoint), ein CTI Client (z.B. Swyx) und bzw. oder ein Messenger (z.B. der Live Messenger) kommen. Wohlgemerkt ist dies nur praktikabel, wenn man mit zwei Monitoren arbeitet. Bei lediglich einem Monitor, wäre diese Anzahl schon zu viel des Guten.

Offensichtlich ist hier nur wenig Spielraum für weitere Clients. Ein manueller Aufruf über die Homepage der jeweiligen Web 2.0 Sites schließe ich aus, weil damit der Browser ständig offen sein müsste und weil die Leute jeden Tag daran denken müssten, diese Seite zu öffnen und sich einzuloggen. Eine große Verbesserung bietet auch die Option von Chrome und Firefox nichts, dass man die Site als Desktop Anwendung verknüpft.

Doch gerade bei diesen Kommunikationskanälen ist es wichtig, dass sie ständig geöffnet sind, da sonst der Vorteil des schnellen Informationsaustausch verloren geht. Kritiker werden jetzt natürlich einwenden, dass die ständigen Updates das Arbeiten behindern. Dem will ich entgegen setzen, dass sie dann diese Systeme nicht dem Business gemäß nutzen, sondern das Verhalten privater Nutzer adaptieren. Beispielsweise kann man lediglich einen Tweet, nämlich den Unternehmens-Tweet, abonnieren, sodass nur diese Informationen angezeigt werden. Man kann auch den Tweet als privat deklarieren.

Um also Twitter erfolgreich in die täglichen Geschäfts- und Kommunikationsprozesse zu integrieren, sollte man Web 2.0 Portale in das ERP System implementieren. Ich will dabei gar nicht darauf eingehen wie das visuell erfolgen könnte. Vielmehr ist mein Anliegen zu zeigen, dass dies schnell und einfach erfolgen kann:

Unser ERP-System basiert auf der .NET Plattform. Deshalb nahm ich mir die Seite www.codeplex.com her und suchte nach einer freien Library, die mir den Zugriff auf Twitter abstrahierte. Ich fand nicht nur ein Framework, sondern gleich mehrere. Ich lud mir NTwitter herunter, erstellte ein Projekt, band die Assembly ein und schrieb folgenden Code, den ich an eine Listbox (ctrl_Output) gehängt habe:

   1: var twitterApi = new NTwitter.Twitter("hecoGmbH", passwordDectrypted);

   2: var tweets = _twitterApi.GetUserTimeline(twitterer);

   3:  

   4: foreach (tweet s in tweets)

   5: {

6: ctrl_Output.Items.Add(s.CreatedAt.ToString() +" Uhr" + (char) 9 +

HttpUtility.HtmlDecode(s.Text));

   7: }

Leider musste ich zum decodieren von HTML Zeichen die Assembly System.Web referenzieren, die nur in Projekten zur Verfügung steht, welche als Target das vollständige .NET Framework voraussetzen. In der Regel sind aber auf Clients nur die Client Versionen von .NET installiert. Deshalb habe ich die Assembly mit ins Projekt kopiert. Die Lösung funktionierte auf Anhieb!

image

Aber Vorsicht: Twitter will keine weiteren Clients von Dritten. Ich gehe jetzt einfach mal davon aus, damit nicht die Integration ins eigene ERP-System gemeint war Smiley mit geöffnetem Mund

SharePoint Testserver: Inhaltsdatenbank aktualisieren

Da man in der Regel immer einen Testserver betreibt, habe ich eine kurze Anleitung geschrieben wie man die Inhaltsdatenbank aus dem Live System in das Testsystem kopiert.

Der Ablauf ist wie folgt:

1. Löschen der bisherigen Inhaltsdatenbank auf dem Testserver über Zentraladministration –> Anwendungsverwaltung –> Inhaltsdatenbanken verwalten. Wählt rechts oben die passende Webanwendung aus. Dann klickt ihr auf die Datenbank. Hier gibt es ganz unten die Option “Inhaltsdatenbank entfernen”. Aktiviert den Haken und klickt auf Ok. Merkt euch den Namen der Datenbank, da ihr diesen in Punkt 4 noch benötigt.

2. Kopiert euch die aktuelle Datenbank vom Live Server. Am einfachsten geht das mit dem SQL Management Studio. Falls ihr euch damit nicht auskennt, dann googelt kurz. Dazu gibt es genügend Einträge. Kopiert die Sicherung auf den Testserver.

3. Beendet auf dem Testsystem im IIS-Manager den Anwendungspool, der mit der Webanwendung verbunden ist.

4. Startet den SQL Dienst auf dem Testserver neu

5. Löscht die immer noch im SQL Server vorhandene Datenbank (den Namen habt ihr euch aus 1 gemerkt) mit dem Management Studio. Aktiviert die Option “Bestehende Verbindungen schließen” im Dialog (optional).

6. Stellt die gesicherte Datenbank wieder her.

7. Startet den Anwendungspool wieder.

8. Geht in der Zentraladministration wieder unter Inhaltsdatenbanken verwalten und wechselt auf die richtige Webanwendung. Klickt auf “Inhaltsdatenbank hinzufügen” und gebt den Datenbankserver und den Datenbanknamen an.

 

Wichtig:

  • Das funktioniert natürlich nur, wenn die entsprechenden SQL Anmeldungen, die auf dem Live-System vorhanden sind, auch auf dem Testsystem sind.
  • Die zwei Server müssen auf dem gleichen Patch-Stand sein.
  • Passt bei absoluten Links auf, da ihr sonst wieder im Live-System landet. Immer relative Links nutzen. Am besten stellt ihr das Farbschema auf dem Dev um, dann merkt ihr sofort, wenn ihr plötzlich die Plattform wechselt.

SharePoint Calculated Fields – Wissenswertes über Formulas

Wer wie ich in einem eigenen Feature berechnete Spalten definieren muss, wird sicherlich auf ähnliche Probleme stoßen. Im Gegensatz zu Formeln, die man über die Oberfläche eingeben kann, befindet man sich bei Spaltendefinitionen innerhalb von XML, sodass Sonderzeichen immer ein leidiges Thema sind. Hier zwei Beispiele:

   1: <Field ID="{FC13F3E2-ED5A-41B6-99A7-FEA6A2605D9E}"

   2:    Name="Heco_ITAM_MachineGroup"

   3:    StaticName="Heco_ITAM_MachineGroup"

   4:    DisplayName="Machine Group"

   5:    Type="Calculated"

   6:    ShowInNewForm="TRUE"

   7:    ShowInEditForm="TRUE"

   8:    Group=" heco ITAM"

   9:    ReadOnly="TRUE"

  10:    Indexed="TRUE"

  11:    Hidden="FALSE"

  12:    Description="Dient zum Gruppieren von zusammengebundener Soft- und Hardware"

  13:    ResultType="Text"

  14:    SourceID="http://schemas.microsoft.com/sharepoint/v3" >

  15:   <FieldRefs>

  16:     <FieldRef Name="Heco_ITAM_AssetNo"/>

  17:     <FieldRef Name="Heco_ITAM_MachineRefId"/>

  18:   </FieldRefs>

  19:   <Formula>=IF(Heco_ITAM_MachineRefId=&quot;&quot;,Heco_ITAM_AssetNo,
Heco_ITAM_MachineRefId)</Formula>

  20: </Field>


  
  1: <Field ID="{9379E5FF-0DDA-4772-91AB-61CAFB9F88E9}"

   2:    Name="Heco_ITAM_OperatingSystem"

   3:    StaticName="Heco_ITAM_OperatingSystem"

   4:    DisplayName="Operating System"

   5:    Type="Calculated"

   6:    ShowInNewForm="TRUE"

   7:    ShowInEditForm="TRUE"

   8:    Group=" heco ITAM"

   9:    ReadOnly="TRUE"

  10:    Indexed="TRUE"

  11:    Description="Dient zum Gruppieren von zusammengebundener Soft- und Hardware"

  12:    ResultType="Text"

  13:    SourceID="http://schemas.microsoft.com/sharepoint/v3" >

  14:   <FieldRefs>

  15:     <FieldRef Name="Heco_ITAM_WindowsVersion"/>

  16:     <FieldRef Name="Heco_ITAM_Architecture"/>

  17:     <FieldRef Name="Heco_ITAM_ServicePack"/>

  18:   </FieldRefs>

  19:   <Formula>=Heco_ITAM_WindowsVersion &amp;&quot; &quot; &amp;
Heco_ITAM_ServicePack &amp;&quot; &quot;&amp; Heco_ITAM_Architecture</Formula>

  20: </Field>

Auf Folgendes sollte man achten:

  • Die FieldRefs benötigen lediglich den Spaltennamen, auf den verweisen wird. Die GUID muss nicht (und darf nicht?) hinterlegt werden
  • Bei den Formeln werden keine Klammern verwendet
  • Um Hochkommata und kaufmännische & abzubilden, müssen die entsprechenden HTML Entities verwendet werden, d.h. &quot; und &amp;. Diese benötigt man z.B. zum Zusammenfügen der Zeichenketten oder zum Prüfen von leeren Eingaben. Eine Tabelle findet ihr hier.
  • Statt Semikolons müssen Kommas verwendet werden, um Funktionsparameter bzw. um Funktionen zu trennen

Projekt: Urlaubspflege mit SharePoint und Export in GDI

 

Seit langem können bei uns die Mitarbeiter ihren Urlaub in SharePoint verwalten. Verwalten bedeutet, dass sie Urlaub eintragen, löschen und übersichtlich anschauen können. Außerdem sehen die Kollegen auf der Startseite übersichtlich, wann wer nicht da ist. Die Eingabemaske sieht wie folgt aus:

image

Und meine eigene Urlaubsübersicht sieht so aus (37,0 sind die Anzahl Tage, die mir dieses Jahr zur Verfügung stehen):

image

Und die rechte Leiste unserer Startseite sieht beispielsweise so aus (Ausschnitt):

image

 

Kurz zum technischen Hintergrund:

Wir verwenden dafür einen eigenen Inhaltstyp Urlaub. Daneben gibt es in unserem Kalender noch die Inhaltstypen ‘Krank’, ‘Außer Haus’ und ‘Im Haus’. Zum Erzeugen der Ausgabe (Spalte Titel), wie man sie auf der Startseite sieht, verwenden wir inzwischen einen Eventhandler, der diese zusammenbaut. Früher war dies ein berechnetes Feld. Wer also nicht programmieren will, muss dies nicht notwendigerweise.

 

Den Geschäftsprozess optimieren und Kosten senken:

Bisher war es so, dass die Buchhaltung am Ende des Monats die Daten in unser Lohnsystem manuell übernommen haben. Das benötigte ca. 1 Manntag. Wenn ihr euch jetzt fragt, ob das dann überhaupt Sinn macht bzw. sich lohnt, dann ist die Antwort ja, da

  • Die Daten müssten so oder so im System erfasst werden
  • Es entfallen die Anfragen der Mitarbeiter nach ihren verbleibenden Urlaubstagen
  • Die Informationen auf der Startseite sind wichtig. Darüber hinaus gibt es noch einen Urlaubskalender, sodass sich z.B. in- und außerhalb der Abteilungen besser abgestimmt werden kann. Kurz: Der Informationsgewinn ist wichtig!

Um den Geschäftsprozess nun vollständig zu automatisieren, benötigten wir noch die Möglichkeit die Daten in unser Lohnsystem von GDI zu exportieren.

Dazu habe ich die Liste der Mitarbeiter, welche gefüllt ist mit Einträgen vom Inhaltstyp ‘heco Mitarbeiter’ um zwei Einträge erweitert:

  • Export zu GDI (interner Feldname: ExportToGDI)
  • Urlaub in Tagen (interner Feldname: HolidayInDays)

Beide Spalten sind vom Typ Ja/Nein. Das sieht dann so aus:

image 

Die Spaltennamen sind selbsterklärend. Nun habe ich mit freundlicher Unterstützung von Olaf Didszun eine Kommandozeilenanwendung geschrieben, die anhand der gesetzten Eigenschaften in der Mitarbeiterliste die Daten aus dem Kalender ausliest und als CSV Datei exportiert (vgl. meinen Artikel dazu hier).

In dem ganzen Prozess gab es zwei besondere Herausforderungen:

  • Monatsübergreifende Einträge
  • Logik für Änderungen nach dem Export

Bei Punkt 1 wäre eine Option gewesen über Gültigkeitsprüfungen (ab SharePoint 2010 für Listen und Spalten out of the box möglich) solche Einträge zu unterbinden. Wir haben uns dagegen entschieden und haben die Logik so programmiert, dass das Programm solche Einträge erkennt und entsprechend monatsgenau abrechnet.

Zu Punkt 2: Das Problem ist, dass es möglich wäre, dass Mitarbeiter im Nachhinein Änderungen vornehmen, die im Vormonat liegen. Das stellt insofern ein Problem dar, als dass wir immer nur am letzten des Monats den Export vornehmen. Das Programm hat und sollte keine Logik dafür enthalten. Deswegen sind wir so Vorgegangen, dass wir eine SharePoint Gruppe angelegt haben, die sich “CalendarAdmins” nennt. Hier sind die Mitarbeiter der Buchhaltung enthalten. Nachdem ein Urlaubseintrag exportiert wurde, wird die Berechtigungsvererbung für das Element gestoppt und es werden alle Berechtigungsgruppen außer die ‘Besucher’ (die haben nur Lesezugriff) und die ‚CalendarAdmins’ entfernt. So ist gewährleistet, dass keine Änderungen nach einem Export mehr durchgeführt werden können, ohne über die die entsprechenden Stellen zu gehen. Der Code hierfür sieht so aus:

   1: urlaubItem.BreakRoleInheritance(true);

   2: SPGroup groupToRemove = urlaubItem.Web.AssociatedOwnerGroup;

   3: urlaubItem.RoleAssignments.Remove(groupToRemove);

   4: groupToRemove = urlaubItem.Web.AssociatedMemberGroup;

   5: urlaubItem.RoleAssignments.Remove(groupToRemove);

   6: urlaubItem.Update();

Meine Quintessenz zum Thema Mitarbeitergespräche

Das Thema Mitarbeitergespräche hat mich die letzten Monaten verstärkt beschäftigt. Ich habe viele Bücher und Artikel dazu gelesen, ich habe mit vielen Kollegen sowohl intern, als auch extern darüber diskutiert, ich habe natürlich Mitarbeitergespräche geführt und ich wurde durch unsere DHBW Studentin im Rahmen ihrer Bachlorthesis dazu befragt.

Die folgenden Punkte sind meine persönlich Quintessenz zu diesem sehr spannenden und wichtigen Thema. Ich will noch Anmerken, dass dies meine Erfahrungen im Rahmen meiner Arbeit bei einer mittelständischen Firma mit einer sehr überschaubaren IT sind.

  • Vertraulichkeit: Es sollte von Anfang an klar geregelt sein, was von dem Besprochenen in welcher Form an Dritte (z.B. den eigenen Vorgesetzten) weitergegeben wird. Meiner Ansicht nach sollten das nur die Zieldefinitionen sein. Ich persönlich spreche mich klar gegen ein Dokument aus, welches in die Personalakte fließt, wenn es Bewertungen gemäß dem Schulnotensystem enthält, da Mitarbeitergespräche innerhalb einer vertraulichen Atmosphäre stattfinden müssen. Bietet man dem Mitarbeiter keinen Wohlfühlraum an, wird das Ziel, nämlich der ehrliche Gedankenaustausch, sowieso verfehlt.
  • Benotungen sind Gift. Außerdem sind sie meiner Ansicht unnötig, unzweckmäßig und ggf. kontraproduktiv. Warum? Zum einen bieten solche Noten gar nicht die nötigen Abstufungen um die feingranulare und detaillierte Bewertung des Faktors Arbeit zuzulassen. Zum hat der Vorgesetzte eine genaue intrinsische Meinung darüber, wie ‚gut‘ oder ’schlecht‘ ein Mitarbeiter ist. Das ist ein rein subjektives Empfinden, das viel mit Gespür für den einzelnen Mitarbeiter zu tun hat.
  •  Regelmäßige Kommunikation: Mitarbeitergespräche sind als großes Ganzes zu verstehen, die die vielen kleinen Gespräche (wie sie idealerweise in einer lebendigen Abteilung täglich oder wöchentlich vorkommen) aufgreifen und denen sie einen strategischen Kontext geben. Das ist ein essentieller Punkt: Ein Mitarbeitergespräch ersetzt die ständigen und kontinuierlichen, meist themaspezifischen Gespräche nicht! Ganz im Gegenteil: Die meisten Mitarbeiter wollen viel häufiger Rückmeldung, v.a. genau dann, wenn sie etwas besonders gut oder schlecht gemacht haben. Mein Gesamteindruck ist, dass fast jeder Mitarbeiter sich mehr Feedback bzw. Input wünscht. Das gilt umso mehr, je jünger der Mitarbeiter ist. Gerade Azubis benötigen meiner Meinung nach mindestens 1x im Monat Rückmeldung, eher mehr. Die Auszubildende wollen die Gedanken und Meinungen des Vorgesetzten hören und das nicht nur zur eigentlichen Arbeit, sondern auch zur eigenen Person. Wenn man sich speziell für so ein Gespräch pro Monat 30 Minuten Zeit reserviert, dann hat das auch eine gewisse Signalwirkung an den Azubi. Nichtsdestotrotz werden richtige Mitarbeitergespräche benötigt, da die Intension und die Möglichkeiten ganz andere sind. Das Ganze hat einen formalen Charakter.
  • Ziele: Zielvereinbarungen bzw. auch das Überprüfen der Zielerreichung sind das Wichtigste am ganzen Gespräch. Neben der Tatsache, dass die Aufstellung derer die Grundlage für Gehaltsempfehlungen und –verhandlungen bildet (und zwar für beide Parteien), sind Zieldefinitionen maßgeblich, damit der Mitarbeiter sich weiter entwickeln und wachsen kann. Und das ist es letztendlich, was man mit einem Mitarbeitergespräch erreichen will: Der Mitarbeiter soll sich entwickeln und dadurch produktiver werden!
  • Aufwand: Der Aufwand für den Vorgesetzten ist enorm, wenn das Werkzeug Mitarbeitergespräch der Personalführung produktiv und positiv verlaufen soll! Ich habe für mich eine Art Tagebuch geführt, in welches ich wöchentlich meine Gedanken festgehalten habe. Ich vermeide absichtlich den Begriff Leistung, da ich auch Dinge wie den Umgang mit Kollegen oder Einschätzungen der Wesenszüge eintrage. Wichtig dabei ist, dass man nur regelmäßige Arbeitsmuster und nicht einmalige Aktionen (seien sie positiv oder negativ) festhält. Das ist natürlich ein erheblicher Zeitaufwand, den ich als Vorgesetzter aber durchaus wieder reinholen kann, wenn mein Mitarbeiter dadurch z.B. schnell eigenständig wird und kontinuierlich gute, verlässliche Arbeitet leistet. Wie vor kurzem ein Bekannter aus einer anderen Firma zu mir gesagt hat: “Wenn meine Mitarbeiter mich nicht mehr brauchen, habe ich meine Aufgabe als Vorgesetzter richtig gemacht.” (lest euch unbedingt dazu dieses Buch durch!). Neben dem Tagebuch habe ich (und auch der Mitarbeiter!) natürlich noch einen typischen Leitfaden, wie man ihn zur Genüge im Internet oder aus der Fachliteratur findet, verwendet.

Das sind meine 5 wesentlichen Punkte. Es gibt sicher noch viele andere Elemente, die es wert sind angesprochen zu werden, aber das würde den Rahmen sprengen.

Eine Anmerkung hätte ich noch: Neben den großen, zentralen Vorteilen bringen Mitarbeitergespräche viele weitere, kleinere Goodies mit. Beispiel: Oftmals wird man informiert, dass der Mitarbeiter mit dem Gedanken des Firmenwechsels spielt (wenn man den angesprochenen Wohlfühlraum geschaffen hat!). Hier bietet sich an, dass man ggf. schon zusammen mit dem Mitarbeiter einen Lösungsweg bespricht, um die Rahmenbedingungen für dessen Erhalt zu schaffen oder aber einen Nachfolger rechtzeitig nachzuziehen.

Fluent Interfaces zum Erstellen von APIs

In meinem Betrieb setzen wir SharePoint als Intranetplattform und GDI als Buchhaltungssystem ein. Kürzlich hatte ich die Anforderung, dass man Urlaubsdaten, welche die Mitarbeiter über SharePoint pflegen, ins GDI Lohnsystem zu überführen. Als ich bei GDI anfragte, gab man mir eine Beschreibung einer Standard CSV-Import-Schnittstelle oder einfach ausgedrückt: Eine Liste wie ich ich eine CSV Datei mit Datensätzen aufbauen muss.

Um das Programm, welches die Daten aus SharePoint extrahiert und GDI als CSV zur Verfügung stellt, für etwaige Dritte, die das Programm später pflegen müssen, einfach zu halten, habe ich mit sogenannten Fluent Interfaces eine rudimentäre API implementiert. Das hat den Vorteil, dass die Logik, welche zum Erstellen einer gültigen Export-Datei benötigt wird, sauber abstrahiert ist. Keiner, der z.B. den Zugriff auf SharePoint ändern will, muss sich noch mit dem Datenformat auseinandersetzen. Wie bin ich vorgegangen?

Zunächst habe ich mir für jedes Feld, welches bei Urlaubseinträgen gefüllt sein muss, ein Interface geschrieben. Der Einfachheit halber habe ich alles in die Datei IGdiApi.cs gepackt. Die Datei sieht so aus:

 

   1: public interface IGdiContracts_Personalnr

   2: {

   3:     IGdiContracts_Fehlzeitennr SetPersonalnr(Int32 personnelNo);

   4: }

   5:

   6: public interface IGdiContracts_Fehlzeitennr

   7: {

   8:     IGdiContracts_Beginn SetFehlzeitennr(Int32 timesAbsentNo);

   9: }

  10:

  11: public interface IGdiContracts_Beginn

  12: {

  13:     IGdiContracts_Ende SetBeginn(DateTime begin);

  14: }

  15:

  16: public interface IGdiContracts_Ende

  17: {

  18:     IGdiContracts_GanzeTage SetEnde(DateTime end);

  19: }

  20:

  21: public interface IGdiContracts_GanzeTage

  22: {

  23:     IGdiContracts_GdiEntry SetGanzeTage(bool allDayEvent);

  24: }

  25:

  26: public interface IGdiContracts_GdiEntry

  27: {

  28:     string GetCsvLine();

  29: }

 

Im Anschluss habe ich die Datei GdiApi.cs erstellt. Die Datei enthält zwei Klassen (wie clean das ist, sei mal dahingestellt). Die erste Klasse bildet den späteren Einstiegspunkt für die API:

   1: public class GdiApi : IGdiContracts_Personalnr

   2: {

   3:     public IGdiContracts_Fehlzeitennr SetPersonalnr(int personnelNo)

   4:     {

   5:         return new GdiEntry(personnelNo);

   6:     }

   7: }

 

Es folgte die zweite Klasse, die die restlichen Interfaces implementiert:

   1: class GdiEntry : IGdiContracts_Fehlzeitennr, IGdiContracts_Beginn, IGdiContracts_Ende, 

                       IGdiContracts_GanzeTage, IGdiContracts_GdiEntry

   2: {

   3:     Int32 _personnelNummer;

   4:     Int32 _timesAbsentNo;

   5:     DateTime _begin;

   6:     DateTime _end;

   7:     char _beginIsAHalfDay;

   8:     char _endIsAHalfDay;

   9:

  10:     public GdiEntry(Int32 personalNummer)

  11:     {

  12:         _personnelNummer = personalNummer;

  13:     }

  14:

  15:     public IGdiContracts_Beginn SetFehlzeitennr(Int32 timesAbsentNo)

  16:     {

  17:         _timesAbsentNo = (Int32) timesAbsentNo;

  18:         return this;

  19:     }

  20:

  21:     public IGdiContracts_Ende SetBeginn(DateTime begin)

  22:     {

  23:         _begin = begin;

  24:         return this;

  25:     }

  26:

  27:     public IGdiContracts_GanzeTage SetEnde(DateTime end)

  28:     {

  29:         _end = end;

  30:         return this;

  31:     }

  32:

  33:     public IGdiContracts_GdiEntry SetGanzeTage(bool allDayEvent)

  34:     {

  35:        //Logik zur Berechnung von _beginIsAHalfDay und _endIsAHalfDay

  36:         return this;

  37:     }

  38:

  39:     public string GetCsvLine()

  40:     {

  41:         System.Text.StringBuilder result = new StringBuilder();

  42:

  43:         result.Append(DateTime.Now.ToString("yyyy") + ";"); //aktuelles Jahr

  44:         result.Append(DateTime.Now.ToString("MM") + ";"); //aktueller Monat

  45:         result.Append(_personnelNummer.ToString() + ";"); //Personalnummer

  46:         //es folgen viele weitere leere Felder

  47:         result.Append(_timesAbsentNo.ToString() + ";"); //Fehlzeitennummer

  48:         result.Append(";"); //Bemerkung zur Fehlzeit

  49:         result.Append(_begin.ToShortDateString() + ";"); //Fehlzeiten Beginn

  50:         result.Append(_end.ToShortDateString() + ";"); //Fehlzeiten Ende

  51:         result.Append(_beginIsAHalfDay + ";"); //Halber Tag Beginn

  52:         result.Append(_endIsAHalfDay + ";"); //Halber Tag Ende

  53:         result.Append("N;"); //Keine Führung auf der Lohnsteuerkarte

  54:         result.Append("N;"); //Arbeitsunfähigkeit durch Unfall verursacht

  55:         result.Append("N;"); //Arbeitunfähigkeitsbescheinigung liegt vor

  56:         result.Append("N;"); //Fehlzeiten fürs Baugewerbe

  57:

  58:         return result.ToString();

  59:     }

  60: }

Die API wird nun wie folgt genutzt:
   1: new GdiApi().

   2:                     SetPersonalnr(iPersonalNr).

   3:                     SetFehlzeitennr(iFehlzeit).

   4:                     SetBeginn(dtStart).

   5:                     SetEnde(dtEnd).

   6:                     SetGanzeTage(bAllDayEvent).

   7:                     GetCsvLine();

Der Vorteil ist, dass der Aufrufer der API genau in dieser Reihenfolge vorgehen muss, um an den benötigten Datensatz zu kommen. Ihm wird genau gesagt wie er Vorgehen muss. Die Logik, wie sich der Datensatz zusammenbaut, ist dem Aufrufer unbekannt. Er weiß beispielsweise nichts von der Berechnung der halben Tage oder den unzähligen zusätzlichen, leeren Feldern.

Warum habe ich nicht einfach alle Interfaces in der ersten Klasse implementiert? Das hätte den Nachteil, dass der Aufrufer zu diesem Zeitpunkt noch alle Methoden gesehen und nicht gewusst hätte, mit welcher er anfangen soll.

Warum habe ich nicht für jedes Interface eine separate Klasse verwendet? Das hätte ich auch machen können, allerdings hätte ich dann die bereits gesetzten Informationen in irgendeiner Form weiter übergeben müssen. Eine Möglichkeit wäre gewesen, ein DTO zu definieren und dieses immer im Konstruktor in die nächste Klasse weiter zu reichen.

Warum habe ich es so gemacht? Weil die Methoden größtenteils nur 2 Zeilen beinhalten und weil eine starke Kohäsion der Daten bzw. Methoden besteht. Vergleicht bitte dazu das Prinzip tell don’t ask.

Zu guter Letzt will ich euch noch auf den Artikel Generische Fluent API mit flexiblen Modulen, den ich soeben noch gefunden habe, aufmerksam machen. Zugegebenermaßen ist er eher advanced und sehr lang, sodass auch ich ihn nicht vollständig durchgelesen habe, aber es ist ein sehr gutes Beispiel.

%d Bloggern gefällt das: