Saturday, 27 December 2008

TDD

Det är verkligen svårt att komma ihåg principerna med Testdriven utveckling (Test Driven Development, TDD), jag får påminna mig själv hela tiden, men det är det absolut värt, som jag skrev i förra bloggposten.

Nu har jag startat med formuläret för att skapa en golfspelare. Layouten är simpel, det enda jag vill kunna spara till databasen, just nu, är namn, hcp, kön, golf-id, e-post och en bild.

Så vad är det första som ska hända när man klickar på spara? Jo, fälten ska valideras. Första fältet ut är namn, och det är här jag märker att jag får påminna mig om att tänka TDD.

Det första jag gjorde var att skapa ett test som heter CanValidateThatUserNameIsEntered enligt nedan.

[Test]
public void CanValidateThatUserNameIsEntered()
{
   string userName = "";
   Assert.That   (Desktop.ViewHandlers.UserViewHandler.IsUserNameValid(userName), Is.EqualTo(false));
}

Detta skapade såklart ett kompileringsfel, eftersom funktionen IsUserNameValid inte finns, så jag skapar den och skriver följande kod.

public static bool IsUserNameValid(string userName)
        {
            if(userName.Trim().Length> 0 && userName.Trim().Length < 101)
                return true;
            return false;
        }

Och det är här jag gör mitt första fel, bland fler. Koden kollar att namnet är längre än 0 (noll) tecken, men min vana trogen kollar den även att den inte är mindre än 101 tecken, dvs. max 100 tecken. (Maxlängd i databasen för namn.) Det är inte rätt, och inte TDD! Enligt TDD ska man koda tillräckligt för att klara testet! Alltså ska if-satsen skrivas som

if(userName.Trim().Length> 0)
return true;

Om jag vill testa att den inte är längre än 100 tecken så skriver jag ett nytt test som kontrollerar ENBART maxlängden, sen kan jag skriva en funktion som anropar båda dessa valideringar. Och vips har jag refaktorerat bort det andra felet! Originalfunktionen, enligt ovan, gör två saker, inte enbart EN sak. En funktion ska utföra EN sak och bara en sak!. Jag skrev om detta tidigare här

Det tredje felet upptäcker jag när jag skriver denna bloggpost. Funktionen heter IsUserNameValid, vilket jag inte tycker är ett bra funktionsnamn. Det luktar property lååååång väg! Funktionen kommer jag att döpa om till ValidateUserName.

Labels: , ,

kick it on DotNetKicks.com

BitmapSource och Image

Ok, ibland tänker jag för mycket… 
Jag använder ju nu ett renodlat WPF gränssnitt. I tidigare versioner jag påbörjade var det Winforms som gällde, och när det handlade om bildhantering så var det System.Drawing.Image objektet som jag använde för att hantera bilder, från databas upp till gränssnittet. Nu är det som sagt WPF som gäller och när man visar en bild i ett WPF-formulär är det istället en BitmapSource som används för att visa bilder. Objektet BitmapSource är en ny klass som använder DirectX för att rendera bilder, beroende på vilken hårdvara som finns i klienten och vilken version av DirectX som är installerad så hanterar denna klass detta automagiskt, och bättre prestanda erhålls för rendering av bilder.

Fantastiskt tänkte jag och började ändra min kod, från DAL till gränssnitt, dvs. jag bytte ut alla System.Drawing.Image objekt mot BitmapSource istället. Det var ju lättare sagt än gjort!
Först och främst, jag är så nöjd att jag kör TDD nu, all kod jag ändrade var täckt av unit tester, så listan över kompileringsfel var lång, och det var bara att kavla upp ärmarna. Ju mer jag jobbade med att implementera BitmapSource objektet för bilder desto mer skeptisk blev jag att det var rätt väg att gå. Jag var ju tvungen att lägga till referenser till PresentationCore.dll i DAL lagret vilket inte kändes helt ok. Jag googlade en hel del, men hittade inga bra källor som beskrev hur man ska hantera bilder från databasen upp till WPF-gränssnittet. Så när jag låg och försökte sova en natt tänkte jag på detta, och då slog det mig. P:et i WPF står för PRESENTATION, och då bestämde jag mig för att klasser som ingår i WPF har med presentationen att göra, inte dataacess, inte heller affärslogik. Så tillbaka till Visual Studio, och ändra tillbaka, återigen otroligt glad att jag har anammat TDD!

För att skicka en bild från databas till gränssnittet så har jag följande funktioner, mer än så behövs inte.

Först och främst, bilden är lagrad i Sql Server 2008 i ett Image fält, vilket sin tur översätts till System.Data.Linq.Binary. För att översätta detta till en Image har jag följande funktion:

