9

Are there any objective arguments for or against using objects vs unique ID as method/function parameters? (and members of other objects?). Specially in the context of statically typed languages (C#/Java/Scala)

Pros of object itself:

  • More typesafe calls. With IDs there is a risk of wrong ordering of arguments. This can be mitigated though by keeping a 'mini' class for each class that only stores the ID of that class.
  • Get once from persistence, no need to get again
  • With ID, if the id type changes, say int -> long, then would require change across the board with possibility of mistakes.. (courtsey: https://softwareengineering.stackexchange.com/a/284734/145808)

Pros of using ID:

  • Most of the time no need for the actual object, just uniqueid would do, so having ID saves time from getting it from persistence.

A mixture of these techniques, as far as I can see, would only have cons of both and pros of neither.

Given that this is a concretely defined problem, I hope there are objective answers and not- "I think" or "I like" types... :-).

EDIT: Context on the suggestion of a commenter- The only restriction is a statically typed languages. The application is a general purpose application, although happy to get answers based on specific usage scenarios as well.

EDIT: Some more context. Say I have a book management system. My model is:

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. 
}

Why would I want to keep only id and not the full member? Say when I get info about book, I rarely need to know about who has donated or borrowed it. Getting their full information to populate the donatedBy and borrowedBy fields is an overkill.

Ofcourse, these are only my points. I'm sure I'm missing things so wanted to know what are the points in favour/against.

  • Could you edit your question with a bit more context? It's not clear what othe scenario is. I'm going to guess that you're talking about objects which are managed by an ORM system (like Hibernate) and that by "mini-class" you mean a lazy-loading proxy-object. – Darien May 28 '15 at 4:28
  • 2
    Possibly related? programmers.stackexchange.com/questions/284634/… – h.j.k. May 28 '15 at 5:43
  • Adding as comment here (till I can get enough context to expand to a full-featured answer): Unless you are hard-coding ID values in your code (a big no-no), you'll still have to load your ID values from somewhere right? "Most of the time" is also pretty vague IMHO... – h.j.k. May 28 '15 at 6:45
  • @h.j.k. Added some more context, thanks. You do have a point- you still have to load ID values from somewhere. However, its not about not 'touching' DB but about minimizing usage and bandwidth to DB. I can get ids of all those who want to borrow a book, but actually getting their full details would be needless. – 0fnt May 28 '15 at 8:59
  • It seems that would depend on several things: (1) whether the use cases require the actual attributes (columns) or just the ID, which depends on that use cases themselves; (2) whether you use some caching or performance-enhancing techniques that would make the ID-to-attributes query effortless, and whether your use cases would be heavy enough as to break such caching schemes. – rwong May 28 '15 at 9:19
7

Depending on your environment and problem context, the need to go the database can be mitigated, and as a result (IMO) the argument for using id's only is lost.

For example, PHP's Doctrine2 ORM has EntityManager::getReference which produces a lazily loaded proxy to an entity using the provided id; I don't know how many other ORM's do that, but it matches roughly the notion of a "mini-class" you mention. For most intents and purposes, this is a fully hydrated entity, so you can pass it around and use it like any other.

The only case in which it's not is when it's given an invalid id, and in that case you'd want to go to the database anyway to validate; plus, to reduce the cost of getting entities, most ORM's have (first & second level) caching functionality available in some form.

Once we reduce the need and cost of going to database, we're left largely with pro's of using the entity as a parameter:

  1. Type safety.
  2. Less knowledge required by the caller. A developer 6 months down the line can't mistakenly pass in the id of the wrong entity.
  3. Reduced refactoring cost. If a method were to change to require 2 pieces of information from the entity, there's no cost in changing the caller to provide it, presuming the caller got the fully hydrated entity to begin with.
  4. Less validation required per method. Many methods can assume that the entity they're given exists in the database (because it came from the ORM), and so no longer have to validate the id.
  5. (Potentially) fewer database calls. If you're passing id's to 3 methods, and each one has to validate it or fetch more data on the entity, that's 3 times as many database calls as 1 method fetching the entity and 2 operating on it.

Of course, there are cases where one might want to pass an id only: for example when crossing a bounded context boundary (in domain driven design speak). If we consider two bounded contexts with differing notions of what makes up an account (eg finance vs customer relations), then we can't pass context A's account to context B. Instead, an account id (in the language of the domain) can be passed across.

  • Caching wouldn't scale well with multiple instances of application running. I do have my own caching in place. The points are excellent though. – 0fnt May 28 '15 at 11:29
  • I guess one more point against IDs is that using ID implies calling persistence besides validation, for actually getting the object too. – 0fnt May 28 '15 at 12:29
3

For this sort of question, I delegate to whichever solution best communicates my intent as a programmer, and makes the job of "Future Greg" easiest.

Using the object as arguments makes it easier to get the method call right 6 months from now and I've forgotten the gritty details of this application. To build on your "is this book in this person's wishlist example", let's say the isInWishList method really only needs to compare the integer Ids of books and members. In reality, this one method only needs two pieces of data in order to do it's job. Now let's take a step back from this one method, and look at the whole software system. I doubt it only requires a book Id and a member Id in order to operate. You'll be pulling the full book and member from the database anyway before even calling the isInWishList because it is likely you'll need to do something more than just call this one method. Maybe you do, but most times you'll need to do more.

Given that, you aren't realistically going to incur a performance penalty by fetching and mapping both objects in full from the database. Theoretically you'll require fewer database calls, but you'll find in practice that this simply isn't true. If you pass those Ids as parameters in the query string back to the server, then yeah, you'll just need to fetch the wishlist from the database. But this is a very narrow view of your whole application. There will be scenarios when you are performing other operations in addition to checking a wish list --- for instance, adding a book to a wishlist. You don't want to add duplicate items.

Go for the solution that is easiest for a new programmer, or your Future Self to understand, which is to pass the Book and Member objects in. Only after finding an actual performance problem should you really concern yourself with just passing the Ids.

Or, we could remember that statically typed languages like Java offer method overloading, so you can have your cake and eat it too:

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

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

The real answer to this question is to write both methods, call the integer-only version from the strongly typed version, and Bob's your uncle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.