Identyfikator vs obiekt domeny jako parametr metody

10

Czy istnieją obiektywne argumenty przemawiające za lub przeciw używaniu obiektów vs unikalny identyfikator jako parametry metody / funkcji? (i członkowie innych przedmiotów?). Specjalnie w kontekście języków o typie statycznym (C # / Java / Scala)

Plusy samego obiektu:

  • Więcej bezpiecznych połączeń. W przypadku identyfikatorów istnieje ryzyko niewłaściwego uporządkowania argumentów. Można to jednak złagodzić, utrzymując klasę „mini” dla każdej klasy, która przechowuje tylko identyfikator tej klasy.
  • Zdobądź raz z wytrwałości, nie musisz więcej
  • Z identyfikatorem, jeśli typ identyfikatora zmienia się, powiedzmy int -> long, wówczas wymagałoby to zmiany z możliwością wystąpienia błędów .. (courtsey: https://softwareengineering.stackexchange.com/a/284734/145808 )

Plusy używania identyfikatora:

  • Przez większość czasu nie potrzeba rzeczywistego obiektu, wystarczyłoby tylko uniqueid, więc posiadanie identyfikatora oszczędza czas od uzyskania go z trwałości.

Mieszanka tych technik, o ile widzę, miałaby tylko wady obu i zalety obu.

Biorąc pod uwagę, że jest to konkretnie określony problem, mam nadzieję, że istnieją obiektywne odpowiedzi, a nie typy „myślę” lub „lubię”… :-).

EDYCJA: Kontekst sugestii komentatora - Jedynym ograniczeniem są języki o typie statycznym. Ta aplikacja jest aplikacją ogólnego przeznaczenia, chociaż chętnie otrzymuje odpowiedzi na podstawie konkretnych scenariuszy użytkowania.

EDYCJA: Więcej kontekstu. Powiedz, że mam system zarządzania książkami. Mój model to:

Book: { id: Int, isbn: String, donatedBy: Member, borrowedBy: Member }
Member: {id: Int, name: String}

Table- wishlist: { isbn: String, memberId: Int}
Table- borrows:  { id: Int, memberId: Int}  

Method: 
isInWishList( book: Book, member: Member ) vs isInWishList( bookId: Int,  memberId: Int)

handleBorrowRequest( memberId: Int, book: Book ){ 
   //the login system doesn't actually get the entire member structure, only the member id. I don't need the full member yet. can run a query to verify that the memberId has less than max allowed books for borrowing. 
}

Dlaczego miałbym chcieć zachować tylko identyfikator, a nie pełnego członka? Powiedz, kiedy otrzymuję informacje o książce, rzadko muszę wiedzieć, kto ją podarował lub pożyczył. Uzyskanie pełnych informacji w celu wypełnienia pól darowanych i pożyczonych to przesada.

Oczywiście, to tylko moje punkty. Jestem pewien, że brakuje mi rzeczy, więc chciałem wiedzieć, jakie są punkty za / przeciw.

0fnt
źródło
Czy możesz edytować swoje pytanie w nieco większym kontekście? Nie jest jasne, jaki jest inny scenariusz. Idę do odgadnięcia , że mówimy o obiektach, które są zarządzane przez system ORM (jak Hibernate) i że przez „mini-klasy” masz na myśli leniwy ładowaną proxy obiektu.
Darien
Dodanie tutaj jako komentarza (dopóki nie zdobędę wystarczającej ilości kontekstu, aby rozwinąć się do pełnej odpowiedzi): O ile nie kodujesz wartości identyfikatora w swoim kodzie (duża nie-nie), nadal będziesz musiał załadować wartości identyfikatora skądś, prawda? „
Przez
@hjk Dodano więcej kontekstu, dzięki. Masz rację - nadal musisz gdzieś ładować wartości ID. Nie chodzi jednak o to, by nie „dotykać” DB, ale o minimalizowanie użycia i przepustowości do DB. Mogę dostać identyfikatory wszystkich, którzy chcą pożyczyć książkę, ale w rzeczywistości uzyskanie ich pełnych danych byłoby niepotrzebne.
0nt
Wydaje się, że zależałoby to od kilku rzeczy: (1) czy przypadki użycia wymagają rzeczywistych atrybutów (kolumn) czy tylko identyfikatora, który zależy od samych przypadków użycia; (2) czy używasz pewnych technik buforowania lub zwiększających wydajność, które sprawiłyby, że zapytanie ID-atrybuty byłoby łatwe, i czy twoje przypadki użycia byłyby na tyle ciężkie, aby przerwać takie schematy buforowania.
rwong

Odpowiedzi:

8

W zależności od środowiska i kontekstu problemu, potrzeba przejścia do bazy danych może zostać złagodzona, w wyniku czego (IMO) argument za używaniem tylko identyfikatora zostaje utracony.

Na przykład PHP Doctrine2 ORM, EntityManager::getReferencektóry tworzy leniwie załadowane proxy do encji przy użyciu podanego identyfikatora; Nie wiem, ilu innych ORM to robi, ale w przybliżeniu pasuje to do pojęcia „mini-klasy”, o której wspominasz. Dla większości celów i celów jest to w pełni uwodniona istota, więc możesz ją przekazywać i używać jak jakikolwiek inny.

Jedyny przypadek, w którym tak się nie dzieje, to podanie niepoprawnego identyfikatora, w którym to przypadku i tak chcesz przejść do bazy danych, aby sprawdzić; ponadto, aby obniżyć koszty uzyskania encji, większość ORM ma funkcję buforowania (pierwszego i drugiego poziomu) w jakiejś formie.

Gdy zmniejszymy potrzebę i koszt przejścia do bazy danych, w dużej mierze jesteśmy za tym, aby użyć encji jako parametru:

  1. Wpisz bezpieczeństwo.
  2. Dzwoniąca wymaga mniej wiedzy. Deweloper po 6 miesiącach nie może omyłkowo podać identyfikatora niewłaściwej jednostki.
  3. Niższe koszty refaktoryzacji. Jeśli metoda miałaby się zmienić tak, aby wymagała 2 informacji od jednostki, zmiana dzwoniącego w celu jej dostarczenia nie wiąże się z żadnymi kosztami, zakładając, że osoba dzwoniąca otrzymała w pełni uwodniony byt.
  4. Wymagana mniejsza walidacja dla każdej metody. Wiele metod może zakładać, że podana jednostka istnieje w bazie danych (ponieważ pochodzi z ORM), a zatem nie musi już sprawdzać poprawności identyfikatora.
  5. (Potencjalnie) mniej wywołań bazy danych. Jeśli przekazujesz identyfikator do 3 metod, a każda z nich musi je zweryfikować lub pobrać więcej danych o encji, to 3 razy więcej wywołań bazy danych niż 1 metoda pobierania encji i 2 operujące na niej.

Oczywiście zdarzają się przypadki, w których można chcieć przekazać tylko identyfikator: na przykład podczas przekraczania ograniczonej granicy kontekstu (w projektowaniu sterowanym domeną). Jeśli weźmiemy pod uwagę dwa ograniczone konteksty z różnymi wyobrażeniami o tym, co składa się na konto (np. Finanse vs. relacje z klientami), nie możemy przekazać konta kontekstu A do kontekstu B. Zamiast tego identyfikator konta (w języku domeny) może być przekazywanym.

Andy Hunt
źródło
Buforowanie nie skalowałoby się dobrze przy wielu wystąpieniach aplikacji. Mam swoje własne buforowanie. Punkty są jednak doskonałe.
0nt
Myślę, że jeszcze jeden argument przeciwko identyfikatorom polega na tym, że używanie identyfikatora oznacza wywoływanie trwałości oprócz sprawdzania poprawności, aby faktycznie uzyskać obiekt.
0nt
3

W przypadku tego rodzaju pytań deleguję do dowolnego rozwiązania, które najlepiej komunikuje moją intencję programisty i sprawia, że ​​praca w „Future Greg” jest najłatwiejsza.

Używanie obiektu jako argumentu ułatwia prawidłowe wywołanie metody za 6 miesięcy i zapomniałem o drobnych szczegółach tej aplikacji. Opierając się na „jest to książka na przykładzie listy życzeń tej osoby”, powiedzmy, że isInWishListmetoda naprawdę wymaga jedynie porównania liczb całkowitych książek i członków. W rzeczywistości ta jedna metoda wymaga tylko dwóch fragmentów danych, aby wykonać swoje zadanie. Cofnijmy się teraz od tej jednej metody i spójrzmy na cały system oprogramowania. Wątpię, aby do działania potrzebował tylko identyfikatora książki i identyfikatora członka. W każdym razie będziesz wyciągać pełną książkę i członka z bazy danych, zanim nawet zadzwonisz, isInWishListponieważ prawdopodobnie będziesz musiał zrobić coś więcej niż tylko wywołać tę jedną metodę. Może i tak, ale w większości przypadków musisz zrobić więcej.

Biorąc to pod uwagę, realistycznie nie poniesiesz kary za wydajność, pobierając i mapując oba obiekty w całości z bazy danych. Teoretycznie będziesz potrzebować mniej wywołań bazy danych, ale w praktyce przekonasz się, że to po prostu nieprawda. Jeśli przekażesz te identyfikatory jako parametry w ciągu zapytania z powrotem na serwer, to tak, wystarczy pobrać listę życzeń z bazy danych. Jest to jednak bardzo wąski widok całej aplikacji. Będą scenariusze, gdy wykonujesz inne operacje oprócz sprawdzania listy życzeń --- na przykład dodając książkę do listy życzeń. Nie chcesz dodawać duplikatów.

Wybierz rozwiązanie, które jest najłatwiejsze dla nowego programisty lub Twojego Future Self do zrozumienia, a mianowicie przekazywanie obiektów Booki Member. Dopiero po znalezieniu rzeczywistego problemu z wydajnością powinieneś naprawdę zająć się samym przekazywaniem ID.

Możemy też pamiętać, że statycznie pisane języki, takie jak Java, oferują przeciążanie metod, więc możesz mieć swoje ciasto i je zjeść:

public boolean isInWishList(int bookId, int memberId) {
    // ...
}

public boolean isInWishList(Book book, Member member) {
    return isInWishList(book.getId(), member.getId());
}

Prawdziwą odpowiedzią na to pytanie jest napisanie obu metod, wywołanie wersji z liczbą całkowitą z mocno wpisanej wersji, a Bob jest twoim wujem.

Greg Burghardt
źródło