Czy tworzenie czegoś w zdobyczu jest złą praktyką kodowania, jeśli nie istnieje?

18

Mam więc usługę internetową, która ma coś w rodzaju miejsca, w getAccountktórym zwróci identyfikator na konto, jeśli go dostanie, w przeciwnym razie zgłasza wyjątek. Klient zawsze będzie chciał założyć konto, jeśli zgłoszony zostanie wyjątek z tymi samymi informacjami, z którymi jest zrobione.

Tworzę wygodną bibliotekę dla klientów, którzy będą obsługiwać wszystkie wywołania usługi internetowej w środku, więc nie będą musieli wiedzieć, jak samodzielnie wykonywać połączenia.

Zastanawiam się w tej bibliotece, gdybym miał utworzyć getAccount(accountName)konto, które dostanie konto, jeśli istnieje, a jeśli nie utworzy go i nie zwróci informacji, czy to źle? Czy powinienem pozostawić klientowi obsługę wyjątków, czy po prostu nazwać coś takiego jak getOrCreateAccount? Czy to ma znaczenie?

Czy złym zwyczajem jest tworzenie czegoś w operacji get?

Mikrofon
źródło
9
Przynajmniej nazwałbym to getOrCreateAccountlub podobnie.
Telastyn
4
Wydaje się poprawny w kontekście leniwej inicjalizacji. Zależy to jednak od tego, czy musisz użyć tego wzorca w bibliotece.
Chris C
1
I jak czasownika acquire, np acquireAccount. Nie ma on żadnego znaczenia w żadnym z głównych protokołów, które napotkałem, i ma nadrzędny pierścień, który mu dobrze pasuje. „Rób wszystko, co musisz zrobić, aby zdobyć dla mnie jeden z nich. Poproś o to, zbuduj, udawaj, ukradnij, nie obchodzi mnie to, po prostu weź mnie lub zgiń próbując”.
Dan Ross
2
„Klient zawsze będzie chciał założyć konto” - wydaje się to bardzo nietypowe. Jeśli nie mogę uzyskać konta, ponieważ użytkownik błędnie wpisał nazwę użytkownika, z pewnością nie chcę utworzyć konta z błędnie wpisaną nazwą.
gnasher729,
Według javabean spec oracle.com/technetwork/articles/javaee/spec-136004.html getSomething() jest przeznaczony dla osób pobierających i setSomething()ustawiających. Imo nic, że robi coś bardziej intelektualnego musi być nazywany coś innego, to znaczy fetchSomething, obtainSomething, computeSomething, lub doSomethingElseitd.
ccpizza

Odpowiedzi:

31

Tak, to ma znaczenie. Moim zdaniem, generalnie złą praktyką jest tworzenie czegoś w procedurze, która nie jest udokumentowana jako posiadająca moc tworzenia. Nazwij procedurę getOrCreate...lub miej osobną create...procedurę, a następnie, jeśli naprawdę chcesz, zrób getOrCreate...pierwsze próby get..., a jeśli to się nie powiedzie, wywoła, create...a następnie zadzwoni get....

Użytkownik biblioteki prawdopodobnie nie spodziewa get...się utworzenia procedury, jeśli operacja get nie powiedzie się. Jeśli nagle dowiedzą się, że ich wywołania testowe w celu get...stworzenia całej tony danych, prawdopodobnie będą raczej zaskoczeni. A jak to sprzątają? Co jeśli piszą kod myślenie, że będą się błąd, jeśli get...nie powiedzie się i chcą obsługiwać że ich sposób?

FrustratedWithFormsDesigner
źródło
Dzięki, create faktycznie zwraca identyfikator, który zwróci również get, więc nie musiałbym robić get... create... get...tylko dwóch pierwszych. Porozmawiam z klientem o tym, czy kiedykolwiek będzie wymagał zdolności, aby po prostu zadzwonić, getnie chcąc tworzyć
Mike
6
@Mike: Nadal uważam, że powinno tak być createw nazwie, żeby mieć 100% jasności na temat tego, co się dzieje.
FrustratedWithFormsDesigner
Tak, zgadzam się, planowałem zrobić coś podobnego do getOrCreate, ponieważ moją pierwotną myślą było po prostu dostać, ale zdałem sobie sprawę, że nie jest od razu jasne, że to także tworzy coś w tle
Mike
1
Jest bardzo późno, ale getOrCreatema pierwszeństwo w popularnym frameworku internetowym: docs.djangoproject.com/en/1.10/ref/models/querysets/…
tex
18

Nie, to nie jest „zła praktyka”. Tak długo, jak ty i inni programiści zgadzacie się, że tak właśnie ma działać, jest w porządku. W końcu to zwróci konto, czego właśnie chcesz. Utworzenie konta „pod maską” nie ma znaczenia dla dzwoniącego.

