Zustandsdiagramm zu Code u. UI (Teil 1)

by admin 28. September 2007 08:06

Zurzeit entwickeln wir eine Ausschreibungsplattform ähnlich zu MyHammer, für die Vermittlung von Druckaufträgen. Eine Ausschreibung nennen wir ein wenig unpräzise Auktion. Die derzeitige Herausforderung ist das Abbilden und der Test verschiedener Zustände sowohl für die UnitTests als auch für das Frontend. Hier ein (vereinfachtes) Zustandsdiagramm, das die Situation ein wenig verdeutlicht:

CropperCapture[25]

Wir haben also 5 Zustände und 8 Übergange (Transitions). Es fehlen noch einige Bedingungen (Guards) und in Real sind noch ein par mehr Übergänge möglich. Da die Anforderungen sich während der Entwicklung mit sehr hoher Wahrscheinlichkeit ändern werden, ist das Diagramm für also absolut ausreichend. Nun die Fragen:

  • Wie lässt sich das schnell und einfach implementieren?
  • Wie lässt sich das Ergebnis schnellst möglich im User Frontend testen, um eine Gefühl dafür zu bekommen was sinnvoll ist und was nicht.

Der erste Schrit ist die Implementierung, hier ein erster Ansatz:

Ganz klassisch bekommt jeder Zustand eine Klasse. Die Möglichen Übergänge werden dessen Methoden.

Fangen wir jedoch erstmal klein an bauen ein Enum für jeden Zustand.

public enum RequestState
{
Active,
ExpiredWithBids,
ExpiredNoBids,
Awarded,
ClosedSuccessful,
ClosedUnsuccessful
}

Es steht in Frage ob das Enum bis zum Ende der Implementierung überleben wird, aber wir gesagt, wir fangen klein an.

Im Nächsten Schritt möchten wir gerne Wissen, in welchem Zustand sich eine Auktion (im Quellcode und weiter Anfrage genannt) gerade befindet. Noch weiß die Anfrage nichts von Zuständen, also müssen wir von Aussen etnscheiden. Zunächst verantwortlich für den aktuellen Zustand wird die Klasse "RequestStateService":

public class RequestStateService
{
private Request _request;

public RequestStateService(Request request)
{
_request
= request;
}

public RequestState GetState()
{
if (Active())
return RequestState.Active;
else if (ClosedUnseccessful())
return RequestState.ClosedUnsuccessful;
else if (ClosedSuccessful())
return RequestState.ClosedSuccessful;
else if (Awarded())
return RequestState.Awarded;
else if (ExpiredHavingBids())
return RequestState.ExpiredWithBids;
else if (ExpiredNoBids())
return RequestState.ExpiredNoBids;

throw new Exception("unknown state" + _request.Id);
}

...

Eine "RequestStateService" Instanz ist also immer zuständig für einen "Request".

 

Noch sind Methoden Stubs, die sich dank Resharper von selbst (ALT Enter) generiert haben.

...

private bool Active()
{
throw new NotImplementedException();
}

private bool ExpiredHavingBids()
{
throw new NotImplementedException();
}

private bool Awarded()
{
throw new NotImplementedException();
}

private bool ClosedSuccessful()
{
throw new NotImplementedException();
}

private bool ClosedUnseccessful()
{
throw new NotImplementedException();
}

private bool ExpiredNoBids()
{
throw new NotImplementedException();
}
}

Im nächsten Schritt müssen wir herausbekommen wie jeder Zustand definiert ist. Die Idee ist es den Zustand über die Eigenschaften eines "Request" Objekts in Erfahrung zu bringen. Der Einfachheit halber nähern wir uns dem Problem mit Kommentaren.

private bool Active()
{
//Not Closed
//Auction End Before Now
}

private bool ExpiredNoBids()
{
//Not Closed
//Auction End After Now
}

private bool ExpiredHavingBids()
{
//Not Closed
//Auction End After Now
//Has Bids
}

private bool Awarded()
{
//Not Closed
//Auction End After Now
//Has Award
}

private bool ClosedSuccessful()
{
//Is Closed
//Has Award

//May be better: Request knows his own state?
}

private bool ClosedUnseccessful()
{
//Is Closed
//Has No Award

//May be better: Request knows his own state?
}

Um das ganze übersichtlicher zu machen und ein wenig mehr Einblick in die Situation zu bekommen hier ein Klassendiagram eines Requests:

 CropperCapture[27]

Ein Request enthält also mehrere Bids (Gebote). Ein Gebot ist aus dieser Sicht Teil eines Requests und ist daher aggregiert. Prinzipiell könnte man auch argumentieren, das ein Gebot auch für selbst existieren kann aber die Notation für Aggregation ist in UML (1.0;) irgendwie immer ein bisschen Geschmackssache, eine Assoziation wäre sicherlich auch Okay. Wobei die MDA Verfechter und Verwender sicherlich über die Art der Verknüpfung eine Aussage über die Implementierung treffen würden. Aber dieses Diagramm soll nur einen Überblick geben, also ist das was ein CodeGenerator aus dem Diagramm machen würde egal. Dazu kommt das Diagramm Tool Gliffy nicht ganz UML konform ist, aber einfach und schnell zu bedienen.  U.a fehlen zum Beispiel Quantifier.

Hier nochmal ein alternatives Diagramm für das gleiche Thema.

CropperCapture[32]

Besser herausgestellt ist, dass ein Request (Anfrage) keinen oder einen Award (Zuschlag) hat.

Während Request und Bid ziemlich vollständig implementiert sind und es auch schon eine ziemlich umfangreiche Oberfläche dafür gibt, fehlt das Verteilen von Zuschlägen noch komplett. Hierfür müssen erstmal die Anforderungen erarbeitet werden, dazu noch das User Interface. Wenn das geschehen ist, geht es in ein, zwei Tagen weiter mit dieser Miniserie.

Abschliessend wie nach jedem Arbeitsschritt:

CropperCapture[33]

Alles Tests laufen lassen und erst dann weitermachen, wenn alles grün ist :-)

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.