Archiv für den Monat Juli 2012

MSpec mit ReSharper 7

Soeben habe ich testweise von ReSharper 6 auf Version 7 aktualisiert.

image

Wie auch bei den letzten Upgrades fehlen danach zunächst die Plugins:

image

Zunächst habe ich mit der aktuellen Version 0.5.7 die Installation versucht. Hier fehlt zwar noch das entsprechende Installationsskript für VS2010 + R# 7, aber das ist schnell gebastelt. Leider half dies nichts.

Um eine lange Geschichte abzukürzen: Auch Version 0.5.8 Beta 9 löst das Problem nicht. Ich habe die Info bereits an Alexander Gross von GROSSWEBER weitergeleitet.

Aktualisierung von 23.08 Uhr: AGross teilte mir soeben mit, dass er morgen die finale 0.5.8 bereitstellen will, die das Problem lösen sollte!

Aktualisierung von 23.17 Uhr: Wer nicht warten kann, installiert sich die Version 0.5.8 Beta 11. Danach läuft alles geschmeidig:

Install-Package Machine.Specifications -Version 0.5.8-beta11 -Pre

imageimage

Webcast Strategy Pattern mit IoC

Den Webcast zu diesem Blogeintrag gibt es hier.

Mitarbeitergespräche – Wer fängt an

Heute war es wieder einmal so weit: Es wurden die Mitarbeitergespräche geführt. In der Literatur ist zu lesen, dass man als Vorgesetzter immer als erster sprechen und danach den Mitarbeiter seine Sichtweise darlegen lassen soll. Persönlich halte ich das genau andersherum. Dies hat mehrere Gründe.

Zum einen hat es den Vorteil, dass der Mitarbeiter z.B. beim Feedback (als Teilgespräch von Mitarbeitergesprächen) eine kritische Selbsteinschätzung abliefern muss. Deckt sich diese Selbstbild mit dem eigenen, so beweist das eine gute Eigenwahrnehmung des Mitarbeiters (vorausgesetzt der Vorgesetzte kann ihn korrekt einschätzen). Außerdem dürfte es bedeuten, dass hier aus beiden Sichtweisen objektiv beurteilt wurde. Fängt der Vorgesetzte hingen als erstes an, seine Einschätzung darzulegen, so kann dies die Aussage des Mitarbeiters beeinflussen (umgekehrt halte ich dies für weniger realistisch). Beispielsweise wird ein Mitarbeiter sicherlich nicht ein Verhalten kritisch beäugen, welches zuvor vom Vorgesetzten gelobt wurde.

Ein weiterer Vorteil ist, dass der Vorgesetzte ggf. nur noch Punkte ergänzen muss, sodass er – so wie es eigentlich ideal ist – lediglich das Gespräch passiv lenkt und ein Groß vom Mitarbeiter selbst kommt. Dadurch gibt es weniger Unterbrechungen, schließlich sind Mitarbeiter immer in Versuchung direkt zu widersprechen, schließlich wirken Kritikpunkte – und sei der Sachverhalt noch so euphemistisch ausgedrückt – immer provozierend.

Bei Zielvereinbarungen (als weiteres Teilgespräch) werden sich Mitarbeiter eher mit Zielen identifizieren, welche sie selbst hervorgebracht haben (die man natürlich subtil noch den eigenen Vorstellungen angleicht), als denen, die ihnen “aufgebrummt” wurden.

Wer genauer darüber nachdenkt, wird noch auf einige weitere Vorzüge dieses Vorgehens kommen. An dieser Stelle lasse ich es aber darauf beruhen. Schreibt mit doch bitte einen Kommentar, wenn ihr anderer Meinung seid!

Strategy Pattern mit IoC sauber implementiert

Ein kurzer Hinweis vorab: Den Inhalt des Blogeintrags habe ich auch in einem Webcast zusammengefasst. Das Video gibt es auf YouTube. Den Code findet ihr hier.