public static Image ConvertLinqBinaryToBitmapImage(System.Data.Linq.Binary imageData)
        {
            if(imageData ==null)
                return null;
            using (MemoryStream pictureStream = new MemoryStream(imageData.ToArray()))
            {
                Image imageToReturn =Image.FromStream(pictureStream);
                return imageToReturn;
            }
        }

Och för att konvertera en Image till System.Data.Linq.Binary har jag följande funktion:

public static System.Data.Linq.Binary ConvertImageToLinqBinary(Image picture)
        {
            if(picture==null)
                return null;
            using(MemoryStream pictureStream = new MemoryStream())
            {
                picture.Save(pictureStream, picture.RawFormat);
                return pictureStream.ToArray();
            }
        }

Ovanstående kod används alltså för att konvertera bilden fram och tillbaka till mitt objekt som har en bild som property, när jag vill hämta eller spara till Linq To Sql objektet.

I WPF formuläret behövs ju Image klassen konverteras till en BitmapSource, och det finns det inbyggt stöd för. Följande funktion konverterar en System.Drawing.Image till BitmapSource:

private static BitmapSource CreateBitmapSourceFromImage(Image sourceImage)
        {
            if(sourceImage ==null)
                return null;
            System.Drawing.Bitmap bitmap = new Bitmap(sourceImage);
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                bitmap.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
        }

Labels: , ,

kick it on DotNetKicks.com

Friday, 19 December 2008

Linq To Sql eller Entity Framework

Jaha, en till ändring. När jag började med detta projekt i somras för den n:te gången så beslutade jag mig för att använda Linq To Sql i datalagret. Mycket av mitt beslut grundades på denna artikel. Speciellt som jag vet med säkerhet att system kommer att använda MS Sql Server som databas. Nåväl, idag läser jag följande artikel. Linq To Sql kommer alltså inte att vidareutvecklas och Linq To Entities saknar ju vissa saker som Linq To Sql har, tex. stöd för Stored Procedures. Så nu är ju frågan vad jag ska göra. Jag kommer först och främst läsa igenom denna blogg. Ett av syftena med detta projekt är att lära mig ny teknik, men som jag skrev tidigare har jag satt ner foten till viss del för att någon gång komma i mål. Men jag får väl lyfta foten igen, jag vill ju inte välja ett spår som Microsoft inte ska vidareutveckla. Jag vill inte börja om igen när VS2010 och .Net 4.0 lanseras.

Labels: , , ,

kick it on DotNetKicks.com

Thursday, 18 December 2008

Luktfri kod

För någon vecka sedan fick jag boken Clean Code – A Handbook of Agile Software Craftmanship skriven av Robert C. Martin, eller Uncle Bob som han även kallas. Boken handlar alltså om att skriva ren och fin kod utan en massa code smells.

Efter att ha läst förord och inledning av boken tänkte jag försöka läsa några kapitel per vecka och anamma detta i kodningen av Invitational Tour systemet.  De tre första kapitlen heter Clean Code, Meaningful Names och Functions. Det första kapitlet, Clean Code handlar om bra och dålig kod, och några personer är intervjuade och får berätta om sin syn på saken. Det andra kapitlet redogör för att egentligen behövs ju inga kommentare i koden längre, eftersom nuförtiden kan man döpa variabel namn och funktioner till små uppsatser, i princip. Jag tycker i och för sig att man ska tänka på att inte skriva allt för långa namn, men en bra regel som nämns i boken är att döpa en variabel är som att döpa sitt barn. Nu har inte jag några barn, men jag kan nog förstå den vånda som föräldrar går igenom när dom ska komma på ett namn till sitt barn, och poängen är att variabelnamn verkligen ska tänkas igenom.

Att skriva en for-loop och använda det vanlig i:t som index är faktiskt förkastligt.

for (int i = 0; i < User.Count; i++)

Varför inte byta ut ‘i’ mot en förklarande variabel? Det blir ju så mycket snyggare

for (int userIndex = 0; i < User.Count; userIndex++)

Något annar som beskrivs i kapitel 3, Functions, är att en funktion ska göra en sak, och endast en sak. Detta tänkte jag nu implmentera i systemet, det gör det ju så mycket enklare och unit testa, och koden blir mer hanterbar och överskådlig.

I ett fönster där man ska redigera en golfspelare vill jag sätta texten på fönstret till “Ny spelare - [Spelarnamn]” alternativt “Redigera spelare [Spelarnamn]. Tidigare skrev jag funktionen för det enligt nedan:

   1:  private void setWindowTitle()
   2:          {
   3:              if(CurrentUser.IsNew)
   4:              {
   5:                  this.Title = String.Format("Ny spelare - {0}{1}", CurrentUser.Name, CurrentUser.IsDirty ? "*" : "");
   6:              }
   7:              else
   8:              {
   9:                  this.Title = String.Format("Reigera spelare - {0}{1}", CurrentUser.Name, CurrentUser.IsDirty ? "*" : "");
  10:              }
  11:          }

