这 3 个 class 之间在(class 图)中的关系是什么?

What is the relation in (class diagrams) between those 3 classes?

我有如下代码:

class Synchronization

  def initialize

  end
  
  def perform
    detect_outdated_documents
    update_documents
  end

  private

  attr_reader :documents


  def detect_outdated_documents
    @documents = DetectOutdatedDocument.new.perform
  end

  def update_documents
    UpdateOutdatedDocument.new(documents).perform
  end

@documents 是来自 DetectOutdatedDocument 中方法的哈希数组 I return。 然后我使用这个哈希数组来初始化 UpdateOutdatedDocument class 和 运行 执行方法。

这样的说法正确吗? 还是我应该使用关联或其他东西?

张贴的代码中没有关系,无论是 UML 还是其他。事实上,乍一看它可能 看起来 就像一个同步有很多 @documents,但变量及其内容从未被定义、初始化,或分配。

如果这是家庭作业,您可能需要问老师 objective 是什么,以及正确答案应该是什么。如果这是一个真实世界的项目,您还没有完成以下操作:

  1. 定义了协作者对象,例如 Document
  2. 以同步可访问的方式初始化 @documents class
  3. 允许您的 class 方法接受任何依赖项注入

如果没有列出至少一项,您的 UML 图与发布的代码不相符。

Ruby 到 UML 映射

我不是 Ruby 专家,但根据您的 syntax 片段,我了解到的是:

  • 有一个Ruby class Synchronization: 那是一个UML class
  • Ruby class 有 4 个方法 initializeperformdetect_outdated_documentsupdate_documents,最后两个是私有的。这些将是 4 个 UML 操作。
  • initialize,因为它是空的,所以你没有在你的 UML class 图中提到它,没关系。
  • Ruby class 有 1 个实例变量 @documents。在 UML 中,这将是一个 属性,或一个关联端的角色。
  • Ruby class 有一个 getter 使用 attr_reader 创建。但由于它在私有部分,它的可见性应该是 - 解释了如何在 UML 中优雅而准确地使用 getters 和 setter(非常感谢@engineersmnky 在 Ruby 中对 getters 的解释,以及纠正了我最初在这方面的误解)
  • 我了解到 SomeClass.new 在 Ruby 中创建了 class SomeClass 的新对象。

Ruby 和 UML 中的动态类型

UML class 图基于明确定义的 types/classes。您通常会仅与已知的 classes 表明关联、聚合和组合,与这些人肯定有稳定的关系。 Ruby 是动态类型的,关于实例变量的所有已知信息都是 Object 类型,Ruby.

中可能的最高泛化

此外,Ruby方法return其执行路径中最新statement/expression的值。如果您不关心对象的 return 值,您只需将其标记为 Object(感谢 engineersmnky 的解释)。

补充说明:

  • UML 中没有 void 类型(另见 SO question)。一个没有 return 任何东西的 UML 操作,只是一个没有指示 return 类型的操作。
  • 还请记住,使用不属于 UML 标准的类型(例如 ArrayHashObject、...)会假设使用特定语言 UML profile.

基于所有这些,并考虑到数组也是一个 Object,您的代码将导致一个非常简单的 UML 图,其中包含 3 个 class,它们都是 Object,以及 SynchronizationObject 之间的一对多关联,角色 @documentsObject 端。

这就是我们所希望的吗?

很笼统的class图,或许可以很好的配合实现。但它可能无法准确代表设计。

您有权在 UML 中独立于实现对设计进行建模。因此,如果实例变量的类型是设计已知的(例如,您希望它是某种类型并通过初始化和 API 设计确保该类型将被强制执行),您可以在你的图表,即使它偏离了代码:

  • 您已经进行了一些手动类型推断来推断 UML 操作的 return 类型。由于所有 Ruby 方法 return 东西,我们期望所有 Ruby 方法至少有一个 Object return 类型。但是你可以不指定任何 return 类型(UML 等同于 void)来表达 return 值并不重要。
  • 您还对实例变量 (UML 属性) 进行了一些类型推断:您阐明它唯一可以取的值是 DetectOutdatedDocument.new.perform 的值 return。
  • 您的图表表明 class 与未指定数量的 DetectOutdatedDocument 个对象相关,我们猜测这是因为 @documents 的可能值。 属性 表示为对象数组。将两者都放在图表上是非常误导的。所以我建议删除 document 属性。相反,更喜欢 DetectOutdatedDocument 一侧的关联端 document 角色。对于非Ruby-本地readers,这将极大地阐明为什么图表上有第二个class。 :-)(我花了一段时间)
  • 现在你不应该使用黑色菱形来构图。因为documents有个publicreader;因此其他对象也可以分配给相同的文档。由于 Ruby 似乎对对象具有 引用语义 ,因此副本将引用相同的对象。这充其量是共享聚合(白色菱形)。由于 UML 没有很好地定义聚合语义,您甚至可以显示一个简单的关联。

最后一点:根据您显示的代码,我们无法确认 UpdateOutdatedDocumentDetectOutdatedDocument 之间存在聚合。如果你确定有这样的关系,你可以保留它。但是,如果它仅基于您向我们展示的片段,请删除聚合关系。您最多只能显示使用依赖性。但通常在 UML 中,如果它是关于方法的主体,您不会显示这种依赖性,因为操作可以以非常不同的方式实现而不必具有这种依赖性。