诸如string
或的基元int
在业务领域中没有任何意义。这样做的直接后果是,当期望产品ID时,您可能会错误地使用URL,而在期望价格时,可能会使用数量。
这也是为什么Object徒手健身挑战将原语包装作为其规则之一的原因:
规则3:包装所有原语和字符串
在Java语言中,int是原始的,而不是实际的对象,因此它遵循与对象不同的规则。它与非面向对象的语法一起使用。更重要的是,一个int本身仅仅是一个标量,因此没有任何意义。当方法将int作为参数时,方法名称需要完成所有表达意图的工作。如果相同的方法将“小时”作为参数,则更容易了解发生了什么。
同一文档还说明了另一个好处:
诸如Hour或Money之类的小物件也为我们提供了放置行为的明显场所,否则这些行为本来会在其他类的周围乱扔。
确实,使用原语时,通常很难跟踪与这些类型相关的代码的确切位置,这通常会导致严重的代码重复。如果有Price: Money
类,很自然地可以在内部找到范围检查。相反,如果使用int
(更糟糕的是double
)来存储产品价格,那么谁应该验证范围?产品?回扣?购物车?
最后,文档中未提及的第三个好处是能够相对轻松地更改基础类型。如果今天我ProductId
有short
作为其基本型,后来我需要使用int
,而不是,有机会改变不会跨越整个代码库中的代码。
缺点-相同的论点适用于对象健美操锻炼的每条规则-就是如果很快变得不堪重负而无法为所有事物创建一个类。如果Product
包含ProductPrice
从PositivePrice
哪个继承而从哪个继承又从Price
继承Money
,那么这不是干净的体系结构,而是一个完整的混乱局面,为了找到单个事物,维护者应该每次打开几十个文件。
要考虑的另一点是创建其他类的成本(就代码行而言)。如果包装器是不可变的(通常应该如此),则意味着,如果采用C#,则必须至少在包装器内:
- 财产获取者,
- 它的支持领域
- 将值分配给后备字段的构造函数,
- 自定义
ToString()
,
- XML文档注释(行很多),
- A
Equals
和GetHashCode
覆盖(也很多LOC)。
最终,在相关时:
- 一个DebuggerDisplay属性,
==
和!=
运算符的替代,
- 最终,隐式转换运算符的重载可以无缝地与封装类型进行转换,
- 代码协定(包括不变式,这是一个相当长的方法,具有三个属性),
- 在XML序列化,JSON序列化或将值存储到数据库或从数据库加载值时将使用几个转换器。
一个简单包装的100 LOC使其非常昂贵,这也是为什么您可以完全确定这种包装的长期盈利能力的原因。Thomas Junk解释的范围概念在这里特别重要。编写一百个LOC来代表ProductId
整个代码库中的一个用例,看起来非常有用。为一段代码编写这种大小的类,使一个方法中的三行代码变得更加可疑。
结论:
实际上,在中public string CreateNewThing()
,返回ThingId
类的实例代替string
可能会有所帮助,但是您也可以:
返回Id<string>
类的实例,该实例是通用类型的对象,指示基础类型是字符串。您将获得可读性的好处,而不必维护许多类型。
返回Thing
类的实例。如果用户仅需要ID,则可以使用以下方法轻松完成此操作:
var thing = this.CreateNewThing();
var id = thing.Id;