Spójrzmy na opcje, w których możemy umieścić kod weryfikacyjny:
- Wewnątrz seterów w Konstruktorze.
- Wewnątrz
build()
metody.
- Wewnątrz konstruowanej encji: zostanie ona wywołana
build()
metodą podczas tworzenia encji.
Opcja 1 pozwala nam wcześniej wykryć problemy, ale mogą wystąpić skomplikowane przypadki, w których możemy zweryfikować dane wejściowe tylko w pełnym kontekście, wykonując co najmniej część sprawdzania poprawności build()
metodą. Zatem wybranie opcji 1 doprowadzi do niespójnego kodu z częścią sprawdzania poprawności w jednym miejscu, a drugą częścią w innym miejscu.
Opcja 2 nie jest znacznie gorsza niż opcja 1, ponieważ zwykle setery w programie budującym są wywoływane tuż przed build()
, szczególnie w płynnych interfejsach. Dlatego w większości przypadków nadal można wykryć problem wystarczająco wcześnie. Jeśli jednak konstruktor nie jest jedynym sposobem na utworzenie obiektu, doprowadzi to do duplikacji kodu sprawdzania poprawności, ponieważ będziesz musiał mieć go wszędzie tam, gdzie tworzysz obiekt. Najbardziej logicznym rozwiązaniem w tym przypadku będzie umieszczenie walidacji tak blisko stworzonego obiektu, jak to możliwe, czyli wewnątrz niego. I to jest opcja 3 .
Z punktu widzenia SOLID, sprawdzanie poprawności w kreatorze również narusza SRP: klasa konstruktora ma już obowiązek agregowania danych w celu skonstruowania obiektu. Sprawdzanie poprawności polega na ustanawianiu umów dotyczących własnego stanu wewnętrznego, nową odpowiedzialnością jest sprawdzanie stanu innego obiektu.
Tak więc, z mojego punktu widzenia, nie tylko lepiej jest zawieść późno z perspektywy projektowania, ale także lepiej zawieść wewnątrz zbudowanej jednostki, niż w samym budowniczym.
UPD: ten komentarz przypomniał mi o jeszcze jednej możliwości, gdy sprawdzanie poprawności wewnątrz konstruktora (opcja 1 lub 2) ma sens. Ma to sens, jeśli budowniczy ma własne kontrakty na obiekty, które tworzy. Załóżmy na przykład, że mamy konstruktora, który konstruuje ciąg o określonej treści, powiedzmy, listę zakresów liczb 1-2,3-4,5-6
. Ten konstruktor może mieć taką metodę addRange(int min, int max)
. Wynikowy ciąg nie wie nic o tych liczbach, ani nie powinien wiedzieć. Sam konstruktor definiuje format ciągu i ograniczenia liczb. Dlatego metoda addRange(int,int)
musi sprawdzić poprawność liczb wejściowych i zgłosić wyjątek, jeśli max jest mniejsze niż min.
To powiedziawszy, ogólną zasadą będzie sprawdzanie poprawności tylko umów określonych przez samego konstruktora.