标识符vs域对象作为方法参数


10

是否有客观的论据支持或反对使用对象vs唯一ID作为方法/函数参数?(以及其他对象的成员?)。特别是在静态类型语言(C#/ Java / Scala)的上下文中

对象本身的优点:

  • 更多类型安全的调用。使用ID时,存在参数错误排序的风险。尽管可以通过为每个仅保留该类ID的类保留一个“ mini”类来缓解这种情况。
  • 从持久中获得一次,无需再次获得
  • 使用ID时,如果id类型发生更改,例如int-> long,则将要求全面更改,并且可能会出错。.(courtsey:https ://softwareengineering.stackexchange.com/a/284734/145808 )

使用ID的优点:

  • 在大多数情况下,不需要唯一的对象,只需要uniqueid即可,因此拥有ID可以节省从持久性获取ID的时间。

据我所知,将这些技术混合使用既有弊又无利。

鉴于这是一个具体定义的问题,我希望有客观的答案,而不是-“我认为”或“我喜欢”类型... :-)。

编辑:评论者建议的上下文-唯一的限制是静态类型的语言。该应用程序是一个通用应用程序,尽管也很高兴能够根据特定的使用场景获得答案。

编辑:更多的上下文。说我有一个图书管理系统。我的模型是:

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

为什么我只想保留id而不保留完整成员?例如,当我获得有关图书的信息时,我几乎不需要知道谁捐赠或借书了。获取他们的完整信息以填充donatedBy和orrowedBy字段是过大的。

当然,这些只是我的观点。我确定我想念东西,所以想知道赞成/反对的要点是什么。


您可以在更多背景下编辑问题吗?目前尚不清楚是什么情况。我您正在谈论的是由ORM系统(例如Hibernate)管理的对象,而“微型类”则表示延迟加载的代理对象。
达里安(Darien)


在此处添加评论(直到我可以得到足够的上下文来扩展为功能全面的答案):除非您在代码中 ID值进行硬编码(很大的禁忌),否则您仍然必须加载ID值从某个地方开始?“大多数时候”也是模糊的恕我直言...
hjk

@hjk添加了更多上下文,谢谢。您确实有一点-您仍然必须从某个地方加载ID值。但是,这不关乎不是“接触”数据库,而是关乎最小化数据库的使用和带宽。我可以得到所有想借书的人的身份证,但实际上不需要他们的全部详细信息。
2015年

似乎这取决于几件事:(1)用例是否需要实际属性(列)还是仅需要ID,这取决于用例本身;(2)您是否使用某些缓存或性能增强技术来使ID归因查询变得轻松,以及您的用例是否足够繁重而无法破坏此类缓存方案。
rwong 2015年

Answers:


8

根据您的环境和问题上下文,可以减轻对数据库的需求,因此(IMO)丢失了仅使用id的参数。

例如,PHP的Doctrine2 ORM EntityManager::getReference使用提供的id生成对实体的延迟加载代理。我不知道还有多少其他的ORM,但是它与您提到的“微型类”的概念大致相符。对于大多数意图和目的而言,这是一个完全水合的实体,因此您可以将其传递并像其他任何对象一样使用它。

唯一没有出现这种情况的情况是,给它一个无效的ID,在这种情况下,您无论如何都要去数据库进行验证。另外,为了降低获取实体的成本,大多数ORM都提供了某种形式的(第一级和第二级)缓存功能。

一旦我们减少了使用数据库的需求和成本,就剩下了很多使用实体作为参数的专业知识:

  1. 类型安全。
  2. 呼叫者所需的知识较少。下线六个月的开发人员不能错误地传入错误实体的ID。
  3. 降低了重构成本。如果要更改方法以从实体中获取2条信息,则假定调用者从一开始就已经完全水化了实体,那么更改呼叫者以提供该信息就没有成本。
  4. 每种方法所需的验证更少。许多方法可以假定给定的实体存在于数据库中(因为它来自ORM),因此不再需要验证id。
  5. (可能)减少数据库调用。如果将id传递给3个方法,并且每个方法都必须对其进行验证或获取实体上的更多数据,则数据库调用的数量是1个获取实体的方法和2个对该实体进行操作的数据库调用的3倍。

当然,在某些情况下,可能只希望传递一个id:例如,当跨越一个有界的上下文边界时(以域驱动设计为例)。如果我们考虑两个构成一个帐户的概念不同的有界上下文(例如,财务与客户关系),则我们无法将上下文A的帐户传递给上下文B。相反,一个帐户ID(使用域的语言)可以被传递。


缓存无法在应用程序的多个实例上很好地扩展。我确实有自己的缓存。这些要点是极好的。
0fnt 2015年

我猜想针对ID的另一点是,使用ID意味着除了验证之外还调用持久性,这实际上也是为了获取对象。
2015年

3

对于此类问题,我委托哪种解决方案都能最好地传达出我作为程序员的意图,并使“ Future Greg”的工作最轻松。

使用对象作为参数可以使从6个月后开始正确调用方法变得更加容易,而我已经忘记了此应用程序的详细内容。为了建立在“这是此人的愿望清单示例中的这本书”的基础上,假设该isInWishList方法实际上只需要比较书和成员的整数ID。实际上,这种方法仅需要两个数据即可完成工作。现在,让我们从这种方法中退后一步,看看整个软件系统。我怀疑它只需要一个书号和一个会员号即可运行。无论如何,您都将在从数据库中取出完整的书籍和成员之前,甚至调用,isInWishList因为您可能需要做的不仅仅是调用此方法。也许可以,但是大多数时候您需要做更多的事情。

鉴于此,您实际上不会通过从数据库中完全获取和映射两个对象而导致性能下降。从理论上讲,您将需要较少的数据库调用,但实际上,您会发现事实并非如此。如果您将这些Id作为查询字符串中的参数传递回服务器,那么是的,您只需要从数据库中获取心愿单即可。但这是整个应用程序的狭窄视图。在某些情况下,除了检查愿望清单之外,您还需要执行其他操作-例如,将书添加到愿望清单。您不想添加重复的项目。

选择最适合新程序员或您的Future Self理解的解决方案,即传递BookMember对象。只有在发现实际的性能问题后,您才真正关心传递ID。

或者,我们可能还记得像Java这样的静态类型语言会提供方法重载,因此您也可以吃蛋糕:

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

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

这个问题的真正答案是编写这两种方法,从强类型版本中调用仅整数版本,而Bob是您的叔叔。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.