Grandmaster B.
źródło
10
Jedynym wyjątkiem jest sytuacja, gdy jest to publicznie dostępny interfejs API. Większa społeczność już zgodziła się, że GET jest niezależny i nieczuły; innymi słowy, ta sama akcja jest wykonywana za każdym razem i jest to bezpieczna metoda, która nie zmienia stanu serwera. To jest na Wiki REST . Poza tym, jeśli API istnieje tylko w twoim małym świecie, rób wszystko, co akceptuje grupa.
jmort253
9
Nie zgadzam się, nawet jeśli dla publicznego API jest to w porządku - o ile ma to sens w kontekście tego, co API ma robić. Tworzenie wielu wpisów w interfejsie API nie ma sensu, gdy wystarczy jeden.
GrandmasterB
Dla przypomnienia, jest to biblioteka całkowicie wewnętrzna i mogę po prostu powiedzieć zespołowi, który z niej korzysta, jak to działa
Mike
1
@ jmort253: Usługa jest nullipotentna, jeśli nie ma żadnych skutków ubocznych widocznych dla klienta . Serwer może robić, co chce.
kevin cline
1
@kevincline, myślę, że myślałem o tym w kategoriach, powiedzmy, obciążenia mojej karty kredytowej. Jeśli serwer obciąża moją kartę na podstawie żądania GET, ale nadal daje mi wynik „odrzucenie” za każdym razem, gdy go uruchamiam, wydaje się, że jest to sprzeczne z duchem GET. Powiedziawszy to, rozumiem twój punkt widzenia. Serwer może policzyć liczbę żądań GET i zablokować mnie po 3 zapytaniach, ograniczając tym samym szybkość, ale bez zmiany jakichkolwiek szczegółów mojego konta. Myślę, że to ważne rozróżnienie, gdy mówi się, że stan serwera nie jest zmodyfikowany. Być może powinienem zamiast tego powiedzieć „stan klienta na serwerze”
jmort253
10

Jeśli getAccount()zawsze można zwrócić konto, to z punktu widzenia dzwoniącego konto istnieje i zawsze istniało. Nie trzeba getAccount()niczego „tworzyć”. Konto nie musi być nigdzie przechowywane, dopóki nie będzie inne niż konto domyślne.

Kevin Cline
źródło
+1 Zasadniczo GetOrCreatejest zły semantyczny, ale uzyskanie obiektu, który może „logicznie” istnieć, niezależnie od tego, czy istnieje fizycznie, jest w porządku. Na przykład rzadka tablica zmiennych elementów może nie mieć przydzielonej pamięci dla elementu 1 841 533, ale nadal umożliwia „pobranie” tego elementu przez utworzenie nowego obiektu, przechowanie go i zwrócenie referencji.
supercat
4

Najbardziej sensowne jest stworzenie 3 metod:

getAccount -> Który właśnie pobiera konto.

createAccount -> Tworzy konto.

getAccountAndCreateIfNeeded -> Wybierz własne nazewnictwo;)

Dlaczego separacja: masz prostą metodę uzyskiwania i tworzenia. Jest to jasna metoda, którą można przetestować w obu przypadkach. W przypadku getAccount nie jest wyjątkiem, że nie można znaleźć konta. Więc po prostu zwróć false lub coś w tym rodzaju, jest to oczekiwane.

Następnie możesz użyć tej wartości zwracanej w funkcji zgrupowanej: getAccountAndCreateIfNeeded, która jest teraz również testowalna, powinna zawsze zwracać konto. Cokolwiek poprosisz.

Wszystkie te 3 metody są jasne, dokładnie wiadomo, co robią i co zwracają. Możesz teraz zawierać porozumienia ze swoim zespołem, ale tego rodzaju wyjątki są straszne na dłuższą metę. Po prostu wyjaśnij je, a nie będziesz mieć problemów.

Luc Franken
źródło
Również z Clean Code: „Jeśli twoja metoda nie może zrobić tego, co sugeruje jej nazwa, to wyrzuć wyjątek”. Miałbym blok Try / Catch w 'GetOrCreateIfneeded', który rzuca nieudany GET, a następnie ma „createAccount” w Throw dla dowolnego typu wyjątku powracającego dla nieudanego get.
Graham
Mógłbym zasugerować czwartą metodę: getAccountIfExistsktóra albo założy konto, albo wskaże, że nie istnieje bez utworzenia nowej. Sama getAccountmetoda powinna zakładać, że konto istnieje, i jeśli nie, zgłosić wyjątek.
supercat
2

To zależy od okoliczności.

Na przykład możesz go użyć do leniwego ładowania / tworzenia instancji, odraczania ładowania danych lub tworzenia instancji, dopóki nie będzie to faktycznie potrzebne. Jest to zwykle rozsądne, ponieważ oszczędza zasoby, których możesz nie potrzebować (jeśli klasa / dane nigdy nie są potrzebne, to nigdy nie są ładowane).

Jednak w tym konkretnym przypadku powiedziałbym, że posiadanie metody o nazwie getAccount, która utworzyłaby nowe konto, gdyby nie istniało, nie byłaby dobrą praktyką. Jeśli użytkownik podał pewne dane uwierzytelniające w celu zidentyfikowania określonego konta, a konta tego nie można znaleźć, czy to oznacza, że ​​użytkownik nie jest jeszcze klientem i że należy dla niego utworzyć konto, czy oznacza to, że dane logowania zostały błędnie wpisane a użytkownik musi zostać poproszony o sprawdzenie, czy wpisał to, co zamierzał wprowadzić?

Jeśli masz metodę getAccount, która tworzy nowe konto, jeśli nie można go zidentyfikować, oznacza to, że nie masz wyboru. Jeśli podzielisz tworzenie konta i wyodrębnianie konta na osobne metody, masz większą swobodę w podejmowaniu decyzji, co zrobić, gdy próba założenia konta nie powiedzie się.

GordonM
źródło