In meinem letzten Blogeintrag habe ich das Strategy Pattern in der Praxis gezeigt. Primär gab es noch einen Smell, welchen ich erwähnt hatte: Das Open Closed Principle ist damit noch nicht 100%ig eingehalten und an einer Stelle heble ich mein IoC Konzept aus. Das passiert genau hier:

   1: public StateHandler(IClock clock, IBuildDefaultProposalsValues defaultValues)

   2: {

   3:     Handler = new Dictionary<SelloutStates, ICreateOrderProposal>

   4:               {

   5:                   {SelloutStates.Oversupply, null},

   6:                   {SelloutStates.Predicted, new PredictedGap(defaultValues)},

   7:                   {SelloutStates.Urgent, new UrgentGap(defaultValues, clock)},

   8:                   {SelloutStates.AppointmentWarnings, new AppointmentWarning(defaultValues, clock)},

   9:                   {SelloutStates.Unknown, null},

  10:                   {SelloutStates.Verify, new NoForecastPossible(defaultValues)},

  11:                   {SelloutStates.Ok, new UncomplicatedAppointment(defaultValues, clock)}

  12:               };

  13: }

Zum einen müsste ich die Klasse immer anpassen, wenn ich einen weiteren Status aufnehmen möchte. Zum anderen erzeuge ich mir Instanzen von Klassen, welche ich nicht über den Container erhalten habe, sprich ich hole mir die Abhängigkeiten selbst, statt mir diese gemäß Inversion of Control Principle zuweisen zu lassen. Das wäre zwar möglich gewesen, allerdings würde der Konstruktor dadurch erheblich aufblähen, weil ich pro Status eine Implementierung reinreichen müssten. Dieses Problem könnte ich über Service Location lösen, d.h. ich lasse mir lediglich den Container reinreichen und mache die Auflösung intern. Für mich persönlich auch keine schöne Lösung (Hinweis: Dafür müsste man den Container in sich selbst registrieren…).

Ob nun saubere Dependency Injection mit aufgeblähtem Konstruktor oder Service Location, in jedem Fall bliebe das Problem, dass ich jedes Mal im Container eine weitere Registrierung für einen neuen Status durchführen müsste.

Daraufhin kamen Rückfragen, wie dies am besten zu lösen sei. Das soll Thema dieses Blogeintrags und des dazu passenden Webcasts sein, v.a. weil im Netz auch nur selten saubere Implementierungen anzutreffen sind, die alle Prinzipien einhalten.

Als erstes wird der obere Code gänzlich entfernt, da dieser nicht mehr benötigt wird. Hinter dem Code steckte eigentlich das Factory Pattern. Ich lasse mir durch die Factory (abhängig vom Status) das entsprechende Objekt erzeugen und arbeite dann mit diesem weiter. Castle.Windsor als IoC Container bietet uns hierfür sogenannte interface-based factories an.

Anmerkung: Der folgende Code basiert nicht mehr auf dem bisherigen, welchen ich im vorherigen Blogeintrag verwendet habe. Im Webcast ist dazu alles erläutert und ich habe das Projekt unter Github zur Verfügung gestellt.

   1: yield return Component

   2:     .For<IchVermittleVorschlagsrechner>()

   3:     .AsFactory(c => c.SelectedWith(new VorschlagsrechnerVermittler()));

Wie man sieht, muss man lediglich das Factory-Interface, welches man zuvor selbst ausimplementiert hatte, angeben und im Anschluss ‘AsFactory’ anhängen. Als Parameter übergebe ich den Verweis auf eine Logik, welche die Factory später intern verwendet, um mir das korrekte Objekt (den korrekten Service) zurückzuliefern. Das Bedeutet, dass “new VorschlagsrechnerVermittler” der Factory, wenn auf ihr ein Aufruf stattfindet, sagen muss, was sie zurückgeben soll. Es folgt die Implementierung:

   1: public class VorschlagsrechnerVermittler : DefaultTypedFactoryComponentSelector

   2: {

   3:     protected override string GetComponentName(MethodInfo method, object[] arguments)

   4:     {

   5:         if (method.Name == "Für" && arguments.Length == 1 && arguments[0] is Bestandsstatus)

   6:         {

   7:             var status = arguments[0].ToString();

   8:             //von mir frei gewählte Konvention wie die Komponente im Container heißt

   9:             return string.Format("BerechnerFürArtikelstatus{0}", status);

  10:         }

  11:         return base.GetComponentName(method, arguments);

  12:     }

  13: }

