czwartek, 31 lipca 2008

Podpięcie projektu do Subversion (SNV) - cz. 1

Kontrolę wersji w Visual Studio można zrealizować przy użyciu (niezbyt udanego) M$ produktu - Source Safe. Niestety z tego co wiem, to za Source Safe'a trzeba słono zapłacić oraz nie da się go spiąć z wersją Visual Studio Express. Więc co maja zrobić "biedni" użytkownicy, którzy chcą mieć kontrolę wersji we własnym projekcie? Odpowiedź jest prosta, powinni się zainteresować Open Sourceowym SVN'em. Bez zbędnych przedmów weźmy się za instalację.

Na początek przyjdzie nam ściągnąć pliki:
- http://www.visualsvn.com/server/ - zawiera niemal wszystko czego potrzebujemy ("It includes Subversion, Apache and a management console. ")
- http://tortoisesvn.tigris.org/ - klient do SVN'a

Instalacja.
Najzwyczajniej w świecie instalujemy VisualSvn oraz TortoiseSvn. Nie ma co tu opisywać, stara dobra metoda przy instalacji "dalej, dalej, dalej ... " raczej się sprawdzi ;)

Po instalacji musimy zresetować komputer, gdyż muszą zostać wprowadzone zmiany.

....

Okey, jesteśmy po resecie :)

Klikamy sobie np. na pulpicie prawym klawiszem i pojawiają nam się 2 dodatkowe opcje:


Zatem teoretycznie wszystko jak na razie idzie dobrze. Zapewne serwer SVN działa, więc przejdźmy do utworzenia repozytorium plików. W tym celu uruchamiamy "VisualSVN Server Manager".



Tworzymy nowe repozytorium:


Nadajemy nazwę:


Gotowe, repozytorium TestoweRepo już sobie stoi:


Zajmiemy się teraz wrzuceniem do naszego repozytorium przykładowego projektu.
Przyjmijmy, że wchodzimy do katalogu E:\Projekty\ gdzie znajdują się ważne projekty. Prawy klawisz na katalogu E:\Projekty -> SVN CheckOut pojawia się nam okno:



Urla musimy pobrać z VisualSVN:


Następnie go wklejamy i akceptujemy:


Na katalogu E:\Projekty powinna się pojawiać zielona ikonka



Teraz na wszystkim co się znajduje w E:\Projekty pojawiła się ikonka z pytajnikiem, znaczy to, że plik jest w katalogu zespolonym z SVN, lecz on sam nie jest. Musimy ręcznie go dodać:



Dodajemy do SVN:


Akceptujemy wszystko i pojawia się nam okno:


Odświeżamy nasz katalog. Powinna się na nim pojawić ikonka z plusem, co znaczy, że "już prawie dodaliśmy katalog" ;) do repozytorium:


Musimy potwierdzić przez wybranie Commit'a:


Zgadzamy się na wszystko i po skończonym commitowaniu na katalogu powinna się pojawić znajoma już ikonka:



Ufff :) mamy już to co chcieliśmy w repozytorium. W następnej części zajmiemy się integracją z Visual Studio Express.

niedziela, 27 lipca 2008

Dependency Injection + Autofac + mały przykład

[Wymagania: .NET 3.5]
Załóżmy że mamy zrealizować bardzo proste zadanie: zmienić wszystkim osobom o jakimś wieku ich imię. Więc...

Mapujemy osobę:




class Osoba
{
public int Id { get; set; }
public string Imie { get; set; }
public int Wiek { get; set; }

public override string ToString()
{
return string.Format("{0} {1} {2}", Id, Imie, Wiek);
}
}

Symulujemy działanie Data Access Layer (zwracającego wszystkie osoby)

interface IDal
{
IEnumerable<Osoba> Osoby { get; }
}

class Dal : IDal
{
private IEnumerable<Osoba> osoby = new List<Osoba>()
{
new Osoba() { Id = 1, Imie = "osoba1", Wiek = 23},
new Osoba() { Id = 2, Imie = "osoba2", Wiek = 23},
new Osoba() { Id = 3, Imie = "osoba3", Wiek = 32}
};

public IEnumerable<Osoba> Osoby { get { return osoby; } }
}

Tworzymy Data Access Object (LINQ wyciągający osoby przy użyciu IDal)
interface IDaoOsoby
{
IEnumerable<Osoba> FindByWiek(int wiek);
}

