【问题标题】:What is the relation in (class diagrams) between those 3 classes?这三个类之间的(类图)关系是什么?
【发布时间】:2021-05-18 13:19:20
【问题描述】:

我的代码如下:

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 中的方法返回的哈希数组。 然后我使用这个 Hash 数组来初始化 UpdateOutdatedDocument 类并运行 perform 方法。

这样的事情正确吗? 还是我应该使用关联或其他方式?

【问题讨论】:

  • 您的代码中只有一个类,但您的图表中有三个。这不可能。
  • 除了加载顺序依赖外,这些类之间没有显示任何关系
  • 您能澄清一下您的问题吗?您的代码中只有一个类,因此所提出的问题实际上没有意义。

标签: ruby uml class-diagram


【解决方案1】:

Ruby 到 UML 的映射

我不是 Ruby 专家,但我从你的 sn-p 中了解到它的syntax 是:

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

UML 中的 Ruby 和动态类型

UML 类图基于明确定义的类型/类。通常,您只会指示与肯定存在稳定关系的已知类的关联、聚合和组合。 Ruby 是动态类型的,关于实例变量的所有已知信息就是它的类型为 Object,这是 Ruby 中可能的最高泛化。

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

补充说明:

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

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

这就是我们所希望的吗?

很一般的类图,或许可以很好的匹配实现。但它可能无法准确地代表设计。

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

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

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

【讨论】:

  • 通常当在 Ruby 中记录“无返回类型”时,它会被记录为返回对象(当方法或返回类型未记录时,YARD 默认会这样做)。
  • @qwerty_so 我的回答试图在 Ruby 术语和 UML 术语之间架起一座桥梁。在这方面,我可以同时使用两个术语:使用方法时我指的是 Ruby 方法,当我说操作时我指的是 UML 操作,但因为两者都指的是同一个命名的“事物”……这就是说,关于强类型你是完全正确的,我将在今天晚些时候重新措辞以更准确地表达我的意思。
  • @qwerty_so 确实,术语上的差异是故意的。方法调用已经是一种非常精确的语言特定实现技术。有些语言,如 smalltalk,没有方法,而是使用消息。其他一些使用显式类型参数定义具有普通过程和函数的操作(我认为要记住这是 ada 方式)。有些人有方法,但称它们为函数。 UML 打算独立于实现。我认为该操作是从一些抽象类型理论中借来的。
  • @qwerty_so 我现在改写了关于打字的答案。 (顺便说一句,我没有发现“方法”的歧义使用,但我添加了 Ruby 和 UML 以消除任何可能的混淆)。感谢您的建设性意见。
  • @engineersmnky 再次感谢您的有用解释 :-)
【解决方案2】:

在发布的代码中没有关系,UML 或其他。事实上,乍一看它可能似乎像一个 Synchronization has-many @documents,但变量及其内容从未定义、初始化或分配。

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

  1. 定义了 Document 等协作对象
  2. 以 Synchronization 类可以访问的方式初始化 @documents
  3. 允许您的类方法接受任何依赖注入

如果没有列出的至少一项,您的 UML 图就不能真正适合发布的代码。

【讨论】:

    猜你喜欢
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多