Czy funkcja add może również przeprowadzać walidację?

0

Załóżmy, że mamy kolekcję, która jest poprawna tylko wtedy gdy jej składowe elementy spełniają pewne wymogi. Wymogiem wobec tych elementów jest warunek by wszystkie jego pola (wartości liczbowe) były większe od zera. Teraz rozpatrzmy funkcję Add. Chcemy do kolekcji dodać nowy element ale chcemy wiedzieć czy element ten jest poprawny, żeby nie "uszkodzić" kolekcji. Czy funkcja Add oprócz dodawania elementu powinna najpierw sprawdzać jego poprawność? Czy to nie jest naruszenie zasady SRP? Klient może nie oczekiwać, że funkcja, która z nazwy zajmuje się dodawaniem będzie jeszcze walidować obiekt.
Oprócz tego potrzebna jest oddzielna funkcja która sprawdza poprawność obiektu: Validate.Gdzie powinna być zaimplementowana ta funkcja? W kolekcji? Ale walidacja obiektów, nie leży w granicach obowiązków kolekcji. Zatem funkcja Validate powinna być zaimplementowana w klasie Element. Dobrze myślę?
Jeżeli funkcja Add w kolekcji będzie tylko dodawać elementy bez ich walidowania, natomiast klasa element będzie zawierać funkcję Validate to wymuszamy by klient za każdym razem najpierw sprawdził element: element.Validate(), a dopiero potem mógł go dodać do kolekcji: list.Add(element). Problemem jest, że za każdym razem trzeba powtórzyć tę czynność, narażamy klienta na to, że po prostu zapomni to zrobić. Czy zatem kolekcja powinna być wyposażona w funkcję ValidateAndAdd(), zamiast Add()?

ValidateAndAdd(Element el)
{
el.Validate();
Add(el); // funkcja prywatna, dzięki czemu zabezpieczamy się, że w kolekcji nie znajdzie się nieprawidłowy obiekt
}

Ok, jeżeli wybiorę podejście ValidateAndAdd, to czy dobrym rozwiązaniem jest rzucanie wyjątku gdy element nie przejdzie walidacji? Czy zamiast tego funkcja powinna zwracać true/false?

ValidateAndAdd(Element el)
{
if (!el.Validate())
throw new ElementValidationException();
Add(el); // funkcja prywatna
}

1

Najprościej to zrób klasę Element, tak żeby nie dało się mieć instancji z ujemnymi polami (np.: walidacja w konstruktorze).
I po kłopocie.

0

W tym konkretnym przypadku jeszcze prościej użyć typu liczbowego bez znaku. Małe szanse, że ktoś obejdzie system typów.
Natomiast w przypadku ogólnym, to po prostu nigdy nie pozwalać na tworzenie zepsutych obiektów, jak to przedmówca wspomniał.

0
somekind napisał(a):

W tym konkretnym przypadku jeszcze prościej użyć typu liczbowego bez znaku. Małe szanse, że ktoś obejdzie system typów.

Ma sens. Ale z tym obejściem systemu typów to różnie. Krzywdę zrobić sobie można łatwo w niektórych językach i konfiguracjach.

        int x = -7;
        uint y = (uint)x;
        Console.WriteLine(y);

Cholerny spadek po C.

0

@jarekr000000: no w C to akurat faktycznie problem, bo to język słabo typowany, ale w nowoczesnych językach ten problem jest rozwiązany. Np. w takim C# służy do tego checked: https://www.dotnetperls.com/checked

0
jarekr000000 napisał(a):

Najprościej to zrób klasę Element, tak żeby nie dało się mieć instancji z ujemnymi polami (np.: walidacja w konstruktorze).
I po kłopocie.

somekind napisał(a):

W tym konkretnym przypadku jeszcze prościej użyć typu liczbowego bez znaku. Małe szanse, że ktoś obejdzie system typów.
Natomiast w przypadku ogólnym, to po prostu nigdy nie pozwalać na tworzenie zepsutych obiektów, jak to przedmówca wspomniał.

Lekko modyfikując oryginalny problem: a jeśli warunkiem poprawności elementów należących do kolekcji będzie dodatnia wartość pól, a nie będzie to warunek poprawności elementów w ogólności? Nie wiem, elementami będą osoby, a będziemy tworzyli kolekcję osób z wprowadzonymi mailami?

1

Trywialne. W takim razie tworzysz klasę PersonWithEmail, którą można utworzyć tylko mając Person, który ma wprowadzone mail (bo w konstruktorze jest walidacja), ew., a może nawet lepiej Person ma metode zwracającą PersonWithEmail jako Maybe/Option. Dostaniesz PersonWithMail tylko jesli mail jest.
I znowu add w kolekcji jest trywialne - działa na PersonWithEmail.

1 użytkowników online, w tym zalogowanych: 0, gości: 1