class DaoOsoby : IDaoOsoby
{
private IDal dal;

public DaoOsoby(IDal dal)
{
this.dal = dal;
}

public IEnumerable<Osoba> FindByWiek(int wiek)
{
return from osoba in dal.Osoby
where osoba.Wiek == wiek
select new Osoba() { Id = osoba.Id, Imie = osoba.Imie, Wiek = osoba.Wiek };
}
}


Serwis odpowiedzialny za zmianie imion (główna logika)
interface IServiceOsoby
{
void ZmienImie(int wiek);
}

class ServiceOsoby : IServiceOsoby
{
private IDaoOsoby daoOsoby;

public ServiceOsoby(IDaoOsoby daoOsoby)
{
this.daoOsoby = daoOsoby;
}

public void ZmienImie(int wiek)
{
IEnumerable<Osoba> osoby = daoOsoby.FindByWiek(wiek);

foreach (var osoba in osoby)
{
osoba.Imie = "zmienione imie";
}
}
}

Dzięki temu, że wszystko oparte zostało na interface'ach uzyskaliśmy bardzo elastyczną
i niepowiązaną żadnymi zależnościami strukturę
. Świetnie, o to właśnie nam chodziło :)
Teraz wystarczy określić zachowanie przez "wstrzykniecie" konkretnych klas do konstruktorów.

Jeśli chodzi o testy jednostkowe, to stworzenie ich nie będzie żadnym problemem.
Dzięki użyciu Dependency Injection możemy testować jedną warstwę niezależnie od drugiej.
Tutaj przyjdą nam z pomocą Mock'i, lecz zostawmy sobie to na później :)

Rzućmy okiem w jaki sposób możemy się tym wszystkim posługiwać.


class Program
{
static void Main(string[] args)
{
// "Normalne" użycie
IDal dal = new Dal();
IDaoOsoby daoOsoby = new DaoOsoby(dal);
IServiceOsoby service = new ServiceOsoby(daoOsoby);

service.ZmienImie(23);

// Autofac
ContainerBuilder cb = new ContainerBuilder();

cb.Register<Dal>().As<IDal>().SingletonScoped();
cb.Register(v => new DaoOsoby(v.Resolve<IDal>())).As<IDaoOsoby>();
cb.Register(v => new ServiceOsoby(v.Resolve<IDaoOsoby>())).As<IServiceOsoby>();

Container container = new Container();
cb.Build(container);

container.Resolve<IServiceOsoby>().ZmienImie(23);
}
}




Jest to tylko bardzo "drobny i ogólny" przedsmak tego co będzie działo w kolejnych postach :)

kod źródłowy

środa, 23 lipca 2008

TDD. Przykładowa aplikacja cz. 5 / System płacowy cz. 1

Hmm klient się do nas wciąż nie odzywa. W sumie to i dobrze, bo ciągle robimy sobie wakacje ale dość tego! Czas się zająć refaktoryzacją naszej aplikacji. Podczas trwania urlopu na obozie programistycznym (jasne ;)), dowiedzieliśmy się o istnieniu całkiem ciekawych projektów: Autofac oraz PostSharp. Tak bardzo nas zainteresowały, że postanowiliśmy je wdrożyć do całej aplikacji. Ba! co tam chodźmy za ciosem, przebudujemy całą i DAJMY JEJ W KOŃCU JAKĄŚ nazwę ;)!.. póki klient milczy. Zacznijmy więc od początku.

Podzielmy naszą aplikację na warstwy.

I. Warstwa dostępu do danych. Tutaj stworzymy mechanizm odpowiedzialny za dostęp/komunikację z bazą, zupełnie elastyczny, niezależny od rodzaju bazy.

II. Domain Model (Model dziedziny). Zmapowane tabele bazy danych, DAO, reguły biznesowe.

III. Services (Serwisy). Ta warstwa będzie udostępniać funkcje aplikacji (coś na kształt uporządkowanego API systemu).

IV. Interface Klienta. Czyli po prostu w skrócie... formatki ;)


Cały model będzie tak wyglądać:



Nie noo.. wszystko super, zobaczymy jak to wyjdzie w praktyce.