Dieser Code macht nun genau das, was ich vorher explizit im Dictionary definiert habe, allerdings anhand einer Konvention. Die Methode in Zeile 3 liefert als String den Namen der Komponente zurück basieren auf dem Namen des Status, also z.B. ergibt sich für den Status “Ok” der Komponentenname “BerechnerFürArtikelstatusOk”.

Nun fehlt lediglich noch eine kleine Anpassung für den IoC Container. Dieser weiß bisher nichts von einer Komponente “BerechnerFürArtikelstatusOk”. Eine Möglichkeit wäre, dass ich – wie bereits eingangs erwähnt – für jeden Status die Ausprägung im Container registriere, also in diesem Fall beispielsweise so:

   1: yield return Component.For<IchErrechneArtikelZukaufsvorschlag>()

   2:     .ImplementedBy<Ok>()

   3:     .Named("BerechnerFürArtikelstatusOk")

   4:     .LifestyleTransient();

Das widerspricht immer noch dem OCP, sodass ich es wie folgt löse:

   1: yield return AllTypes.FromThisAssembly()

   2:     .BasedOn<IchErrechneArtikelZukaufsvorschlag>()

   3:     .Configure(a => a.Named(string.Format("BerechnerFürArtikelstatus{0}",

   4:         a.Implementation.UnderlyingSystemType.Name)))

   5:     .Configure(a => a.LifestyleTransient());

Zunächst hole ich mir alle Klassen aus der aktuellen Assembly, welche auf “IchErrechneArtikelZukaufsvorschlag” basieren, also alle meinen konkreten Status-Implementierungen. Im Anschluss sage ich noch, welchen Name diese Komponenten im Container bekommen sollen. Hier steht die selbe Konvention wie in dem Code Sample  zuvor. Der Vollständigkeit halber sei erwähnt, dass ich hier gegen das Don’t Repeat Yourself Principle verstoße. Dies soll lediglich der Vereinfachung im Beispiel dienen.

Ab jetzt muss ich, wenn ein weiterer Status hinzu kommt, lediglich eine Implementierung für diesen machen. Bestehender Code muss nicht mehr angefasst werden.

Die Sache mit den Entscheidern – Lean Management

Im ersten Teil habe ich mich mit Pair Programming beschäftigt. Auf dem .NET Open Space Süd kam natürlich auch das Dauerbrennerthema agiles Projektmanagement auf. Deshalb geht es in diesem Artikel genau darum, d.h. nicht um Scrum als eine Ausprägung davon, sondern um die Konzepte dahinter und wie sich diese den verantwortlichen Stellen verkaufen lassen.

Persönlich denke ich, dass sich die Ideen dahinter auch zum großen Teil umsetzen lassen, ohne dass der Prozess vollständig implementiert werden muss. Das könnte die Basis für eine offene Diskussion sein, denn viele Projektmanager (PMs) winken bereits ab, wenn sie Begriffe wie Agilität und Scrum hören.

Fangen wir doch mit dem Work In Progress (WIP) an: Statt an 10 Projekten gleichzeitig zu arbeiten, die erst nach 10 Monaten einsatzbereit sind, ist es betriebswirtschaftlich gesehen immer sinnvoller, ein Projekt nach dem anderen zu realisieren. Bündelt man die Arbeitskraft, wäre je ein Projekt nach einem Monat fertig (vereinfachte Rechnung…), sodass beispielsweise 3 Module nach 3 Monaten effektiv eingesetzt werden und Kosten eingespart bzw. Gewinne maximiert werden könnten. Diese Argumentation dürfte einfach zu gewinnen sein, wenn denn der PM rudimentäre Kenntnisse im Projektmanagement besitzt.

