Jak projektować zwracanie rezultatu lub błędów przez metodę?

0

W jaki sposób zwracać status wykonania metody [ OK | błąd + rodzaj błędu ]?

Np. tworzę sobie metodę:

public String sprawdzSmakZupy(...) {

if (...) { 
   return null;

   return "zupa za słona";

   return "zupa za kwaśna";

   return "zupa niedobra";
}

i zależnie od sprawdzanych warunków zwracany jest resultat:
**null **- jeżeli wszystko OK
String jakis - jeżeli jest błąd, a zawartość stanowi opis tego błędu.

Wydaje mi się, że używanie wyjątków nie jest najlepszą rzeczą, bo one nie są do tego.
Myślałem jeszcze, żeby rodzaj błędów zwracanych zadeklarować jako stałe odpowiedniej klasie. W ten sposób było by przynajmniej czytelnie.

Pytanie brzmi w jaki inny sposób można zaprojektować w/w problem? Oczywiście problem nie jest tak prosty jak sam rodzaj gotowanej zupy, a aplikacja zbudowana jest na bazie MVC.

3

Wyjątki są od sytuacji wyjątkowych ;]
W prawidłowo skonfigurowanym środowisku program nie powinien generować czy łapać wyjątków. Natomiast jeśli program źle skonfigurujesz, nastąpi zerwanie połączenia, błąd sterownika, itd rzeczy niezależne od autora programu to wyjątek wręcz powinien polecieć.

Dlatego dużo zależy od tego przed czym chcesz się bronić.

Inna sprawa to np kontrakt funkcji. Np funkcja przyjmuje parametr typu String i w dokumentacji jest wyraźnie zaznaczone, że nie powinien to być null, bo inaczej poleci NullPointerException. W takim przypadku wyjątek jest OK, bo złamaliśmy kontrakt, a mogliśmy programowo sprawdzić czy go dotrzymujemy.

Co do twojego przypadku konkretnie - jakoś nie przypominam sobie by w Javie były funkcje które zwracają typ błędu. Funkcje sprawdzające czy parametry spełniają jakiś kontrakt zwykle zwracają po prostu booleana. Jeśli jest wiele metryk do sprawdzenia to używa się wielu funkcji zwracających booleana i liczy ich koniunkcję.

Jeśli już miałbyś jakiś przypadek usprawiedliwiający sprawdzanie wielu rzeczy w jednej metodzie (hmm, raczej sugerowało by to kiepski design) to na pewno zwracanie Stringów byłoby co najmniej nieporozumieniem. W takim przypadku zwracaj np enuma.

I jeszcze jedno - zwracanie nulla jest czymś czego należy unikać. Bardzo dużo wyjątków które lecą w kodzie produkcyjnym to wyjątki typu NullPointerException. Gdy założymy, że każda funkcja może zwrócić nulla to po każdym wywołaniu takiej funkcji musielibyśmy robić dodatkowego ifa. Kod byłby wtedy masakryczny. Dlatego unikaj zwracania nulla. W twoim przypadku np zwracaj enuma. Enum to prawie że zwykła klasa, więc możesz jej dodać metody - dodaj więc metodę sygnalizująca czy był błąd, np isErrorStatus().

0

Kod został tak zaprojektowany, a nie inaczej i ja na niego wpłwu nie mam. Właśnie co mnie zdziwiło to fakt, że metoda sprawdzająca zwraca null jako stan OK, natomiast String z komunikatem jako coś niepoprawnego. Wewnątrz jednej takiej sprawdzającej metody natknąłem się na fakt kolejnego sprawdzania. Dlatego zacząłem się zastanawiać jak ja bym to zaprojektował. Bo obecnie jak czytam ten kod to po prostu się "zapętlam". Z Enum'em pomysł w sumie jest niezły - czy jest błąd - true|false, a jeśli true to jaki. Na pewno było by to bardziej czytelne i bardziej obiektowe.

1

Może coś takiego?

public class GenericOperationResult<T>
{
   bool Succeed { get; set; }
   T Data { get; set; }
   string AdditionalMessage { get; set; }
   Exception InnerException { get; set; }
}

Oczywiście to tylko przykład, w zależności od tego, co może zwrócić wywoływany serwis.
Jeśli wszystko jest ok, to Succeed jest true, a dane które miała zwrócić operacja są w Data. Jeśli poleci wyjątek, to możemy go zapakować do InnerException, jeśli niepowodzenie objawia się po prostu jakimś komunikatem, to wsadzamy go w AdditionalMessage.
Oczywiście, jeśli jest kilka możliwych stanów zakończenia operacji, to można tak:

public class SpecificOperationResult
{
   SpecificOperationResultStateEnum State { get; set; }
   IEnumerable<SomeClass> Data { get; set; }
   string AdditionalMessage { get; set; }
   Exception InnerException { get; set; }
}

A jeśli np. piszemy funkcję parsującą kolejne wiersze pliku do obiektów, to możemy chcieć tak:

public class FileParsingOperationResult
{
   bool Succeed { get; set; }
   IEnumerable<SomeClass> Data { get; set; }
   IDictionary<int, Exception> InnerExceptions { get; set; }
}

Dzięki czemu wiemy jaki wyjątek poleciał w którym wierszu, i możemy poprawić plik.

Ogólnie wszystko trzeba dopasować do potrzeb, do poziomu w architekturze (czy to wnętrze jakiegoś serwisu aplikacyjnego, czy komunikacja między warstwą biznesową a prezentacji), i do tego, czy chcemy jakiś kod problemu, czysty wyjątek, czy komunikat dla użytkownika.

1

Jeśli te opcje że "zupa była za słona" to jest jakiś rodzaj błędu to wyjątek można rzucić i nie ma w tym nic złego. Ale jeśli to jest metoda która ma weryfikować poprawność czegoś i zwracanie przez nią kodu błędu to jest jej zadanie to wtedy faktycznie jakas lista enumów byłaby bardziej sensowna.
Czyli gdybyś miał metodę zjedzZupe() to ZupaZaSłona mogłoby być wyjątkiem, bo jest to błąd i sytuacja wyjątkowa. Jeśli jednak masz metodę oceńSmakZupy() to wtedy już nie bardzo (ale za to moze być wyjątek BrakZupy ;) )

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