在类中定义结构的 UML 类图

UML Class Diagram with Struct Defined Inside Class

提问人:spaceKelan 提问时间:9/8/2023 最后编辑:spaceKelan 更新时间:9/12/2023 访问量:217

问:

对于包含仅在类生命周期内存在的结构的类的情况,UML 图会是什么样子?

免责声明:我看到有类似的问题,但我的不同之处在于,我使用的结构我在定义如此处所述。我看到的关于如何在 UML 中使用结构的例子总是引用该类的特定结构。 请先看下面的 4 个问题。

示例代码

Parent.h

class Parent{
    private:
        uint8_t age;

        struct{
            uint16_t stepsADay;
            uint8_t levelsADay;
        }healthInfo;

    public:
        int setAge(uint8t_t a_age);
        uint8_t getAge();

        int setHealthInfo(uint16_t a_stepsADay,uint8_t a_levelsADay);
        struct healthInfo getHealthInfo();
};

Parent.cpp

   // omitting (de-)/constructor for readability
   int Parent::setAge(uint8t_t a_age){
       this->age = a_age;
       return 0;
   }
   uint8_t Parent::getAge(){
       return this->age;
   }

   int Parent::setHealthInfo(uint16_t a_stepsADay,uint8_t levelsADay){
       this->healthInfo.stepsADay = a_stepsADay;
       this->healthInfo.levelsADay= a_levelsADay;
       return 0;
   }
   struct healthInfo Parent::getHealthInfo(){
       return this->healthInfo;
   }

main.cpp

#include "Parent.h"

uint32_t createMessage(Parent *thatParent){
   healthInfo tmpHealthData=thatParent.getHealthInfo();

 // create message to give to doctor
  uint32_t messageToDoctor = (age <<24)
                             + (tmpHealthData.stepsADay<<8)
                             + (tmpHealthData.levelsADay);
 return messageToDoctor;
}

int main() {
 Parent papa = new Papa();

 papa.setAge(54);
 papa.setHealthInfo(1000,6);

 // do other stuff ...

 uint32_t message= createMessage(papa);
 // send message ... 

 return 0;
}

UML

对我来说,结构在某种程度上类似于类似乎是合乎逻辑的,但由于我只将它与 w.r.t. 一起使用(当我不与父级交互时,我根本不需要该上下文)。从技术上讲,我在方法中短暂地调用该结构,但仅在那里。classcreateMessage

我想将重要数据存储在一个有意义的事情中,直到我需要它来序列化这些信息以在其他地方使用它。

我有多个不同的结构类似于这个healthInfo示例

enter image description here

关注

  1. 当每个 1 个 UML 类时,我怀疑这是否实际上提高了整个 UML 图的整体可读性。但是,如果需要这样做,它会看起来像上面的那个吗?我不确定 Parent 中的 healthInfo 属性。我会说它是一种聚合,因为没有类就无法存在。«struct»structParent

  2. 在类中定义 as 时使用 as 是错误的吗?structreturn type

  3. 首先在类内创建是错误的吗?structs

  4. 一旦我作为一个整体返回,我想在没有 getter 方法的情况下访问结构的属性。这很好还是我应该使用 getter 方法将它们设置为私有以使其“更干净”的编码?struct

我认为有一些两极分化的意见,期待推理。

C++ UML 内部类 C ++03

评论

1赞 Eljay 9/8/2023
在 C++ 中,类型和类型是一回事。UML 应该以相同的方式表示它们。A 具有默认的公共继承和成员,A 具有默认的私有继承和成员。这只是可访问性默认值的区别;它们的打字方式没有区别。与其他一些面向对象语言不同,嵌套类仅由其包含类限定命名空间范围,它们没有任何隐式外部容器指针(必须显式编码)。structclassstructclass
0赞 spaceKelan 9/8/2023
感谢您的快速回复。我不确定我是否完全理解最后一部分。此时此刻,我无意使用指针。你告诉我我不应该创建一个内部类来使用它作为吗?structreturn type
1赞 Eljay 9/8/2023
在类中声明结构是完全可以的。用于任何合适的目的。作为返回类型当然是一个合适的目的。
0赞 spaceKelan 9/8/2023
谢谢你的澄清。当我现在有默认参数时,比如会在父级或结构本身的 UML 中移动该默认值吗?我认为 C++ 03 希望在构造函数中显式声明这一点,而不是在 .h 文件中使用行赋值。stepsADay=100

