Jak nazwać metodę, która zarówno wykonuje zadanie, jak i zwraca wartość logiczną jako status?

33

Jeśli istnieje metoda

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

czy raczej powinno się to nazywać IsStuffDone()?

Obie nazwy mogą zostać błędnie zinterpretowane przez użytkownika: Jeśli nazwa jest DoStuff()przyczyną, dla której zwraca wartość logiczną? Jeśli nazwa brzmi, IsStuffDone()nie jest jasne, czy metoda wykonuje zadanie, czy tylko sprawdza jego wynik.

Czy istnieje konwencja w tej sprawie? Lub podejście alternatywne, ponieważ uważa się je za wadliwe? Na przykład w językach, które mają parametry wyjściowe, takie jak C #, boolowska zmienna statusu może być przekazana do metody jako jedna, a typem zwracanej metody będzie void.

EDYCJA: W moim konkretnym problemie obsługa wyjątków nie może być bezpośrednio delegowana do programu wywołującego, ponieważ metoda jest częścią implementacji interfejsu. W związku z tym osoba dzwoniąca nie może być obciążona zajmowaniem się wszystkimi wyjątkami różnych implementacji. Nie zna tych wyjątków. Jednak osoba dzwoniąca może poradzić sobie z niestandardowym wyjątkiem, takim StuffHasNotBeenDoneForSomeReasonExceptionjak zasugerowano w odpowiedzi i komentarzu npinti .

Wygnanie z otchłani
źródło
2
Zależy to od użycia funkcji i wykonywanych czynności. Jeśli to konieczne, aby rzeczy musiały zostać wykonane poprawnie, to uważam, że to podejście jest wadliwe, ponieważ użytkownik funkcji może nie zauważyć flagi boolean, a także brak informacji dostarczonych przez wyjątek.
Benni
7
Przez większość czasu nazywam to „zepsutym”, ponieważ zwracanie booleanzamiast owijania lub przekazywania wyjątku jest prawie zawsze błędne.
maaartinus
2
Ten rodzaj obsługi wyjątków rzadko jest dobrym pomysłem. Złap tylko określone wyjątki, których się spodziewasz, a nie wszystkie. I jeśli to możliwe, unikaj rzucania nim w pierwszej kolejności, eliminując potrzebę łapania go.
CodesInChaos
2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? I aby odpowiedzieć na twoje pytanie dotyczące wyjątków - albo pozwól abonentowi je obsłużyć, albo złapię je, a następnie wyrzucę niestandardowy wyjątek, który oznacza, że ​​„ta metoda nie działa”. Udostępnij i ciesz się.
Bob Jarvis - Przywróć Monikę
5
Do wszystkich, którzy mówią: po prostu wyrzuć (lub przepuść) wyjątek, przyjmujesz nieuzasadnione założenia co do sposobu używania tego kodu. Jednym z możliwych scenariuszy jest problem, który należy rozwiązać, przy użyciu różnych heurystycznych metod rozwiązania, które rozwiązują coraz większe podklasy problemu przy coraz wyższych kosztach; sensowne byłoby napisanie czegoś takiego if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. To samo z wyjątkami (niestandardowymi?) Byłoby bezużytecznie bardziej skomplikowane.
Marc van Leeuwen,

Odpowiedzi:

68

W .NET często masz pary metod, z których jedna może zgłosić wyjątek ( DoStuff), a druga zwraca wartość logiczną, a po pomyślnym wykonaniu rzeczywisty wynik za pomocą parametru out ( TryDoStuff).

(Microsoft nazywa to „Wzorem Try-Parse” , ponieważ być może najbardziej znanym przykładem są TryParsemetody różnych pierwotnych typów).

Jeśli Tryprefiks jest rzadki w twoim języku, prawdopodobnie nie powinieneś go używać.

ne2dmar
źródło
3
+1 W ten sposób widziałem tego rodzaju rzeczy gdzie indziej. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");jest moim zdaniem dość standardowym i intuicyjnym idiomem.
Karl Nicoll,
12
Należy zauważyć, że TryDoStuffzakłada się , że metody zawodzą w sposób czysty i nie powodują żadnych skutków ubocznych, gdy zwracają wartość false.
Trillian
Do Twojej dyspozycji są również Removemetody, na przykład w .NET Framework, które usuwają element ze struktury danych (np. Dictionary) I zwracają a bool. Konsument interfejsu API może zdecydować o jego zużyciu lub zignorowaniu. Błędy są jednak zgłaszane jako wyjątki.
Omer Iqbal,
18

Co jeśli po prostu rzucisz wyjątek na kod wywołujący?

