1

在 oop 中,我们寻求封装。我们尽量不通过 getter 或公共字段公开内部状态,只公开方法。

到目前为止,一切都很好。

在我们想对多个实体进行操作的情况下,我们引入了Service。但是这项服务如何能够在这些实体上自由运行呢?

如果所有(服务和实体)都在同一个包中,实体可以公开包私有方法或字段,服务可以使用它们,保留封装。但是当实体和服务来自不同的包时呢?似乎实体应该公开公共获取器(贫血模型的第一步和实体逻辑的泄漏),或者执行特定于服务需求的逻辑的公共方法,可能仅由该服务的要求引入 - 似乎也很糟糕。如何解决这个问题?

4

3 回答 3

1

在 OO 的上下文中,您要了解的最重要的事情是对象响应消息,特别是在 OO P中,方法是这些响应的实现方式

例如,假设您有一个Person对象,您(作为程序员)已将责任分配给该对象以响应“增长”消息。通常,您会将其实现为一种Person.grow()方法,就像这样。

class Person {
    int age;

    public void grow() { this.age++; }
}

这似乎相当明显,但您必须注意,从消息发送者的角度来看,Person对象的反应是没有意义的。无论如何,该方法Person.grow()可能会触发导弹发射,这并不重要,因为其他一些对象(或多个对象)可能会以正确的方式响应(例如,在屏幕上更新自身的 UI 组件)。但是,决定当Person对象处理“增长”消息时,它必须增加其年龄属性的值。这是封装。

因此,为了解决您的问题,“执行特定于服务需求的逻辑的公共方法,可能仅由该服务的需求引入 - 看起来也很糟糕”,这一点也不坏,因为您正在设计实体以响应以特定方式来自服务的消息,以匹配您的应用程序的要求。要记住的重要一点是,服务并不决定实体的行为方式,而是实体以自己的方式响应来自服务的请求。

最后,您可能会问自己:实体如何知道他们需要响应某些消息?这很容易回答:决定如何将消息链接到响应。换句话说,您考虑应用程序的要求(各种对象将发送哪些“消息”)以及如何满足它们(如何以及哪些对象将响应消息)。

于 2019-04-28T18:21:48.150 回答
0

我不认为吸气剂的使用是迈向贫血模型的一步。或者至少,就像编程中的一切一样,这取决于。
贫血模型的缺点是每个访问对象的组件都可以在不强制执行其不变量的情况下对其 进行变异(可能会导致数据不一致),使用 setter 方法可以轻松完成。

(我将使用术语命令和查询来指示修改对象状态的方法和只返回数据而不更改任何内容的方法)

拥有聚合/实体的目的是强制执行对象不变量,因此它公开了不反映对象内部结构的“命令”方法,而是“面向领域”(使用“无处不在的语言”)命名),暴露其“域行为”(建议避免 get/set 命名,因为它们是表示对象内部结构的标准命名)。

这与set方法有关,那么get呢?
由于 set 方法可以被视为聚合的“命令”,因此您可以将 getter 视为用于向聚合询问数据的“查询”方法。向聚合询问数据是完全可以的,如果这不会破坏强制执行不变量聚合的责任。这意味着您应该注意查询方法返回的内容。
如果查询方法结果是一个对象,那么,不可变的,拥有它是完全可以的。以这种方式,查询聚合的人得到的回报是只能读取的东西。
所以你可以让查询方法使用对象内部状态进行计算(例如。int missingStudents()LessontotalNumber具有学生和内部状态的实体List<StudentId>),或者像这样的简单方法List<StudentId> presentStudent()只返回其内部状态的列表,但从 a 改变的List<StudentId> getStudents()只是名称)。
因此,如果 get 方法返回不可变的东西,使用它的人不能破坏聚合的不变量。
如果该方法返回一个可变对象,该对象是聚合状态的一部分,那么访问该对象的任何人都可以查询该对象,现在可以在不传递正确命令方法的情况下改变聚合内的某些内容,跳过不变量检查(除非它是通缉和管理)。
另一种可能性是该对象是在查询期间动态创建的,并且不是聚合状态的一部分,因此如果有人访问它,即使它是可变的,聚合也是安全的。最后,如果你是一个 ddd 极端主义者,get 和 set 方法被认为是一件丑陋的事情,但有时它们作为标准命名约定也很有用,并且一些库使用这种命名约定,所以我不认为它们不好,如果他们不破坏聚合/实体的责任。

最后一件事,当你说在我们想对多个实体进行操作的情况下,我们引入了服务。,这是真的,但服务也应该在单个聚合上操作(变异,保存),但这是另一个话题。

于 2019-05-01T09:38:38.733 回答
0

在我们想对多个实体进行操作的情况下,我们会引入服务。

不,我们没有。好吧,我想有些人会这样做,但关键是他们不应该这样做。

在面向对象中,我们对特定的问题域进行建模。我们不(再次,不应该)根据单个对象操作的其他对象的数量进行区分。如果我必须对一个Appointment和一个集合建模,Appointment我不引入一个AppointmentService,我引入一个ScheduleTimetable,或者任何适合该领域的东西。

Entity和的区别Service不符合域。它纯粹是技术性的,通常是回归到程序性思维,其中一个Entity是数据,而一个是Service对其采取行动的程序。

今天实践的 DDD 不是基于 OOP,它只是使用对象语法。一个明确的迹象是,在大多数项目中,实体是直接持久化的,甚至包含数据库 ID 或与数据库相关的注释。

所以要么做 OOP要么做 DDD,你不能同时做这两个。是我的一个关于 OO 和 DDD 的谈话(谈话是德语,但幻灯片是英语)。

于 2019-04-30T11:47:42.463 回答