答:

2赞 Jean-Baptiste Yunès 9/8/2023 #1
  • 在我看来,这似乎是合乎逻辑的,结构在某种程度上类似于一个类,当然它只是一个没有操作的类。

  • healhInfo不能私密,你通过方法把它暴露在外面。getHealthInfo()

对于收容,应该是这样的:

enter image description here

请注意,应该写成 .建议类型以大写字母开头。healthInfoHealthInfo

评论

0赞 spaceKelan 9/8/2023
谢谢,这很有帮助!当将类型更改为大写的驼峰时,它在父 UML 中会是什么样子? 或。只有前者对我来说有意义,请确认。你对将该结构作为有什么想法吗?- HealthInfo: HealthInfo- healthInfo: HealthInforeturntype
3赞 Eljay 9/8/2023
@spaceKelan • 由于结构是匿名的,因此它没有类型名称。将其更改为为类型指定名称。struct HealthInfo { uint16_t stepsADay; uint8_t levelsADay; } healthInfo;HealthInfo
1赞 qwerty_so 9/8/2023 #2
  1. «struct» 不是 UML 的构造型或关键字。你需要一个配置文件来定义这种刻板印象,实际上破坏了 UML 的语言不可知的方式。更好的可能是不要对它进行刻板印象并使用简单的类。任何编码人员都可以猜到这将像在 Cxx 语言中一样使用struct
  2. 您可以从操作中返回任何类。
  3. 嵌套是 UML 中的一项基本内容,您可以以不同的方式展示它。使用嵌套连接器,或者通过放大外部类并将内部类呈现在边界内。
  4. 这是一个编码规则的问题,与UML设计不同。如果你有公共属性,你就不需要 getter/setter。在 SO 上有几个关于使用 getter/setter 的答案。

一般评论:UML 只有整数,没有您使用的特定整数。

评论

0赞 spaceKelan 9/11/2023
感谢您的见解!你能给我举个例子吗 1.看起来,那将不胜感激!关于 2.@Christophe回答了我对返回类型的担忧。我会记住 3,但前者所说的关于 +。4 我认为现在已经解决了。关于你的一般性评论,这确实是一个很好的提醒,但出于可读性的目的,我决定保持这种方式。containmentaggregation
0赞 qwerty_so 9/11/2023
不确定您对 1.会意味着。撇开结构而只使用普通类是显而易见的,不是吗?
0赞 spaceKelan 9/11/2023
好吧,如果它如此清楚,我不会要求澄清,对吧?所以我所做的就是,我删除了所有属性都是公共的,没有方法,你的意思是,“编码员”会直接知道,现在是一个结构,对吧?<<struct>>
0赞 qwerty_so 9/11/2023
是的,一个只有属性的类显然是编码人员的结构体。如果没有,编码员应该寻找其他谋生的事情。
0赞 spaceKelan 9/11/2023
我真的不明白你为什么这么活泼,但谢谢你的澄清。
1赞 Christophe 9/10/2023 #3

总之

如果此设计可行,则需要在两个类之间定义两个不同的关系,并且:HealthInfoParent

  • 第一个收容关系(圆圈加号),正如Jean-Baptise所解释的那样。这说明 UML 类的定义嵌套在 中。HealthInfoParent

  • 第二个复合聚合(黑菱形),它告诉父级有一个成员 healthInfo,并且该成员的生命周期由 Parent 管理(这是因为 C++ 使用值语义,除非您明确需要引用/指针)。

仅显示其中一种关系并不能正确捕获您的设计。

但是,外部泄漏的嵌套私有类型往往会导致隐藏的耦合。公共嵌套类型,甚至更好的非嵌套类型(在包含所有相关类的单独命名空间中)是更好的替代方法。

更多细节,包括设计问题

代码中的一些 C++ 问题

在 C++ 中,结构是一个类,唯一的区别是成员和基类默认是公共的。考虑到您已经向所有成员展示了公共访问权限,因此无需定义和使用构造型。«struct»

