PHP7中缺少:值对象

这是我的第三篇帖子PHP7系列中缺失前一个是关于命名参数.

Value对象没有标识,这意味着如果您有两个具有相同数据的对象,则它们被视为相等(以两个纬度、经度对为例)。一般来说,他们是不可变的除了简单的getter之外,没有其他方法。

这些对象是领域驱动设计以及一种常见的对象类型,即使是在设计良好的不遵循域驱动设计的代码库中也是如此。我当前的项目德国维基媒体大体上遵循“干净的体系结构架构,这意味着每个“用例”或“交互器”都有两个值对象:请求模型和响应模型。这些当然不是唯一的Value对象,当这个相对较小的应用程序完成时,我们可能已经有50多个了。这使得PHP让创建Value Objects变得如此痛苦,这真的很不幸,尽管它当然不会阻止您这样做。

让我们看一个这样的值对象的示例:

如您所见,这是一个非常简单的类。那么我在抱怨什么?实际上有三种不同的情况。

1.初始化很糟糕

如果你读过我在这个系列中的前一篇文章,你可能会看到这篇文章。事实上,我在那篇文章的末尾提到了ValueObjects。为什么它很烂?

这个缺少命名参数强制用户使用非命名参数的位置列表,这不仅不利于可读性,而且容易出错。当然,可以创建一个带有名字和姓氏字段的PersonName值对象,以及某种部分电子邮件消息值对象。不过,这只是部分缓解了问题。

有一些方法可以解决这个问题,尽管它们都不好。一个明显的解决方案,其缺点也同样明显,就是建设者使用流畅接口对于每个值对象。对我来说,增加的混乱足以使程序复杂化,从而取消了删除位置未命名参数列表所带来的好处。

避免位置列表的另一种方法是根本不使用构造函数,而是依赖setter。不幸的是,这确实带来了两个新问题。首先,Value对象在其整个生命周期内都是可变的。虽然有些人可能清楚地知道不应该使用这些setter,但它们的存在表明更改对象没有什么错。不得不依赖于这种特殊的理解或依赖于阅读文档的人肯定是不好的。其次,可以构造一个不完整的对象,即缺少必需字段的对象,并将其传递给系统的其余部分。如果没有进行自动检查,人们最终会错误地执行此操作,并且错误可能非常非本地,因此很难追踪其来源。

不久前,我尝试了一种方法来解决使用setter引入的这两个问题。我创建了一个名字很好的特性由使用Fluent接口格式的setter的Value Objects使用。

trait提供了一个静态newInstance方法,可以按如下方式构造using Value Object:

trait还提供了一些实用函数来检查对象是否已完全初始化,默认情况下,这些函数将假定具有空值的字段未初始化。

最近我尝试了另一种方法,也使用了Value Objects使用的特性:FreezableValue对象。与前面的方法相比,我想在这里更改的一点是,初始化的Value Object的用户不必做任何与通过构造函数调用初始化的更标准的Value对象不同或附加的事情。冻结是一个非常简单的概念。对象一开始是可变的,然后当调用freeze时,修改就停止了。这是通过一个冻结方法,该方法在调用时设置一个标志,该标志在每次调用setter时都会被检查。如果在设置标志时调用setter,则会引发异常。

为了验证构造对象的代码中的初始化是否完成,trait提供了一个资产NoNullFields</span></span>可以与一起调用的方法冻结.(名称资产字段已初始化实际上会更好,因为前者泄漏了实现细节,如果类重写它,结果会不正确。)

与第一种方法相比,第二种trait方法的缺点是每个Value对象都需要调用检查每个setter中冻结标志的方法。这是一件很容易忘记的事情,也是另一个潜在的错误来源。我还没有调查是否可以通过一些反射魔法来消除这种需求。

如果这些方法中的任何一种都能为自己买单,这是很有争议的,而且很明显,它们中没有一种接近于成为好人。

2.重复和混乱

对于值对象的每个部分,都需要一个构造函数参数(或setter)、一个字段和一个getter。这是大量的样板,而类语言构造提供的不需要的灵活性为不一致性创造了足够的空间。我在Value Objected中遇到过很多错误,这些错误是由构造函数中错误字段的赋值或getter中错误字段返回引起的。

“复制是设计良好的系统的主要敌人。”

―罗伯特·C·马丁

(我实际上不同意上述引用的(措辞),并将用“解释和修改的复杂性”取代“重复”。)

3.语言中缺少概念

用代码传达意图很重要。意图不明确会导致程序员在试图理解意图时浪费时间,而由于意图不被理解而导致的错误会浪费时间。当值对象是类,而许多其他事物是类时,可能不清楚给定的类是否打算成为值对象。当项目中有更多初级人员时,这尤其是一个问题。在语言本身中有一个专用的Value Object构造将使意图变得明确。它还迫使有意识和明确的操作将Value对象更改为其他对象,从而消除了代码腐烂的一种途径。

“干净的代码从来不会模糊设计者的意图,而是充满了清晰的抽象和简单的控制线。”

―Grady Booch,面向对象分析的作者

我可以喝

2022年更新

PHP 8.0(菲律宾比索)给我们带来了建造商物业推广8.1比索带来只读属性。结合PHP 7.4中引入的更成熟的类型化属性,实现实体值对象前所未有地容易。没有专门的语法,但仍然非常好:

另请参见

i-will-always-有利值对象

关于的5个想法“PHP7中缺少:值对象”

留下回复

该网站使用Akismet来减少垃圾邮件。了解如何处理您的评论数据.