Strukturę już mamy, wiemy co mamy robić, toteż przyszedł czas na nadanie nazwy dla programu... może po prostu... System Płacowy...? Mało oryginalny :| lecz nasz zlasowany mózg po urlopie na razie nie jest w stanie wymyśleć czegoś chwytliwego ;)

Bierzmy się do roboty.

Ściągamy projekty:
-NAnt
-NHibernate (później nam się przyda)
-NUnit
-NMock
-Ninject
-PostSharp

Na pierwszy rzut idzie I warstwa, dostęp do danych.

cdn.

niedziela, 29 czerwca 2008

Przerwa

Niestety trwa przerwa w pisaniu postów, jest to spowodowane nauką do egzaminu: 70-536, lecz postaram się coś niedługo wrzucić.

czwartek, 1 maja 2008

[brzydkie zapachy kodu] Jeden z Twoich najwiekszych wrogów: DRY! cz. 1

DRY (DON'T REPEAT YOURSELF), czyli: NIE POWTARZAJ SIĘ

Chodzi tu po prostu o powtórzenia kodu. KAŻDE powtórzenie prowadzi do tego, iż cały system coraz trudniej się konserwuje. Wyobraźmy sobie sytuację: mamy metodę, która wylicza składkę ubezpieczenia. Do tego zaraz tworzymy identyczną metodę (w tym samym miejscu lub gdzieś w innej klasie). Musimy pamiętać o tym, że posiadamy dwie takie same metody i jeśli chcemy coś zmienić to musimy to zrobić tu i tu. Przeważnie wystarczy usunąć 1 metodę i korzystać z jednej. To oczywiście najprostszy przypadek. W części drugiej dotyczącej DRY zajmiemy się usuwaniem powielonych części kodu przez stosowanie: metody szablonowej, zastąpieniem algorytmu, przemieszczenie pola w górę etc.

Krótka historyjka wyjaśni nam jak mogą powstawać DRY.

1. Złe zarządzanie projektem
Agnieszka: Kasia, przeglądałam wczoraj Twój kod i natchnęłam się na niemal identyczną klasę, którą w zeszłym miesiącu zrobił Tomek.
Kasia: Co? Ech, gdybym wiedziała, to użyłabym jego klasy a nie traciła czasu na coś co istnieje.

Mogło dojść do tej sytuacji, ze względu:
-na zły podział zadań w projekcie. Widocznie Kasia dostała do zrobienia coś podobnego co już robił Tomek
-nieznajomości struktury aplikacji Kasi (gdyby np. kod był tworzony w parach, to raczej by nie doszło do takiej sytuacji)
-pewność Kasi, że taka klasa nie istniała w systemie i przez to nie zapytała się członków zespołu czy już coś takiego zostało stworzone
-brak (dobrze) rozrysowanej struktury aplikacji, z której można byłoby wyczytać z jakich klas oraz relacji składa się system

2. Głupota (czy jak kto woli brak doświadczenia ;))
Kasia: Tomek, potrzebuje klasy, która służy do obliczania wynagrodzenia na koniec roku, muszę trochę zmienić jej funkcjonalność pod siebie.
Tomek: Okey, tylko nic nie zepsuj. Znajduje się ona w Wyliczenie.Wynagrodzen.
Kasia: Dzięki.
[Kasia bierze całą klasę i kopiuje ją do swojego modułu nad którym pracuje.]

Mogło dojść do tej sytuacji, ze względu:
-brak doświadczenia/zdrowego rozsądku ;)
-obawa przed wprowadzeniem błędu do klasy, co spowodowałoby katastroficzne skutki w całym systemie

To są tylko oczywiście takie dwa przypadki, które powinny Was uczulić na to co robią członkowie zespołu, w którym pracujemy. Sprawdzajmy i pilnujmy się nawzajem ;)

[brzydkie zapachy kodu] Długa metoda

Jak nie trudno się domyśleć zapach - długa metoda, polega na tym, iż metoda jest bardzo ... długa ;) Ale co to oznacza w praktyce? Otóż to, iż np. w takiej metodzie znajdziemy pętle, różnego rodzaju obliczenia, wyświetlenia treści etc. Przykład takiej metody:


class Przyklad
{
public void DlugaMetoda()
{
Console.WriteLine("Naglowek");
Console.WriteLine("---------------------------");

int wynik = 3 * 5;

int a = 0;

for (int i = 0; i < wynik; i++)
{
a += wynik;
}

if (wynik < a)
{
wynik = 123 + 123;
}
else
{
a = wynik;
}


Console.WriteLine("Wyniki:");
Console.WriteLine("a: " + a);
Console.WriteLine("wynik: " + wynik);

Console.WriteLine("---------------------------");
Console.WriteLine("Stopka");
}
}

Rozwiązanie tego problemu jest banalne. Wystarczy stosować "wydzielenie metody". Czyli tą długa metodę dzielimy na mniejsze:


class Przyklad
{
public void DlugaMetoda()
{
DrawNaglowek();

int wynik = ObliczWynik(3, 5);

int a = 0;

a = ZrobCos(wynik, a);

ZrobCosInnego(ref wynik, ref a);

DrawWyniki(wynik, a);

DrawStopka();
}

private static void ZrobCosInnego(ref int wynik, ref int a)
{
if (wynik < a)
{
wynik = 123 + 123;
}
else
{
a = wynik;
}
}

private static int ZrobCos(int wynik, int a)
{
for (int i = 0; i < wynik; i++)
{
a += wynik;
}
return a;
}

private static void DrawStopka()
{
Console.WriteLine("---------------------------");
Console.WriteLine("Stopka");
}

private static void DrawWyniki(int wynik, int a)
{
Console.WriteLine("Wyniki:");
Console.WriteLine("a: " + a);
Console.WriteLine("wynik: " + wynik);
}

private static void DrawNaglowek()
{
Console.WriteLine("Naglowek");
Console.WriteLine("---------------------------");
}

private static int ObliczWynik(int a, int b)
{
return a * b;
}
}

Gdzie mamy zysk?
-wprowadzanie zmian w takiej metodzie jest łatwiejsze
-ciało metody jest CZYTELNIEJSZE
-jesteśmy w stanie objąć całą metodę wzrokiem
-jeśli dobrze nazwiemy te inne metody powstałe przy "wydzieleniu", to nie będziemy musieli zaglądać do ich kodu, gdyż ich nazwa określi nam co robią

[refaktoryzacja] Tworzenie obiektów dla prostych typów danych

Często programiści boją się tworzyć (przynajmniej ja takich spotkałem) małe klasy do obsługi np. kodu pocztowego etc. Niepotrzebnie. Pokażemy to na przykładzie numeru telefonu. Jeśli logika jego obsługi będzie rozmieszczona w różnych klasach, będziemy mieć nie lada problem z zarządzaniem/obsługą/modyfikacją/pielęgnowaniem. Zobrazujmy ten problem:


class Osoba
{
public string NumerTelefonu
{
get
{
// zwraca numer telefonu
}
}
}

class Centrala
{
public string NumerTelefonuBezNumeruKierunkowego
{
get
{
// zwraca numer telefonu bez numeru kierunkowego
}
}
}

class Miejscowosc
{
private Osoba osoba;

public string NumerKierunkowy
{
get
{
// wyciagamy numer kierunkowy z numeru telefonu osoba.NumerTelefonu
}
}
}


Piękny syf w kodzie ;) Ciekawe gdzie teraz byśmy dodali metodę sprawdzającą poprawność numeru telefonu? Żeby uniknąć takich rzeczy wystarczy stworzyć drobną klasę zajmującą się tylko i wyłącznie obsługą tego numeru telefonu. Czyli wszystkie metody dotyczące numeru telefony przemieszczamy do klasy NumerTelefonu:


class NumerTelefonu
{
public NumerTelefonu(string numer)
{
this.numer = numer;
}

private string numer;
public string Numer
{
get
{
// zwraca numer telefonu
}
}

public string NumerTelefonuBezNumeruKierunkowego
{
get
{
// zwraca numer telefonu bez numeru kierunkowego
}
}

public string NumerKierunkowy
{
get
{
// wyciagamy numer kierunkowy z numeru telefonu osoba.NumerTelefonu
}
}

public bool CzyPoprawny
{
get
{
// sprawdzenie czy numer jest poprawny
}
}
}
Teraz mamy wszystko w jednym miejscu. Nie musimy się zastanawiać gdzie umieścić metodę walidującą numer telefonu, oczywiście w ... NumerTelefonu, ale odkrycie ;)