Piszę dużo takiego kodu:
int myFunction(Person* person) {
int personIsValid = !(person==NULL);
if (personIsValid) {
// do some stuff; might be lengthy
int myresult = whatever;
return myResult;
}
else {
return -1;
}
}
Może stać się dość niechlujny, szczególnie jeśli w grę wchodzi wiele kontroli. W takich przypadkach eksperymentowałem z alternatywnymi stylami, takimi jak ten:
int netWorth(Person* person) {
if (Person==NULL) {
return -1;
}
if (!(person->isAlive)) {
return -1;
}
int assets = person->assets;
if (assets==-1) {
return -1;
}
int liabilities = person->liabilities;
if (liabilities==-1) {
return -1;
}
return assets - liabilities;
}
Interesują mnie tutaj komentarze dotyczące stylistycznych wyborów. [Nie przejmuj się zbytnio szczegółami poszczególnych oświadczeń; interesuje mnie ogólny przepływ kontroli.]
coding-style
language-agnostic
William Jockusch
źródło
źródło
Odpowiedzi:
W przypadku tego rodzaju problemów Martin Fowler zaproponował wzór specyfikacji :
Powyżej brzmi to trochę wysoko (przynajmniej dla mnie), ale kiedy wypróbowałem to w moim kodzie, poszło dość gładko i okazało się łatwe do wdrożenia i odczytu.
Moim zdaniem, główną ideą jest „wyodrębnienie” kodu, który wykonuje kontrole w dedykowanych metodach / obiektach.
W twoim
netWorth
przykładzie może to wyglądać następująco:Twoja sprawa wydaje się dość prosta, aby wszystkie kontrole wyglądały OK, aby zmieścić się na prostej liście w ramach jednej metody. Często muszę podzielić się na więcej metod, aby poprawić czytanie.
Zazwyczaj grupuję / wyodrębniam metody związane ze „specyfikacją” w obiekcie dedykowanym, choć bez tego twoja sprawa wygląda dobrze.
To pytanie w Stack Overflow zaleca kilka linków oprócz jednego wspomnianego powyżej: Przykład wzorca specyfikacji . W szczególności odpowiedzi sugerują Dimecastsa „Nauka wzorca specyfikacji” jako przewodnik po przykładzie i wspominają o dokumencie „Specyfikacje” autorstwa Erica Evansa i Martina Fowlera .
źródło
Uważam, że łatwiej jest przenieść sprawdzanie poprawności do własnej funkcji, pomaga to utrzymać zamiar innych funkcji w czystości, więc twój przykład byłby taki.
źródło
if
invalidPerson
?person!=NULL && person->isAlive && person->assets !=-1 && person->liabilities != -1
Zamiast tego po prostu wróć .Jedną z rzeczy, które widziałem szczególnie dobrze, jest wprowadzenie warstwy sprawdzającej do twojego kodu. Najpierw masz metodę, która wykonuje wszystkie niechlujne sprawdzanie poprawności i zwraca błędy (jak
-1
w powyższych przykładach), gdy coś pójdzie nie tak. Po sprawdzeniu poprawności funkcja wywołuje inną funkcję w celu wykonania rzeczywistej pracy. Teraz ta funkcja nie musi wykonywać wszystkich tych kroków sprawdzania poprawności, ponieważ należy je już wykonać. Oznacza to, że funkcja pracy zakłada, że dane wejściowe są prawidłowe. Jak radzić sobie z założeniami? Zapewniasz je w kodzie.Myślę, że dzięki temu kod jest bardzo łatwy do odczytania. Metoda sprawdzania poprawności zawiera cały niechlujny kod do obsługi błędów po stronie użytkownika. Metoda pracy czysto dokumentuje swoje założenia za pomocą stwierdzeń, a następnie nie musi działać z potencjalnie nieprawidłowymi danymi.
Rozważ to refaktoryzacja swojego przykładu:
źródło