W ten sposób przekazujesz obsługę wyjątków komukolwiek, kto kiedykolwiek używa Twojego kodu. Co jeśli w przyszłości chcesz wykonać następujące czynności:

  • Jeśli nie zostaną zgłoszone żadne wyjątki, podejmij działanie A
  • Jeśli (na przykład) FileNotFoundExceptionzostanie wyrzucony a, wykonaj działanie B
  • Jeśli zostanie zgłoszony inny wyjątek, wykonaj działanie C

Jeśli odrzucisz swój wyjątek, powyższa zmiana po prostu pociągnie za sobą dodanie dodatkowego catchbloku. Jeśli pozostawisz go takim, jakim jest, musisz zmienić metodę i miejsce, z którego ta metoda jest wywoływana, co w zależności od złożoności projektu może znajdować się w wielu lokalizacjach.

npinti
źródło
1
Co się stanie, jeśli wyjątek jest rodzaju, którego nie można przekazać i należy się nim zająć wewnętrznie?
Limbo Exile
2
@LimboExile: Myślę, że nadal możesz sobie z tym poradzić wewnętrznie, a potem może rzucić kolejny wyjątek, może twój własny. Zilustrowałoby to, że miało miejsce coś, co nie powinno się wydarzyć, ale jednocześnie nie ujawniłeś, co naprawdę dzieje się pod maską, i zakładam, że właśnie dlatego chcesz poradzić sobie z wyjątkiem wewnętrznie.
npinti
6
Być może warto pamiętać, że wyjątek powinien dotyczyć wyjątkowego przypadku . Jeśli DoStuffawaria jest powszechna lub normalna , zgłoszenie wyjątku w przypadku awarii byłoby podobne do kontrolowania przepływu programu z wyjątkami, które są złe. Wyjątki mają również nieodłączny koszt wydajności. Jeśli DoStuffniepowodzenie jest rzadkim przypadkiem z powodu błędu, wyjątki są zdecydowanie sposobem, jak sugeruje @npinti.
Karl Nicoll,
1
@KarlNicoll To, czy coś jest „wyjątkowe”, jest całkowicie subiektywne. Głównymi kryteriami powinno być to, czy chcesz, aby program się zawiesił, jeśli nikt nie robi czegoś z błędem, i czy chcesz, aby błąd występował w typie funkcji. Ponadto nie jest faktem, że wyjątki są drogie; zależy to od języka i sposobu ich rzucania. Wyjątki są tanie w Pythonie, a jeśli usuniesz informacje śledzenia stosu, mogą one być również tanie w Javie. Nawet jeśli wystąpił koszt wydajności, przedwczesna optymalizacja martwi się nim, dopóki się nie profilujesz.
Doval
1
@Doval - Zgadzam się, że jest subiektywny, dlatego OP nie powinien całkowicie lekceważyć odpowiedzi npinti, ponieważ może to być najlepszy sposób działania. Chodzi mi o to, że zgłaszanie wyjątków nie zawsze jest najlepszą drogą. Istnieje wiele przypadków, w których awaria nie uzasadnia zgłoszenia wyjątku i potencjalnego zawieszenia aplikacji. Na przykład, jeśli DoStuff()metoda OP zostanie wyczyszczona po tym, jak wewnętrzny kod zgłosi wyjątek, a warunki końcowe metody są nadal poprawne, kod powrotu może być lepszą opcją, ponieważ błąd został rozwiązany.
Karl Nicoll,
7

DoStuff() wystarczy, a zwracana wartość funkcji powinna zostać udokumentowana i nie trzeba jej wymieniać w nazwie funkcji, szukając wielu dostępnych API:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )
amd
źródło
6

W Javie interfejs API kolekcji definiuje metodę add, która zwraca wartość logiczną. Zasadniczo zwraca, czy dodatek zmienił kolekcję. Tak więc dla a Listzwykle zwróci true, ponieważ element został dodany, a dla a Setmoże zwrócić false, jeśli element już tam był, ponieważ Sets dopuszcza każdy unikalny element najwyżej.

To powiedziawszy, jeśli dodasz element, który nie jest dozwolony przez kolekcję, na przykład wartość pustą, dodanie wyrzuci NullPointerExceptionzamiast zwracać fałsz.

Kiedy zastosujesz tę samą logikę do swojej sprawy, dlaczego musisz zwrócić wartość logiczną? Jeśli ma to ukryć wyjątek, nie rób tego. Po prostu rzuć wyjątek. W ten sposób możesz zobaczyć, co poszło nie tak. Jeśli potrzebujesz wartości logicznej, ponieważ mogła nie zostać wykonana z innego powodu niż wyjątek, nazwij metodę DoStuff(), ponieważ reprezentuje to zachowanie. Robi rzeczy.

