Jaka jest najlepsza praktyka do sprawdzania poprawności parametrów konstruktora?
Załóżmy prosty C #:
public class MyClass
{
public MyClass(string text)
{
if (String.IsNullOrEmpty(text))
throw new ArgumentException("Text cannot be empty");
// continue with normal construction
}
}
Czy można zgodzić się na wyjątek?
Alternatywą, którą napotkałem, była wstępna weryfikacja, przed utworzeniem wystąpienia:
public class CallingClass
{
public MyClass MakeMyClass(string text)
{
if (String.IsNullOrEmpty(text))
{
MessageBox.Show("Text cannot be empty");
return null;
}
else
{
return new MyClass(text);
}
}
}
c#
validation
constructors
MPelletier
źródło
źródło
Odpowiedzi:
Mam tendencję do wykonywania całej mojej weryfikacji w konstruktorze. Jest to konieczne, ponieważ prawie zawsze tworzę niezmienne obiekty. W twoim konkretnym przypadku myślę, że jest to do przyjęcia.
Jeśli używasz platformy .NET 4, możesz to zrobić. Oczywiście zależy to od tego, czy uważasz, że łańcuch zawierający tylko białe znaki jest nieprawidłowy.
źródło
Init
aby otrzymać kod błędu, gdy wyjątek będzie działał równie dobrze i zmusić obiekt do utrzymywania prawidłowego stanu?ArgumentNullException
drugi, i drugi, który sprawdza pusty i wyrzuca anArgumentException
. Pozwala dzwoniącemu być bardziej wybrednym, jeśli chce w obsłudze wyjątków.Wiele osób twierdzi, że konstruktorzy nie powinni wprowadzać wyjątków. Na przykład KyleG na tej stronie właśnie to robi. Szczerze mówiąc, nie mogę wymyślić powodu, dla którego nie.
W C ++ rzucanie wyjątku od konstruktora jest złym pomysłem, ponieważ pozostawia ci przydzieloną pamięć zawierającą niezainicjowany obiekt, do którego nie masz odniesienia (tj. Jest to klasyczny wyciek pamięci). Prawdopodobnie stąd bierze się piętno - grupa oldschoolowych programistów C ++ na wpół rozwinęła naukę języka C # i po prostu zastosowała do tego to, co znali z C ++. Natomiast w Objective-C Apple oddzielił krok alokacji od kroku inicjalizacji, więc konstruktorzy w tym języku mogą zgłaszać wyjątki.
C # nie może wyciec pamięci z nieudanego wywołania konstruktora. Nawet niektóre klasy w środowisku .NET będą zgłaszać wyjątki w swoich konstruktorach.
źródło
Zgłoszenie wyjątku IFF klasy nie można wprowadzić w spójny stan w odniesieniu do jej zastosowania semantycznego. W przeciwnym razie nie. NIGDY nie pozwól, aby obiekt istniał w niespójnym stanie. Obejmuje to niedostarczenie kompletnych konstruktorów (np. Posiadanie pustego konstruktora + inicjalizacja () przed faktycznym ukończeniem budowy obiektu) ... TYLKO POWIEDZ NIE!
W skrócie, wszyscy to robią. Zrobiłem to innego dnia dla bardzo wąsko używanego obiektu w wąskim zakresie. Pewnego dnia, ja lub ktoś inny prawdopodobnie zapłacę cenę za ten kupon w praktyce.
Powinienem zauważyć, że przez „konstruktor” rozumiem rzecz, którą klient wywołuje w celu zbudowania obiektu. Równie dobrze może to być coś innego niż faktyczna konstrukcja o nazwie „Konstruktor”. Na przykład coś takiego w C ++ nie naruszy zasady IMNSHO:
Ponieważ jedynym sposobem na utworzenie a
funky_object
jest wywołaniebuild_funky
zasady, aby nigdy nie dopuszczać do istnienia niepoprawnego obiektu, pozostaje nienaruszony, nawet jeśli faktyczny „Konstruktor” nie zakończy zadania.To jednak dużo dodatkowej pracy dla wątpliwego zysku (może nawet straty). Nadal wolałbym trasę wyjątkową.
źródło
W takim przypadku użyłbym metody fabrycznej. Zasadniczo, ustaw swoją klasę tylko na prywatne konstruktory i metodę fabryczną, która zwraca instancję twojego obiektu. Jeśli początkowe parametry są niepoprawne, po prostu zwróć null i poproś kod wywołujący o podjęcie decyzji.
Nigdy nie rzucaj wyjątków, chyba że warunki są wyjątkowe. Myślę, że w tym przypadku pustą wartość można łatwo przekazać. W takim przypadku użycie tego wzorca pozwoliłoby uniknąć wyjątków i kar za wydajność, które ponoszą, przy jednoczesnym utrzymaniu prawidłowego stanu MyClass.
źródło
Można rozsądnie zakwestionować te wytyczne i powiedzieć: „Ale nie przestrzegam tych zasad, a mój kod działa dobrze!” Na to odpowiedziałbym: „Cóż, to może być prawda, dopóki tak nie będzie”.
źródło
To zależy od tego, co robi MyClass. Jeśli MyClass była w rzeczywistości klasą repozytorium danych, a tekst parametru był łańcuchem połączenia, najlepszą praktyką byłoby zgłoszenie wyjątku ArgumentException. Jeśli jednak MyClass jest klasą StringBuilder (na przykład), możesz po prostu pozostawić ją pustą.
Zależy to zatem od tego, jak istotny jest tekst parametru dla metody - czy obiekt ma sens z pustą lub pustą wartością?
źródło
Preferuję ustawienie domyślne, ale wiem, że Java ma bibliotekę „Apache Commons”, która robi coś takiego, i myślę, że to również całkiem dobry pomysł. Nie widzę problemu z zgłaszaniem wyjątku, jeśli niepoprawna wartość spowodowałaby, że obiekt byłby niezdatny do użytku; ciąg nie jest dobrym przykładem, ale co, jeśli byłby to dla DI biednego człowieka? Nie można było działać, jeśli
null
zamiastICustomerRepository
interfejsu powiedzmy wartość . Powiedziałbym, że w takiej sytuacji zgłoszenie wyjątku jest właściwym sposobem postępowania.źródło
Kiedy pracowałem w c ++, nie używałem do rzucania wyjątków w konstruktorze, ponieważ może to prowadzić do wycieku pamięci, nauczyłem się tego na własnej skórze. Każdy, kto pracuje z c ++, wie, jak trudny i problematyczny może być wyciek pamięci.
Ale jeśli jesteś w języku C # / Java, nie masz tego problemu, ponieważ śmieciarz zbierze pamięć. Jeśli używasz języka C #, myślę, że lepiej jest używać właściwości w przyjemny i spójny sposób, aby upewnić się, że ograniczenia danych są gwarantowane.
źródło
Zgłoszenie wyjątku będzie prawdziwym bólem dla użytkowników twojej klasy. Jeśli sprawdzanie poprawności stanowi problem, generalnie tworzę obiekt jako proces dwuetapowy.
1 - Utwórz instancję
2 - Wywołaj metodę Initialize z wynikiem zwrotu.
LUB
Wywołaj metodę / funkcję, która utworzy dla ciebie klasę, która może przeprowadzić walidację.
LUB
Miej flagę isValid, co mi się nie podoba, ale niektórzy to robią.
źródło
isValid = false
. Konieczność testowania po utworzeniu klasy, jeśli jest ona ważna, jest okropnym, bardzo podatnym na błędy projektem. Powiedziałeś, że masz konstruktora, a następnie zainicjuj wywołanie ... Co jeśli nie zainicjuję wywołania? Co wtedy? A jeśli inicjalizacja otrzyma złe dane, czy mogę teraz zgłosić wyjątek? Twoja klasa nie powinna być trudna w użyciu.