Bei der Projektplanung wird nicht über die innere Qualität diskutiert, d.h. Debatten wie ‘dann machen Sie es erst einmal Quick and Dirty, damit wir Zeit sparen’ entfallen. Mit der Fachabteilung wird lediglich der Umfang verhandelt, sprich wenn Zeit eingespart werden soll, dann nicht durch schlechteren Code, sondern durch weniger Features. Diese Diskussion könnte schon schwieriger zu gewinnen sein. Wenn der PM über längere Berufserfahrung verfügt, dürfte eine ehrliche Betrachtung der Vergangenheitswerte ihn aber ebenfalls zur Einsicht bringen. In jedem Fall – auch unabhängig von der Praxiserfahrung – können hier Beispiele aus dem privaten Leben fruchten: Wenn das Auto nur provisorisch repariert wird, dann folgt das böse Erwachen meistens früher als später.

Regelmäßige Meetings (alle 1-2 Wochen) ergeben sich allein schon aus der schnellen Entwicklung der geschäftlichen Prozesse. Wer aber auch hier Überzeugungsarbeit leisten muss, der könnte sich von dem PM bzw. der Fachabteilung das Spiel Tic Tac Toe erläutern lassen. Mir scheint es unrealistisch, selbst bei diesem einfachen Szenario, dass immer an alles gedacht wird. Was passiert, wenn ein Spieler gewonnen hat? Soll automatisch ein neues Spiel gestartet werden, soll eine Gewinnmeldung ausgegeben werden, etc.. Hierzu gibt es einen Artikel in der dotnet pro, in welchem besagtes Spiel auseinander genommen und aus diesem Kontext heraus beleuchtet wird. Viele der Fragen tauchen erst im Verlauf der Entwicklung auf, sodass ein großes Meeting, in welchem alles besprochen werden kann, zw. unrealistisch bis unmöglich schwankt.

Planungssicherheit ist ein Schlagwort, welches im Kontext des Wasserfallmodells völlig fehl am Platze ist. Welches Projekt, das auf 2, 5 oder 10 Jahre angelegt war, konnte tatsächlich die Rahmenplanung (Kosten, Zeit) einhalten. Die Zahl dürfte gegen 0 gehen. Vergangenheitsbetrachtungen dürften hier auch schnell Aufschluss geben. Es existieren keine Referenzprojekte? In der Tagesschau hört man wöchentlich von solchen, sei es nun der Bau eines neuen Flughafens oder die Einführung eines Mautsystems. Durch die kurzen Iterationen erreicht man schnell (meistens innerhalb von 8-12 Wochen) einen guten Eindruck dafür, was innerhalb von 2 Wochen erreichbar ist. Was scheint nun plausibler? Eine Planung, die auf Vergangenheitswerten beruht und die vom Kleinen ins Große rechnet oder der umgekehrte Weg, bei welchem 2 Jahre angesetzt werden und dann muss das Projekt abgeschlossen sein? Lieber den Spatz in der Hand oder die Taube auf dem Dach?

Zusammenfassend lässt sich sagen, dass es vielleicht langfristig mehr von Erfolg gekrönt ist, wenn sukzessive einzelne Punkte aus dem agilen Projektmanagement integriert werden, ohne diese vor dem PM als agil zu deklarieren. Wenn dann sowieso bereits zu 50% nach der entsprechenden Ideologie gehandelt wird, wäre ein Scrum Buch vielleicht das passende Geburtstagsgeschenk für den PM. Beim Lesen sollte es ihm/ihr dann wie Schuppen aus den Haaren fallen. Idealerweise hält der PM dann eure Idee noch für seine eigene…

Strategy Pattern in der Praxis

Heute gibt es von mir einen kleinen Bericht aus der Praxis. Ich habe zunächst folgenden Code geschrieben, welchen ich nun, nachdem die Logik fertig ist, überarbeite.

