Mappa din specifikation (User Story) till kod.
Förord
Det finns två saker som oftast utgör de största frågetecken för en utvecklare, det ena är hur man får sin specifikation till kod och det andra är hur man skall mapp sina objekt mot databasen. Det sistnämnda beror dock lite på vilken design princip man nyttjar. Vad denna artikel skall ta upp är det första problemet, hur man får sin specifikation att bli kod.
Förkunskaper:
Kunna förstå UMLKunna läsa C# kod
Introduktion
Det finns två saker som oftast utgör de största frågetecken för en utvecklare, det ena är hur man får sin specifikation till kod och det andra är hur man skall mapp sina objekt mot databasen. Det sistnämnda beror dock lite på vilken design princip man nyttjar. Vad denna artikel skall ta upp är det första problemet, hur man får sin specifikation att bli kod. Många forskare arbetar idag med frågor kring detta, de försöker lista ut hur de skall göra detta mappningssteg så automatiskt som möjligt en ny idé är LOP (Language Oriented Programming.) (Där man på nått sätt modellerar upp sin spec och automatiskt får det att bli effektiv kod. Jag tror dock det kommer att dröja ett tag tills vi kommit så långt, det är många faktorer som måste in så som säkerhet, återanvändning, kompabilitet, versionering etc. Men det är klart ett intressant område, dock får vi nöja oss att själva manuellt sköta denna mappning i dagsläget.
Specifikationer.
Det finns många olika sätt att skriva specifikationer på, det finns de som är lite sämre och de som är lite bättre men alla har ett och samma syfte, de skall förklara för utvecklaren och andra parter hur systemet skall fungera. I denna artikel kommer jag att ta upp User Stories (Jag tycker om både User Stories och Use cases inte de traditionella platta specar som jag ser att många skriver. Med platt spec menar jag där man radar upp en rad funktioner och krav baserat på punkter eller rakt upp och ner få till så mycket detaljer som möjligt. Min erfarenhet av detta är att det skapas lätt missförstånd för man vet inte exakt hur användaren skall använda funktionen, den kan ibland bli för teknisk så interaktionen blir svårhanterad. Ex på sådan spec kan vara.
Tärningsspel.
• 2 tärningar
• 1 spelare
• Vinst om båda tärningarna är 7 Annars förlust.
Eller
Tärningsspel
1.0 Systemet skall ha två tärningar som visar värden, dessa värden skall vara av typen integer.
2.0 Man skall kunna trycka på en knapp vid namn spela för att starta spelet, där Spelet ser till så träningarna slumpar fram ett tal.
3.0 Man har vunnit om tärningarnas tal tillsammans blir sju annars blir det förlust.
4.0 Om tärningarna blir sju skall en grön lampa tändas, annars en röd.
Nu tog jag ett väldigt enkelt exempel så denna spec kanske inte verkar så konstig, dock finns det ju större system med massa text hur systemet skall fungera utan någon direkt ordning. Jag kommer att använda Tärningsspel som exempel genom hela artikeln för att inte göra detta för komplicerat.
User stories
User stories är egentligen ett gammalt sätt att beskriva krav på, men i mångas ögon väldigt nytt. Det hela liknar Usee Cases där man gör en berättelse över systemet. Den stora skillnaden är att Use Case är mer detaljerat och innehåller alternativa flöden på ett sätt User Stories inte gör. Ex på Use Case flöde. Tärningsspel
Huvud flöde:
1. En användare kastar två tärningar
2. om summan blir sju har personen vunnit.
3. Signalera med lampa.
Alternativt Flöde.
3a. Vid annat värde än sju signalera med röd lampa.
3b. Spelaren kan kasta tärningarna igen.
*. Vid fel se Usecase 10 Felhantering.
User Stories är som ett kort säg en Post It lapp där man skriver ner en användarberättlse för varje kort, oftast skriven av kunden själv. Ex.
Tärningsspel
En användare kastar två tärningar, om värdet är sju har personen vunnit och en grön lampa tänds, annars tänds en röd, användarna kan när som helst kasta om tärningen efter röd lampa.
Berättandet utgår från hur kunden uppfattar funktionen och inte hur en tekniker uppfattar den. I många fall vill man ha med text som; resultat skall sparas i databas där vi har en tabel vid namn bla bla…Detta är inte speciellt intressant det är något utvecklarna tillsammans med en arkitekt (om man inte är båda) tar fram i sina tekniska specifikationer och eller modeller som en kund oftast inte behöver känna till, så vida inte kunden själv vill underhålla systemet efteråt. Det är många som vill ha med teknisk information i sina specar men försök undvika dem oftast behövs inte dessa förrän senare i processen. Det är felaktigt att ha med det i Use cases eller User storeis då man oftast inte vet hur sakerna skall se ut eller sättas ihop, att redan vid spec stadiet dra in hur en databas skall se ut etc gör bara att man mer eller mindre sätter en datakälla i fokus och den berättar mer hur applikationen skall fungera än applikationen själv. Detta är ett tankesätt som vi egentligen måste försöka bli av med, vet ej var den härstammar från men det är inte många modeller idag som föredrar sådant byggsätt. Nog om det, nu går vi vidare. En berättelse skall alltså berättas som om det vore en saga över något som skall eller kan ske. User Stories kan byggas ut med flera punkter så som vad man skall testa, special krav m.m.
Låt säga att man är en liten grupp på två till tre personer där ena personen är kunden. Kunden berättar ovan berättelse för utvecklarna.
Utvecklare 1 kontrar med: vad skall vi ha för tärning?
Kunden: Ja, vet inte vad anser ni?
Utvecklare 2: Det borde ju räcka med en sexsidig, beror ju dock på svårhetsgraden.
Kunden: Jo en sexsidig räcker gott och väl.
Utvecklare 2: Låter bra, som jag även ser det behöver vi ju testa det hela, antar att ett testcase i detta fall blir enkelt, vi bara försäkrar oss om att båda tärningarna tillsammans visar sju va?
Kund: Absolut, det är det enda viktiga.
Då detta mer eller mindre blev ett krav så skriver man upp det som sades på User Stories kortet.
Tärningsspel
En användare kastar två tärningar, om värdet är sju har personen vunnit och en grön lampa tänds, annars tänds en röd, användarna kan när som helst kasta om tärningen efter röd lampa.
Notera:
Utvecklare 1 säger att en sexsidig tärning bör räcka.
Test:
Testa så båda tärningarna kan få värde sju tillsammans.
Anledningen till att man anger namn under notera är mer eller mindre för att man skall veta vem som sa vad så man enkelt kan gå tillbaka till personen och säga, du sa detta kan du förklara mer hur du tänkt dig? eller liknande. Sedan kan man inte smita undan med klassiska kommentarer som, Nej det har inte jag sagt, och helt plötsligt har man en spec med saker där ingen känns vid att de sagt det som skrivits ner.
För att inte fastna i detaljerad information om User Stories så går vi vidare.
Omvandla User Stories till en Domän modell
När vi nu har vår(a) User stories klara och satt upp en prioritetsordning för dem så kommer nästa steg, det är att mappa dem till en modell som sedan kan omvandlas till vår kod. Först kanske jag skall förklara vad en Domän modell är. En Domän modell är en skiss rätt lik klass diagram i UML där man visar sina objekt mer relaterat till hur verkligheten ser ut. Denna modell är sedan underlaget för att få fram en mer teknisk modell som i slutändan blir vårt Class diagram och ev. databas diagram om databas skall användas. Detta är ett av skälen till varför specen inte skall ta upp saker så som hur databasen skall se ut för detta är nått vi bestämmer när vi tar fram vår domän modell. Jag kommer slarva lite med UML Notationen och inte följa den punkt och pricka, mycket för att i alla projekt måste man inte känna till alla tillstånd eller ange hela notationen, det kan bara skapa mer röra än nytta. För att få fram en Domän modell tar vi och plockar ut alla substantiv från vår berättelse dessa kommer att utgöra våra grund objekt.
Tärningsspel
En användare kastar två tärningar, om värdet är sju har personen vunnit och en grön lampa tänds, annars tänds en röd, användarna kan när som helst kasta om tärningen efter röd lampa.
Jag får fram.
• Tärningsspel
• Användare
• Tärning
• Lampa
Dessa blir våra Objekt för vår Domän modell. Nästa steg är att plocka ut verben som utgör våra metoder.
Tärningsspel
En användare kastar två tärningar, om värdet är sju har personen vunnit och en grön lampa tänds, annars tänds en röd, användarna kan när som helst kasta om tärningen efter röd lampa.
Jag får fram.
• Kasta
• Tända
Nästa steg är att identifiera egenskaper, adjektiv.
Tärningsspel
En användare kastar två tärningar, om värdet är sju har personen vunnit och en grön lampa tänds, annars tänds en röd, användarna kan när som helst kasta om tärningen efter rödlampa.
Jag får då fram.
• Värde
• Grön och Röd färg
Ibland ingår gömda egenskaper i vår berättelse och det är inte helt fel att ta fram dem heller. Ex en användare har ju troligen ett namn. Vilket jag tycker vi kan lägga till. Bara för att göra modellen mer verklig. För att mer eller mindre visa andra eller oss själva att spelaren bara kan vara en.
Så om vi nu gör en modell över dessa får vi fram följande.
Denna skiss säger att En(1) Användare kastar två(2) Tärningar där ett (1)Tärningsspel spelas av en(1) användare. Där ett(1) tärningsspel använder sig av två(2) tärningar. Där ett(1) tärningsspel använder en(1) lampa.
Nu har vi översatt vår User Storie till en Domän modell. Nästa steg är att få denna modell att bli anpassningsbar för språket. Det vill säga få den att bli en tekniks skiss för en mjukvara. Vi skall nu omvandla denna Domän Modell till ett klass diagram.
Många tycker att det är enklare att skapa ett så kallat sekvensdiagram för att följa det sekventiella flödet. Jag väljer att göra en sådan skiss för att enklare visa vilken ordning saker och ting kommer att anropas efter.
Sekvensdiagram
Ett sekvensdiagram är lite vad det låter som, ett diagram som visar hur sekvensen ser ur. I vårt fall måste vi rita ut spelets flöde. Vi vet ju att användaren spelar ett tärningsspel och kastar två tärningar, och att tärningarna visar olika värden. Vi vet även att användaren blir antingen jag eller du, det är vi som ev. trycker på en knapp eller nått som drar igång funktionen som kastar tärningarna. Eftersom Användaren inte är intressant i detta skede då denna inte kommer bli en del av mjukvaran så utesluter vi denna. Dock måste vi ange att något spelas detta gör ju själva tärningsspelet så vi låter den ta hand om spelet. När vi tar fram ett sekvensdiagram gör vi detta genom att använda de objekt vi identifierat i vår Domän modell ovan. Ett sekvens diagram för vår berättelse kan se ut på följande sätt. Vad vi ser här är alltså att Tärningsspel anropas genom metoden spela() som i sin tur ber tärning ett att kasta därefter frågar tärningsspelet tärning ett om dess värde och sparar undan detta i värde1. Sedan säger Tärningsspelet till tärning två att göra samma sak, därefter skall lampan tändas och tärningsspelet kommer att tala om vilken färg som skall visas. Här har vi en liten klurig sak. Jag valde att tärningsspelet skickar färgen, jag hade lika gärna kunnat anropa två olika metoder hos lampan eller skickat ett boliskt värde till den. Men för att förtydliga händelsen skickar jag med en färg. De vita stora blocken som finns på de streckade trådarna är hur länge de olika objekten är aktiva. Tärningsspelet är igång från start till slut, tärningarna bara när man kastar dem och hämtar dess värde och lampan när en färg skall visas.
När vi nu har denna skiss framför oss så kan vi omvandla den till ett klass diagram. Vi ser nu att vårt Tärningsspel har två tärningar och en lampa. Dessa två tärningar måste via språk (vår kod) anges med två instanser för att tärningsspelet skall kunna kasta båda och vår lampa blir en instans då vi inte behöver mer än en lampa. Om vi nu bygger om detta till ett klassdiagram kommer vi få fram följande skiss.
Vad vi gjort hittills är att vi från vår berättelse tog ut verb, substantiv och adjektiv vi skapade sedan en Domän Modell utifrån detta skapade vi ett sekvensdiagram som visade hur dessa objekt anropades baserat efter vår berättelse. Sedan tog vi och gjorde ett klass diagram utifrån vårt sekvensdiagram. I vår klass diagram ser vi nu vilka egenskaper och metoder de olika klasserna fick. Om vi hade behövt en databas hade denna tagits fram parallellt med klass diagrammet då vi även skulle få veta vilka värden vi ev. måste spara ner i den.
Mappa klassdiagram till kod
Nu när vi har vårt klassdiagram är det bara att sätta igång att översätta den till kod. I vanliga fall hade jag använt mig av Test Driven Development (TDD) (http://c2.com/cgi/wiki?TestDrivenDevelopment)här, där jag först skapar upp testen innan jag bygger koden. Men för att inte gå in för djupt och få denna artikel för stor hoppar jag över detta. Men jag rekommenderar att ni tar en titt på TDD och testramverk så som NUnit (http://www.nunit.org/) för .Net, det kommer ge en helt annan syn och öka kvalitén av er kod när ni bygger. Först kan vi skapa vår tärning. (Har valt att göra detta med engelska ord. Vet att alla skisser m.m. hade svenska men det var för att förtydliga det hela bättre. Har som regel att alltid koda i engelska, hoppas det går lika bra för det.)
using System;
namespace Nsquared2.DiceGame
{
public class Dice
{
int _faceValue;
public Dice()
{}
public void Roll()
{}
public int GetFaceValue
{
get { return ( this._faceValue ); }
}
}
}
Här har vi skalet för vår tärning.
Jag kommer att använda mig av Radom klassen i min Roll() (Kasta) metod för att slumpa fram olika nummer. Dock finns det ett litet problem med denna klass. Random fungerar så att den slänger upp en massa random tal mellan de intervall man anger med en gång då den används. Då datorerna idag är så snabba kommer två instanser av random att ge samma tal. Alltså då jag skapar mina Dice (Tärning(ar)) kommer även en Random pär tärning att skapas, då detta sker så snabbt kommer alltid mina Random instanser att ge samma siffra för båda tärningarna. För att lösa detta sätter jag min tärning som statisk. Detta betyder att båda tärningarna kommer att dela på samma Random instans och på så vis kommer tärning ett att ge random instansens förstas tal och instans två att ge random instansens andra tal.Vår slutliga kod för tärning blir alltså följande.
using System;
namespace Nsquared2.DiceGame
{
public class Dice
{
int _faceValue;
static Random myRandom = null;
public Dice()
{
}
public void Roll()
{
if(myRandom == null)
myRandom = new Random();
this._faceValue = myRandom.Next(1,7); //6 sidig tärning
}
public int GetFaceValue
{
get { return ( this._faceValue ); }
}
}
}
Nästa steg är att skapa Lampan. För att göra det enklare valde jag att göra så lampan bara skriver ut sin färg på skärmen. Ett snyggare gränsnitt vore att verkligen göra en lampa som ändars mellan röd och grön bild. Men det blir för komplicerat att få med det i denna artikel, syftet här är att visa hur man från en berättesle når en fungerande kod.
using System;
using System.Drawing;
namespace Nsquared2.DiceGame
{
public class Lamp
{
public Lamp()
{}
public void Ignite(Color color)
{
Console.WriteLine(color.Name);
}
}
}
Sedan tar vi och bygger vårt Tärningsspel (Game)
using System;
using System.Drawing;
namespace Nsquared2.DiceGame
{
public class Game
{
Dice myDice1 = new Dice();
Dice myDice2 = new Dice();
public Game()
{
}
public void Play()
{
myDice1.Roll();
myDice2.Roll();
if(myDice1.GetFaceValue() + myDice2.GetFaceValue()== 7)
new Lamp().Ignite(Color.Green);
else
new Lamp().Ignite(Color.Red);
}
}
}
Som ni kan se instansierar jag två tärningar, i Play() (Spela) så kastar (Roll()) jag båda tärningarna och summerar deras värden därefter anropar jag lampans tändning (Ignite) med rätt färg baserat på om det gick bra eller ej.
Konsolapplikationen för att starta det hela är väldigt enkel.
using System;
using Nsquared2.DiceGame;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Game myGame = new Game();
myGame.Play();
}
}
}
ASP och ASP .Net
Även när man bygger applikationer i ASP .Net eller i vanliga ASP är struktur och effektivitet viktigt. Många kär ASP mer funktions orienterat, de utgör hela berättelsen i en ensa stor funktion. Försök få bort detta tänkande, tänk mer objekt och dess metoder, det kommer att bli mycket enklare att underhålla uppdatera och göra ändringar i koden. Risken för buggar minimeras, koden blir mer läsbar och återanvändbar.
Slutsats
Från en specifikation i detta fall en User Storie har vi nu mappat vår berättelse till en mer eller mindre objekt orienterad kod. Fördelen med att bygga på detta sätt är att vi får en bättre förståelse för hur vårt system är tänkt att fungera, systemet anpassas efter hur användaren vill använda det. Vill vi bygga ut vårt spel kan vi enkelt öka antalet sidor på tärningen, vill vi ha till fler tärningar går det enkelt att instansiera en till och plocka med dess värde. Vi får en mer strukturerad design/arkitektur. Det är väldigt lätt att börja i fel ända då man bygger system, man börjar lätt skriva för mycket teknisk data i sina specar och blandar in analys och design samt till viss del implemenation i specarna. Detta medför oftast i att vi kan glömma viktiga saker och på så vis skapa oreda i vår modell, applikation och kod. Här kan vi i ett tidigt stadie se om vi tänkt fel och enkelt göra ändringar innan vi skrivit själva koden. Detta är ju inte de enda fördelarna och det finns flera olika hjälpmedel för att nå ännu bättre struktur.
Ladda hem artikeln i wordformat:
Diskutera om artiklen här:
Johan Normén
Johan Normén
Denna artikel är inte ett regelverk hur man skall skapa Objekt Orienterad kod, det är heller inte någon regel hur man skall bygga, det hela är en vägledning till dem som vill få fram ett mer strukturerat byggsätt för en analys,design och implementations metod.
Johan Normén
Ni som röstar... Var vänliga och ge en kommentar varför rösten. Så vi skribenter vet vad vi kan göra bättre till nästa gång. Sedan är det kul att även veta varför den röst som sätts. Kanske man kan starta en intressant diskussion? för en röst under max betyder att man inte gillar nått och då är det kul att dela åsikter... de är ändå till för att brytas ;-) mvh Johan
Pelle Johansson
Jag kan bara inte säga annat att det här är väldigt pedagogiskt skrivet och ger dig därför högsta betyg. Stort tack för att du tagit dig tid och förklara denna modell.
Per Hultqvist
Mycket bra grundläggande artikel. Bra att du tagit dig tid att ta fram bilder, utan dessa bilr det svårt att hänga med. Förslag på fortsättningsartikel : Jag skulle gärna vilja veta mer om klassdiagram och domändiagram. Vad betyder alla olika tecken i klassdiagramen t ex plustecken, minustecken (jag kommer ihåg att det har att göra med scope, men exakt?). Varför är det dubbla parenteser efter +Kasta()()? Antar att det betyder något också? Du har visat hur man specificerar ett argument IN till en funktion, hur specificerar man att den är t ex ref (ByRef i VB.Net)? Hur visas ett returvärde från en funktion?
Johan Normén
Här följer lite uppdatering om min text: I klassdiagramet har jag fått ()() efter Kasta, detta är en liten skönhetsmiss från min sida. Jag gjorde diagrammen i Visio och har troligen råkat skriva metodnamnet som Kasta() och Visio lägger automatiskt till () när det är en metod. Sedan var inte syftet att förklara UML syntaxen men jag kan förtydliga lite ang + och - som ni kan se. - betyder privat och + publik. Mvh Johan
Niklas Jansson
Faktiskt förvånansvärt bra! Mycket inom samma ämne har varit av flum-karaktär, men det här var mer konkret. Tyvärr är det inte fördelarna helt uppenbara, jag tror man behöver ett lite större exempel för att verkligen se nyttan. (Sen kan man ju anmärka en del på stavning och grammatik, kanske skulle låtit någon korrekturläsa den...)
Oskar Johansson
En väldigt intressant och bra skriven artikel, gillar den skarpt! Måste dock erkänna att jag inte lusläst den än, får ta mig tid någon gång att göra det :)
Ulf Elfving
Äntligen har det klickat i skallen och börjat förstå principen med grundläggande arbete innan utvecklingsstarten.
Mikael Söderström
En klar femma! Härligt att se en så genomgående artikel. Ser fram emot en fortsättning!
Peter Öhlin
Blir högsta betyg för denna artikel, perfekt genomgång inför morgondagens föreläsning...
Mazen Harake
Anledningen till att jag anser att du får 4 och inte 5 är för att jag inte riktigt köper User stories konceptet. Jag personligen tycker att man ska hålla sig till Use Cases då dom är precis som du säger mer detaljerade och innehåller alternativa flöden. Frågor jag ställer mig är t.ex: Om utvecklaren inte frågar om ett alternativ, hur ska det behandlas då? Use Cases så är meningen att du ska leta efter alternativa vägar + att du smidigare t.ex. kan lägga upp ett cause-and-effekt testcase om du har bra usecases. Sen är det inte alltid uppenbart vilken egenskap som ska ligga i vilken klass. T.ex. säger storyn att värdet skall vara 7, men vilken klass ska värdet tillhöra? Det kanske känns självklart i detta exemplet men det är inte alltid det är det ändå. Jag menar bara att ta ut substantiv, adjektiv och verb... funkar det verkligen? det känns som om man gör det lite enkelt för sig om man litar på att det vore så enkelt? Du har mer arbetserfarenhet än mig, so you tell me :) För övrigt var det en mycket bra artikel som jag inte kunde slita mig ifrån fastän det vart lite sent ;)
Johan Normén
Hej, du har så rätt så. Även i User Stories kan man ta upp alternativa saker via frågor eller stories. Jag gillar Use Case med, beroe på för vem, vilka kram man har etc och hur projektet skall leva i framtiden. I detta fall var user stories helt ok tycker jag. I ett större case kanske use case hade vart bättre val. Även i user Stories tar man upp punkter för test user stories är rätt stor bit lika så use case så jag kunde inte ta med allt i denna lilla artikel utan bara det jag kände var nödvändigt nog för att skapa ett intresse att läsa mer om de olika sakerna. Ang. bara ta ut verb, substantiv etc... så är svaret NIX det räcker inte. Självklart kommer det till fler metoder eller properties. Men de utgör oftast grunden för sitt API och funktionalitet. I stora projekt tillkommer det alltid någor extra som man kanske inte tänkte m.m. Små hjälpmetoder m.m. private metoder m.m. Tack för dina synpunkter. Mvh Johan
Nyligen
Sidor
Statistik
Antal medlemmar:
Antal inlägg:
Online:
På chatten:
27 926
260 266
340 763
0
Kontakta oss
Frågor runt konsultation, rådgivning, uppdrag, rekrytering, annonsering och övriga ärenden. Ring: 0730-88 22 24 | pelle@pellesoft.se