Denna funktion gör ju faktiskt tre saker!
1) Den avgör om spelaren är ny eller befintlig
2) Om den är  ny sätter den texten för ny spelare
3) Om det en befintlig spelare sätter den texten för en befintlig spelare.

Alltså skapar jag iställer tre funktioner, som var och en utför en av ovanstående listade åtgärder.

   1:  private void setWindowTitle()
   2:          {
   3:              if(CurrentUser.IsNew)
   4:              {
   5:                  setWindowTitleForNewUser();
   6:              }
   7:              else
   8:              {
   9:                  setWindowTitleForUserInEditMode();
  10:              }
  11:          }
  12:          private void setWindowTitleForNewUser()
  13:          {
  14:              this.Title = String.Format("Ny spelare 
- {0}{1}"
, CurrentUser.Name, CurrentUser.IsDirty ? "*" :
"");
  15:          }
  16:          private void setWindowTitleForUserInEditMode()
  17:          {
  18:              this.Title = String.Format
("Redigera spelare - {0}{1}", CurrentUser.Name,
CurrentUser.IsDirty ? "*" : "");
  19:          }

Kapitel fyra och fem handlade om kommentarer och formattering, och där tycker jag sunt förnuft räcker långt, i kombination med vettig namngivning. Ovanstående funktioner behöver väl inga kommentarer? Varje funktion med String.Format kommer att refaktoreras!

Nästa kapitel heter Objects and Data structures, hoppas jag får tid att läsa om det snart!

Labels: ,

kick it on DotNetKicks.com

Wednesday, 17 December 2008

Tillbaka på ruta 3

Det var väldigt länge sen jag skrev något på bloggen. Orsaken till det är att jag har börjat om IGEN! Återigen så säger jag emot mig själv, men det har mycket med att göra att jag inte var riktigt framme i tänket, men nu känner jag att jag är det. Den största orsaken till att jag började om är att jag vill att gränssnittet för windows applikationen ska byggas helt och hållet med WPF. Vidare vill jag driva hela utvecklingsprojektet med TDD. TDD i sig är inget nytt för mig, men jag har inte anammat det i detta projekt, och detta projekt är ytterst lämpat för utföras med TDD, speciellt som utvecklingsarbetet pågår stötvis då det är ett hobbyprojekt, vilket i sin tur innebär att det kan ta lång tid mellan utvecklingsperioderna. Då är det skönt att befintlig kodbas utvecklad med många unit tester och även integrationstester. Vidare har jag uppgraderat databasen till Sql Server 2008 och även installerat Cruise Control.Net på min utvecklingsserver så att automatiska byggen körs varje gång jag checkar in något till source safe. Nu har jag också bestämt mig för vilka delar som ska byggas i projektet, och vad de olika delarna ska göra och också hittat andra användningsområden för systemet. Denna applikation för golfare kommer att bli så häftig! Ovetenskapliga undersökningar som jag har gjort visar att det finns en stor längtan till att få använda ett sådant här system. Systemet kommer att bestå av en windows applikation, några webbplatser, en mobil version samt ett tjänstelager som utvecklas med WCF. Det blir ingen SOA lösning, klienterna kommer att vara tight kopplade till tjänstelagret då dom kommer att dela på entiteter och annat smått och gott. Ett av dom stora problemen jag stöter på när jag utvecklar denna lösning är att det kan gå lång tid mellan gångerna jag sätter mig ned för att utveckla, jag har trots allt ett liv utanför Visual Studio. Det innebär att när jag väl sätter mig ned för att skapa något är uppstartsträckan lång innan jag blir produktiv. Det är lätt hänt att jag endast refaktorerar befintlig kod, eller putsar lite här och där i kodbasen. För att lösa detta har jag skapat en backlog där jag fyller i kraven på systemet enligt "Som [roll] vill jag [aktivitet] för att [skapa värder]". Denna formulering är vanlig när krav skrivs, speciellt i Scrum världen. Den gör också att det är lättare att bryta ner kraven i mindre beståndsdelar, vilket är viktigt för mig eftersom jag inte har så mycket tid till förfogande. När jag påbörjar en userstory vill jag kunna slutföra den inom den tidsperiod jag har på mig, innan det blir en paus på flera dagar i detta projekt. Ett exempel jag använder är "As a user I want to register at the website so that I can activate the application". I detta system betyder det att när användaren ska köra windows applikationen för första gången måte användaren aktivera den med sin e-postadress och sitt lösenord. Alltså, räckte det med att jag skapade en registreringssida på webbplatsen där användaren registrerade sig med just e-post och lösenord, jag behövde inte bry mig om övrig information som användaren kan ange, just nu. Då blev denna userstory hanterbar, och jag kunde slutföra den under en helg.

Labels:

kick it on DotNetKicks.com