Ausgangssituation: Anhand diverser Eigenschaften eines Artikels wird dessen Status ermittelt. Abhängig vom Status soll eine Aktion ausgelöst werden. Im vorliegenden Fall ist dies so gelöst, dass eine Switch-Anweisung anhand des Status in den entsprechenden Zweig springt (vgl. ab Zeile 3). In dem jeweiligen Zweig wird eine dedizierte Methode innerhalb der Klasse aufgerufen.

   1: var state = Decide(entry, folder);

   2:  

   3: switch (state)

   4: {

   5:     case SelloutStates.Urgent:

   6:         CreateUrgentGap(result, entry, folder);

   7:         break;

   8:  

   9:     case SelloutStates.Predicted:

  10:         CreatePredictedGap(result,entry,folder);

  11:         break;

  12:  

  13:     case SelloutStates.Verify:

  14:         ForecastNotPossible(result, entry, folder);

  15:         break;

  16:     

  17:     case SelloutStates.AppointmentWarnings:

  18:         CreateAppointmentWarning(result, entry, folder);

  19:         break;

  20:  

  21:     case SelloutStates.Ok:

  22:         CreateUncomplicatedAppointment(result, entry, folder);

  23:         break;

  24:  

  25:     case SelloutStates.Unknown:

  26:         throw new ApplicationException(string.Format("Unbekannter Status für Artikel '{0}'", entry.Article.NumberFormatted));

  27:  

  28:     default:

  29:         throw new ApplicationException(string.Format("Keine Logik zur Verarbeitung des Status für Artikel '{0}'", entry.Article.NumberFormatted));

  30: }

  31:  

  32: SelloutStates Decide(OrderProposalArticleDataEntry entry, OrderProposalFolder folder)

  33: {

  34:     if (folder == null)

  35:         throw new ApplicationException(String.Format("Der Artikel '{0}' war keiner Mappe zugeordnet", entry.Article.NumberFormatted));

  36:  

  37:     if (!entry.AverageUsage.HasValue || !entry.SelloutDate.HasValue)

  38:         return SelloutStates.Verify;

  39:  

  40:     if (!entry.NextIntakeDateAfterSellout.HasValue)

  41:         throw new ApplicationException(String.Format("Bei Artikel '{0}' war kein nächster Wareineingang gesetzt, obwohl ein Leerverkaufsdatum existiert",

  42:             entry.Article.NumberFormatted));

  43:  

  44:     if (entry.SelloutDate < _clock.Now)

  45:         return SelloutStates.Urgent;

  46:  

  47:  

  48:  

  49:     if (entry.SelloutDate < entry.NextIntakeDateAfterSellout)

  50:     {

  51:         var nextPossibleIntake = _clock.Now.AddDays(folder.HandlingTimeInDays).AddDays(folder.DeliveryPeriodInDays);

  52:         var orderInTimeIsPossible = entry.SelloutDate.Value.TimeSpanIgnoringTimeOfDay(nextPossibleIntake).Days >= 0;

  53:  

  54:         return orderInTimeIsPossible ? SelloutStates.AppointmentWarnings : SelloutStates.Predicted;

  55:     }

  56:  

  57:     return SelloutStates.Ok;

  58: }

Ganz offensichtlich wird hier gegen das Open-Closed-Principle und Single-Responsibility-Principle verstoßen, denn wenn ich nun einen weiteren Status aufnehmen möchte, müsste ich das Switch-Statement erweitern und gleichzeitig noch eine weitere Methode einfügen. Übersichtlich sieht das Ganze auch nicht aus.

Deshalb habe ich ein Interface erstellt:

   1: public interface ICreateOrderProposal

   2: {

   3:     void CreateWith(OrderProposals result, OrderProposalArticleDataEntry entry, OrderProposalFolder folder);

   4: }

Für jeden Status habe ich sodann eine Klasse erzeugt, welche das Interface implementiert. Hier ein Beispiel:

   1: public class NoForecastPossible : ICreateOrderProposal

   2: {

   3:     readonly IBuildDefaultProposalsValues _defaultValues;

   4:  

   5:     public NoForecastPossible(IBuildDefaultProposalsValues defaultValues)

   6:     {

   7:         _defaultValues = defaultValues;

   8:     }

   9:  

  10:     public void CreateWith(OrderProposals result, OrderProposalArticleDataEntry entry, OrderProposalFolder folder)

  11:     {

  12:         var appointment = _defaultValues.ForAppointment(entry, folder);

  13:  

  14:         appointment.CalculatedOrderInDays = null;

  15:         appointment.Status = SelloutStates.Verify;

  16:  

  17:         result.Appointments.Add(appointment);

  18:     }

  19: }

 