mrjink
źródło
1

Może się nie powieść z różnych powodów, wyjątek, normalne warunki, więc skorzystam z tego:

boolean doStuff() - returns true when successful

Ważne jest udokumentowanie, co oznacza wartość logiczna.

użytkownik136354
źródło
1

Zwykle mam metody zwracające OperationResult(lub OperationResult<T>) i odpowiednio ustawiające IsSuccesswłaściwość OperationResult. Jeśli „zwykła” metoda jest nieważna, zwróć OperationResult, jeśli „zwykła” metoda zwróci obiekt, to wróć OperationResult<T>i ustaw Itemwłaściwość OperationResultodpowiednio. Sugerowałbym również posiadanie metod OperationResulttakich jak .Failed(string reason)i .Succeeded(T item). OperationResultszybko staje się typowym typem w twoich systemach, a programiści dowiadują się, jak sobie z tym poradzić.

Scott Rickman
źródło
0

To naprawdę zależy od używanego języka. Podobnie jak w przypadku niektórych języków, istnieją konwencje i protokoły, które tak naprawdę nie istnieją w wielu językach lub w ogóle.

Na przykład w języku Objective-C i nazwach metod SDK dla iOS / Mac OS X zwykle idą w kierunku:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Jak widać, istnieje metoda o nazwie viewDidLoad. Wolę używać tego rodzaju nazewnictwa przy każdym tworzeniu własnych aplikacji. Można to wykorzystać do sprawdzenia, czy coś miało się wydarzyć, jak powiedziałeś. Uważam, że zbyt głęboko myślisz o tym, jak nazywasz swoje metody. Większość metod zwraca status, bez względu na to, co robią, na przykład:

W PHP mysql_connect () zwróci false, jeśli połączenie się nie powiedzie. Nie mówi, że tak, ale tak jest, a dokumentacja mówi, że tak, więc nie można go źle interpretować programistom.

Gergy008
źródło
Ach, ale viewDidLoad jest raczej powiadomieniem zwrotnym. Użytkownicy nie nazywają go, aby załadować widok. Jest on raczej wywoływany po załadowaniu widoku. Podobnie: isVisible nie ustawia widoczności, a jedynie zwraca aktualną wartość. Nic nie robi .
lilbyrdie
0

Chciałbym zaproponować użycie prefiksu „Try”. Jest to dobrze znany wzorzec dla sytuacji, w których metoda może się powieść lub nie. Ten wzorzec nazewnictwa był używany przez Microsoft w .NET Framework (na przykład TryParseInt).

Ale może nie chodzi o nazywanie. Chodzi o to, jak projektujesz parametry wejściowe / wyjściowe metody.

Mój pomysł jest taki, że jeśli metoda ma wartość zwracaną, to celem wywołania tej metody powinno być uzyskanie tej wartości. Dlatego należy unikać używania typu zwracanego do wskazywania statusu operacji.

W języku C # wolę użyć parametru out. Używając out zaznaczam parametr jako parametr boczny. Szczególnie, że C # 6 będzie obsługiwał deklarację zmiennych wbudowanych.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.
000
źródło
0

Wybrałbym nazwę na podstawie podstawowej roli metody. Fakt, że ta metoda zwraca wartość logiczną, nie wymaga przedrostka „Is”. Spójrzmy na kod Java, który używa prefiksu „Is” dość szeroko. Spójrz na java.io.File.delete(). Zwraca boolean, ale nie jest nazywane isFileDeleted(). Powodem jest to, że podstawową rolą metody jest usunięcie pliku. Ktoś wywołuje tę metodę z zamiarem usunięcia pliku.

Wartość logiczna służy tylko do informowania o wyniku pracy. Z drugiej strony zobaczmy java.util.Map.isEmpty(). Oczywiście wywołujesz taką metodę, aby sprawdzić, czy kolekcja jest pusta. Nie chcesz robić żadnych rzeczy. Chcesz tylko dowiedzieć się, jaki jest obecny stan.

Więc osobiście zdecydowanie bym się trzymał DoStuff().

To dla mnie bardzo ważna kwestia. Wiele razy, gdy zachowuję starszy kod, napotykam na coś, co osobiście nazywam „metodą kłamstwa”. Nazwa sugeruje działanie A, ale ciało wykonuje działanie B. Najbardziej zadziwiający przykład to metoda gettera zmieniająca dane w bazie danych.

Jacek Prucia
źródło