编辑:在 C++ 实现中,类和结构之间的区别仅在于成员和基类的可访问性。在 UML 中,您指定了公共成员,并且继承将始终是公共的(没有私有继承)。在这方面,类 X {public: .....}; 和结构体 X {...}; 将具有相同的语义和相同的模型。这就是为什么我说没有必要添加非标准的“结构”刻板印象。当然,如果您使用 C++ 配置文件并且 UML 构造型用于控制代码生成,则使用 «struct» 是有意义的。如果您想在设计和实现之间有一个清晰的映射,也是如此。

但是,结构的 C++ 和 C 用法之间的代码可能会混淆。下面指定一个匿名 (type) 并定义该类型的成员:structhealthInfo

  struct{
        uint16_t stepsADay;
        uint8_t levelsADay;
  }healthInfo; 

因此,不能返回任何类型,因为没有为该类型定义名称。编译器使用错误消息标记此问题。healthInfo

如果要定义私有类型并返回该类型的值,它应该如下所示,正如 Eljay 在评论中解释的那样:

  struct HealthInfo {
        uint16_t stepsADay;
        uint8_t levelsADay;
  }healthInfo; 

返回类型将按如下方式指定:

  HealthInfo Parent::getHealthInfo(){
   return this->healthInfo;
  }

为了避免类型和成员之间的这种混淆,建议的方法是在两个语句中定义类型和成员,即使它们位于同一类中。struct HealthInfo { ...};HealthInfo healthInfo;Parent

更严重的设计问题

用于返回的嵌套私有类型的问题在于,在类之外、其他类或独立函数中使用是具有挑战性的:

由于返回的类型在外部未知(private 是 private),因此其他类或非成员函数根本无法对此返回值执行太多操作。例如,它不能创建局部变量。幸运的是,现代 C++ 带来了 ,因此您可以编写:getHealthInfo()ParentParent::HeathInfo x = ...auto

auto x = papa.getHealthInfo(); 
cout << x.stepsADay;

但这非常烦人,因为 使你能够访问不应该在类外使用的内部结构。因此,这违背了私有嵌套类型的目的,并创建了隐藏的耦合。如果以后决定更改私有类型,则必须使用父类检查所有代码,以确定它们是否使用私有类型的内部结构,而不是将影响局限于类。auto

编辑:对于早于 C++11 的旧 C++ 标准,auto 关键字表示自动存储持续时间(例如函数的局部变量)而不是自动类型推导。在这种情况下,私有返回类型是完全无用的

这里是编译的更正代码

选择?

由于返回类型,如果要在外部使用信息,为什么要嵌套信息并将其设为私有?如果要在其他地方使用此类型,更好的方法是将嵌套类型设为公共类型,并将成员保持私有,如果应用上述建议,则很容易做到这一点。这样至少依赖关系是清晰干净的。

另一项改进是将成员函数设为 。createMessage()Parent

评论

0赞 spaceKelan 9/11/2023
首先,非常感谢您的详细回复!我有一些问题:a)我使用C++ 2003,从我读到的内容来看,在C++11和更新版本中具有不同的含义。因此,我不认为这是一种选择。b)关于替代方案,我是否正确:我采用结构定义并将其放在外部,同时保留在类的私有部分?c) 如果我这样做,我的 UML 会像你上面描述的 In Short 一样吗?我会考虑最后一行!autostruct Healthinfo{ParentHealtIinfo healthInfoParent
0赞 spaceKelan 9/11/2023
当您声明不需要 [构造型] (docs.nomagic.com/pages/viewpage.action?pageId=38044253) 时,我会省略它是结构的信息吗?在复杂的 UML 中,这种构造型有助于提高可读性。最后,我的目标是在 100% 严格的 UML 上有一个易于理解的图表,因为我不从中派生代码。像@qwerty_so一样,抱怨不是UML中使用的类型。我相信它向读者澄清了很多。<<struct>>uint8_t
1赞 Christophe 9/12/2023
@spaceKelan感谢您给我这个机会澄清。关于 a) 和结构,我添加了两个“编辑”段落来澄清。关于 b) 你也可以保持类型嵌套,但将其公开。然后,您可以在其他地方使用它,但使用完全限定的名称,例如 .但我确实会去从课堂上提取它。关于 c) 是的。事实上,qwerty_so是对的。但是,通常的做法是假设语言配置文件和/或 UML 库定义语言特定类型(如 or)并在模型中使用它们。Parent::Xxxuint8_tuint8_tdouble