Zu guter Letzt habe ich mir noch eine Implementierung geschrieben, die in einem Dictionary die Zuordnung von State zu entsprechender Programmlogik hält (ebenfalls als Interface abstrahiert!):

   1: public class StateHandler : IAllocateHandlerToState

   2: {

   3:     private Dictionary<SelloutStates, ICreateOrderProposal> Handler { get; set; }

   4:  

   5:  

   6:     public StateHandler(IClock clock, IBuildDefaultProposalsValues defaultValues)

   7:     {

   8:         Handler = new Dictionary<SelloutStates, ICreateOrderProposal>

   9:                   {

  10:                       {SelloutStates.Oversupply, null},

  11:                       {SelloutStates.Predicted, new PredictedGap(defaultValues)},

  12:                       {SelloutStates.Urgent, new UrgentGap(defaultValues)},

  13:                       {SelloutStates.AppointmentWarnings, new AppointmentWarning(defaultValues, clock)},

  14:                       {SelloutStates.Unknown, null},

  15:                       {SelloutStates.Verify, new NoForecastPossible(defaultValues)},

  16:                       {SelloutStates.Ok, new UncomplicatedAppointment(defaultValues, clock)}

  17:                   };

  18:     }

  19:  

  20:     

  21:     public ICreateOrderProposal Handle(SelloutStates state)

  22:     {

  23:         return Handler[state];

  24:     }

  25: }

Anmerkung: Wie ihr seht verwende ich eine IClock. Das liegt daran, dass ich inzwischen überall im Code statt DateTime.Now immer auf eine Implementierung von IClock zugreife, um Unit Tests zu ermöglichen, die Datumsangaben prüfen.

 

Das Ergebnis ist, dass ich nur noch einen Zweizeiler habe:

   1: var state = _orderProposalState.Evaluate(entry, folder);

   2: _orderProposalFactory.Handle(state).CreateWith(result,entry,folder);

Falls ihr euch wundert, wo die Methode ‘decide’ abgeblieben ist: Diese habe ich im gleichen Zug ebenfalls in eine eigene Klasse extrahiert. Die Implementierung dazu erhalte ich über den Konstruktor vom IoC. Die Variable ‘_orderProposalState’ enthält nun diese Logik.

Vorteil? Ganz offensichtlich ist die Klasse übersichtlicher geworden und zwar erheblich. Natürlich könnte man jetzt argumentieren, dass ich die Logik auf weitere Klassen verteilt habe (was neben dem SRP übrigens auch dem Separation of concerns Prinzip entspricht). Das ist natürlich richtig, aber in dieser Klasse sehe ich auf Anhieb den Zusammenhang: Ich ermittle einen Status eines Artikels. Im Anschluss reagiere ich in einer speziell dafür vorgesehenen Geschäftslogik. Wenn ich nun wissen will, wie der Status ermittelt wird, dann gehe ich in die dafür zuständige Klasse. Will ich hingegen wissen, wie ein Artikel mit diesem speziellen Status verarbeitet wird, gehe ich in die dedizierte Klasse. Will ich das Ganze erweitern, dann baue ich einfach einen weiteren entsprechenden Handler. Bestehender Code muss nicht angefasst werden (Ausnahme: Die Klasse StateHandler, aber auch dies könnte man anders lösen!). Und zu guter Letzt gestalten sich die Tests deutlich einfacher.

SharePoint Suche

Wenn die SharePoint Suche bei manchen Anwendern etwas anzeigt und bei anderen nicht, so könnte das Problem darin bestehen, dass das entsprechende Domänen-Objekt nicht korrekt konfiguriert ist. Im vorliegenden Fall war es so, dass die Kollegen aus dem Marketing keine Suchergebnisse im Wiki angezeigt bekommen haben, wir in der IT hingegen schon. Die Lösung stand in diesem Blogeintrag.

<<Lt. Doku muss man dem Search-Dienst User im Active Directory unter dem Reiter Sicherheit – bei Authentifizierte Benutzer das Recht "Lesen" zuweißen.>>

Inzwischen läuft alles problemlos…

%d Bloggern gefällt das: