我正在开发一个项目,需要将 ETag 作为参数传递到输入端口,特别是像 getDocument(ETag eTag) 这样的方法。但是,我不确定在设计和最佳实践方面将 ETag 传递到输入端口的最佳方法。
我应该将 ETag 作为域对象 (ETag) 还是作为字符串传递?以下是一些注意事项:
语义:将 ETag 作为域对象传递可以在我的代码库中提供更丰富、更语义有意义的 ETag 表示。然而,在某些情况下,将其作为字符串传递可能更简单、更直接。
强类型和安全性:将 ETag 作为域对象传递可以利用强类型,这可以通过确保仅将有效的 ETag 传递给方法来帮助防止错误并提高安全性。另一方面,将其作为字符串传递可能不太容易出错,但缺乏域对象提供的类型安全性。
清晰度和表现力:将 ETag 作为字符串传递可能会更清晰、更具表现力,特别是在使用域对象增加不必要的复杂性或混乱的情况下。
可用性和简单性:为了简单性和易用性,将 ETag 作为字符串传递可能更可取,特别是在处理域对象会增加不必要的开销或者 ETag 验证在该上下文中并不重要的情况下。
考虑到这些因素,我有兴趣听取社区的意见:您建议采用什么方法将 ETag 传递到输入端口,以及在做出此决定时我应该考虑哪些因素?我应该遵循哪些最佳实践或设计原则?
ETag 是一个值对象。实体或聚合怎么样?它们应该存在于应用程序层和域层中吗?
预先感谢您的见解!
您所描述的问题的通用版本(无论是使用通用数据结构还是专用“对象”传递数据)的标签是原始痴迷(来自 Kent Beck 和 Martin Fowler 的《代码中的坏味道》,1999 年)。
每个人都同意,当收益大于成本时,您应该使用“值对象”而不是通用数据结构。同样,每个人都同意,当使用“值对象”的成本超过收益时,您应该使用通用数据结构。
然而,在评估成本和收益时会出现分歧,而且我们没有一个权威机构可以给我们一个正确的答案[tm]。那么:“这取决于”?
IANA HTTP 字段注册表告诉我们,ETag 字段的参考当前是 RFC 9110。该规范告诉我们一些有趣的事情:
这里的要点是,我们可能想要隐藏 ETag 的一些细节(在 因此,我们可能更愿意将其移动到一个单独的库中,而不是将一堆解析/分支标签混合到我们的逻辑中。
verb(opaqueString: String) {
if (ETagLibrary::isWeakValidator(opaqueString))
...
}
现在与更抽象数据类型的方法进行比较
verb(opaqueHandle: ETagLibrary::Handle) {
if (ETagLibrary::isWeakValidator(opaqueHandle))
...
}
这里显然有一个缺点,因为你必须在某个地方举行一些仪式才能获得ETagLibrary::Handle
;但作为补偿,您可以修改数据结构,特别是您可以在该数据结构中保存缓存的答案(相比之下,通用
String
不提供任何方便的位置来缓存“isWeakValidator”答案,或我们可以在解析阶段预先计算的其他值)。您可以采用相同的方法,引入 ETag 域对象作为句柄:
verb(eTag: ETag) {
if (ETagLibrary::isWeakValidator(eTag))
...
}
但是使用域对象方法支持引入seam,它允许您从依赖于答案的代码中隐藏 ETagLibrary 本身:
verb(eTag: ETag) {
if (eTag.isWeakValidator())
...
}
换句话说,我们可以通过修改eTag实现的细节来改变verb
的行为。例如,这可以让我们对 ETag 合约的许多不同实现使用相同的动词方法。 因此,采用值对象路线可以提高内聚性(电子标签的详细信息都集中在一个地方),并且即使合约底层的实现发生变化,也可以提高客户端代码的稳定性。
这些好处可以抵消成本吗?在某些情况下?是的。在大多数情况下?或许。在所有情况下?这似乎根本不可能。