2013-04-26 5 views
64

Załóżmy, że istnieje operacja, która tworzy użytkownika. Ta operacja może zakończyć się niepowodzeniem, jeśli istnieje określony adres e-mail lub nazwa użytkownika. Jeśli się nie udało, trzeba dokładnie wiedzieć, dlaczego. Są trzy podejścia do tego, jak to widzę i zastanawiam się, czy istnieje wyraźny zwycięzca.Który projekt jest najkorzystniejszy: testuj, twórz, tworzyj, przechwytuj?

Więc oto użytkownik klasa:

class User 
{ 
    public string Email { get; set; } 
    public string UserName { get; set; } 
} 

I są 3 sposoby realizacji operacji tworzenia:

Test-Tworzenie

if (UserExists(user)) act on user exists error; 
if (UsernameExists(user)) act on username exists error; 
CreateUser(user); 

Userexists i UsernameExists sprawiają żądanie db serwer do sprawdzania poprawności. Wywołania te powtarza się ponownie w CreateUser, aby zapewnić prawidłowe korzystanie z interfejsu API. W przypadku niepowodzenia sprawdzania poprawności generuję ArgumentOutOfRangeException w obu przypadkach. Jest więc hit wydajnościowy.

Try-Tworzenie

enum CreateUserResultCode 
{ 
    Success, 
    UserAlreadyExists, 
    UsernameAlreadyExists 
} 

if (!TryCreate(user, out resultCode)) 
{ 
    switch(resultCode) 
    { 
     case UserAlreadyExists: act on user exists error; 
     case UsernameAlreadyExists: act on username exists error; 
    } 
} 

Ten wzór robi walidacji tylko raz, ale uciekać się do korzystania z tzw kody błędów, które nie są uważane za dobrą praktykę.

Create-Złap

try 
{ 
    CreateUser(user); 
} 
catch(UserExistsException) 
{ 
    act on user exists error; 
} 
catch(UsernameExistsException) 
{ 
    act on username exists error; 
} 

nie używam tu kody błędów, ale teraz trzeba utworzyć oddzielną klasę wyjątku dla każdego przypadku. Mniej więcej w tym sensie należy używać wyjątków, ale zastanawiam się, czy warto stworzyć osobny wyjątek zamiast wpisywania.

Czy mamy zatem wyraźnego zwycięzcę, czy raczej kwestię gustu?

+9

Możesz być zainteresowany postami na blogu Raymonda Chena ["Czystsze, bardziej eleganckie i złe"] (http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx) i ["Czystsze, bardziej eleganckie i trudniejsze do rozpoznania"] (http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx), w którym twierdzi, że kody błędów są lepsze niż wyjątki. – ruakh

+0

Jakieś inne odniesienia zewnętrzne? Jestem bardzo zainteresowany. – QED

Odpowiedz

80

Czy mamy zatem wyraźnego zwycięzcę, czy raczej kwestię gustu?

Pierwsza opcja ma podstawową wadę - to nigdy nie będzie bezpieczny wątku lub bezpieczne, jeśli CreateUser polega na zasobach zewnętrznych i inne implementacje mogą tworzyć się między testami. Ogólnie rzecz biorąc, staram się unikać tego "wzoru" z tego powodu.

Podobnie jak w przypadku pozostałych dwóch opcji - tak naprawdę sprowadza się do tego, czy wystąpi awaria: ,. Jeśli oczekuje się, że CreateUser zawodzi w nieco normalny sposób, wzór Try * jest moją preferencją, ponieważ używanie wyjątków zasadniczo powoduje używanie wyjątków do sterowania przepływem.

Jeśli awaria byłaby naprawdę wyjątkowa, wówczas wyjątki byłyby bardziej zrozumiałe.

+13

+1 dla problemów z bezpieczeństwem wątków – ken2k

+0

@Reed Coraz bardziej rozważam podejście Try *, ale jestem zniechęcony przez wszystkie książki i artykuły, które twierdzą, że nie powinienem używać kodów błędów do obsługi błędów (np. Rzucanie] (http://msdn.microsoft.com/en-us/library/ms229030 (v = vs.100) .aspx)). Podkreślają, że jednym z największych problemów jest przekazywanie kodów błędów do stosu wywołań. Czy masz jakieś myśli? – andriys

+2

@andriys To zależy od tego, czy należy przekazać stos wywołań. Jeśli tego potrzebujesz, wyjątki mogą być lepsze - ale jeśli tak naprawdę jest to tylko przepływ kontrolny (jak przykładowy kod), uzyskanie pojedynczego wyniku jest często prostsze i czystsze. Zdecydowanie unikam osobnych kontroli, chociaż ... –

37

Test-Create może powodować warunki wyścigu, więc nie jest to świetny pomysł. Prawdopodobnie wykonuje też dodatkową pracę.

Funkcja Try-Create jest dobra, jeśli spodziewasz się, że błędy będą częścią normalnego przepływu kodu (na przykład w przypadku wprowadzania danych przez użytkownika).

Create-Catch jest dobry, jeśli błędy są naprawdę wyjątkowe (więc nie martwisz się o wydajność).

+3

+1 dla problemów z bezpieczeństwem wątków – ken2k

+2

Chciałbym zwrócić uwagę, że w wielu przypadkach, takich jak trafienie w bazę danych, koszt wyjątków nie jest ogromną transakcją. Jednak koncepcyjny koszt postępowania z tymi wyjątkami jest. Jeśli ktoś poszedłby do duplikowania logiki "Create-Catch" i zapomniał części 'Catch', zdarzałyby się dziwne rzeczy, ale w przypadku' Try-Create' wartość zwracana czyni to bardziej oczywistym, a awaria milczy. Plusy i minusy do wszystkiego oczywiście. – Guvante

9

Jest to nieco subiektywne, ale warto wskazać na kilka konkretnych plusów i minusów.

Jedną z wad podejścia do tworzenia testu jest stan wyścigu. Jeśli dwóch klientów spróbuje utworzyć tego samego użytkownika mniej więcej w tym samym czasie, możliwe, że obydwoje przejdą testy, a następnie spróbują utworzyć tego samego użytkownika.

Pomiędzy Try-Create i Create-Catch, wolę Create-Catch, ale to osobisty gust. Można argumentować, że Create-Catch używa wyjątków do sterowania przepływem, które zwykle są mile widziane. Z drugiej strony, Try-Create wymaga nieco kłopotliwego parametru, który można łatwo przeoczyć.

Więc wolę Create-Catch, ale zdecydowanie jest miejsce na debatę.

+0

Ja również wolę Create-Catch w tej sytuacji. Naruszenie klucza głównego jest z pewnością ważnym wyjątkiem. W przypadku nieprawidłowego wprowadzania danych przez użytkownika byłoby to trochę bardziej niejednoznaczne, ale prawdopodobnie nadal zdecydowałbym się rzucić wyjątek FormatException. – JosephHirn

4

Podano, że oba UserExists i UsernameExists wykonują połączenia DB. Zakładam, że CreateUser również wywołuje bazę danych.

Dlaczego nie pozwolić, aby baza danych obsługiwała twoje problemy z wątkami? Zakładając, że na twoich tabelach są ustawione odpowiednie ograniczenia, możesz pozwolić, by to się skończyło w ramach zapisanego wywołania proc.

Dlatego oddam swój głos na Create-Catch.