2

How would a UML diagram look like for the case of the class containing structs which only exist during the life-time of the class?

Disclaimer: I saw that there are similar questions, but mine differs in the fact, that the structs I use I define inside the class as described here. The examples I saw on how to use struct in UML always referred to specific struct to that class.
Please see 4 questions below first.

Sample Code

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

To me it seems logical, that a struct is somehow similar to a class, but as I only use it w.r.t. to the class (when I don't interact with the Parent, I don't need that context at all). Technically I call that struct for a short moment in the createMessage method but only there.

I want to store important data in a meaningful matter until I need it to serializing this information to use it somewhere else.

I have multiple different structs similar to this healthInfo example.

enter image description here

Concerns

  1. When creating 1 UML class per «struct», I am sceptical that this actually improves the overall readability in the overall UML diagram. However, if this is demanded, would it look like the one above? I am unsure regarding the healthInfo attribute in Parent. I would say it is an aggregation,because the struct cannot exist without the Parent class.

  2. Is it wrong to use the struct as return type when it is defined in the class?

  3. Is it wrong to create structs inside the class in the first place?

  4. Once I return the struct as a whole, I want to access the attributes of the struct without getter methods. Is this fine or should I set them as private with getter methods to make it "cleaner" coding?

I assume there are some polarizing opinions, looking forward to the reasoning.

4
  • 1
    In C++, a struct type and a class type are the same thing. UML should represent them in the same way. A struct has default public inheritance and members, and a class has default private inheritance and members. That is only a difference in accessibility defaults; it is not a difference in how they are typed. Unlike some other OO languages, nested classes are only namespace scoped by their containing class, they do not have any implicit outer container pointer (that would have to be coded up explicitly). Commented Sep 8, 2023 at 13:41
  • Thank you for the quick response. I am not sure I fully understand the last part. In this moment, I have no intention to use a pointer. Do you tell me I should not create a struct inside a class to also use it as return type? Commented Sep 8, 2023 at 14:18
  • 1
    It's perfectly fine to declare a struct inside a class. For any suitable purpose. As a return type is certainly a suitable purpose. Commented Sep 8, 2023 at 14:36
  • thank you for that clarification. When I have default parameters now like stepsADay=100 would move that default value in the UML of the Parent or the struct itself? I think C++03 wants this to be explicitly declared in the constructor instead of using assignment in line in the .h file. Commented Sep 8, 2023 at 14:38

3 Answers 3

3

In short

If this design would work, you would need to define two distinct relationships between the two classes HealthInfo and Parent:

  • a first containment relationship (circled plus) as Jean-Baptise explains. This tells that the definition of the UML class HealthInfo is nested within Parent.

  • a second composite aggregation (black diamond), that tells Parent has a member healthInfo and that this member's lifecycle is managed by Parent (this is due because C++ uses a value semantic unless you explicitly want a reference/pointer).

Showing only one of these relationship does not capture appropriately your design.

However nested private types that are leaked outside tend to cause hidden couplings. A public nested type or even better a non-nested type (in a separate namespace that encloses all the related classes) are better alternatives.

More details, including design issues

Some C++ issues in your code

In C++ a struct is a class, the only difference being that members and base classes are public by default. There is no need to define and use a «struct» stereotype considering that you have shown public access for all the members.

Edit: In a C++ implementation, the difference between a class and a struct is only accessibility of members and base class. In UML you have specified public members, and inheritance will always be public (there is no private inheritance) . In this regard, class X {public: .....}; and a struct X {...}; would have the same semantic and the same model. This is why I said there is no need to add a non-standard «struct» stereotype. Of course, if you use a C++ profile and the UML stereotypes are used for controlling code-generation, it makes sense to use «struct». Same if you want to have a clear mapping between design and implementation.

There may however be a confusion in your code between the C++ and C usage of struct. The following specifies an anonymous struct (type) and defines a member healthInfo of that type:

  struct{
        uint16_t stepsADay;
        uint8_t levelsADay;
  }healthInfo; 

You can therefore not return any healthInfo type, simply because you didn't define a name for that type. The compiler flags this problem with an error message.

If you want to define a private type and return a value of that type, it should look like, as Eljay explained in the comments:

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

and the return type would be specified like this:

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

To avoid this kind of confusion between types and members, the recommended approach would be to define the type struct HealthInfo { ...}; and the member HealthInfo healthInfo; in two statements even if it's in the same Parent class.

More serious design issues

The problem with a nested private type that you use for return, is that the use outside of your class, in other class, or self-standing functions is challenging:

Since getHealthInfo() returns a type that is not known outside of Parent (private is private), the other classes or non-member functions simply cannot do much with this return value. It cannot for instance create a local variable Parent::HeathInfo x = .... Fortunately the modern C++ has brought auto, so that you could write :

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

But this is very annoying, because the auto gives you access to internals that should not be used outside the class. So this defeats the purpose of having a private nested type and creates hidden couplings. If later you would decide to change the private type, instead of having an impact limited to the class, you would have to inspect all the code using the parent class to find out if they use the private type's internals.

Edit: with older C++ standards that predate C++11, the auto keyword means automatic storage duration (e.g.. a local variable of a function) and not automatic type deduction. In that case the private return type is completely useless

Here the corrected code that compiles

Alternatives ?

Why nest the information and make it private if it is to be used outside, because of the return type? If you want to use this type elsewhere, the better approach would be to make the nested type public, and keep the member private, which is easily done if you apply the advices above. So that at least the dependencies are clear and clean.

Another improvement would be to make createMessage() a member function of Parent.

Sign up to request clarification or add additional context in comments.

3 Comments

First of all, thank you so much for your detailed reply! I got some questions: a) I use C++ 2003 and from what I read auto has a different meaning in C++11 and newer versions. I therefore don't think this is an option. b)regarding the alternative, do I get you right: I take the struct definition struct Healthinfo{ and put it outside of Parent while keeping HealtIinfo healthInfo in the private part of the Parent class? c) If I do so, would my UML look like you described In Short above? I will think about the last line !
when you state there is no need for a <<struct>> [stereotype] (docs.nomagic.com/pages/viewpage.action?pageId=38044253), would I just omit the info that it is a struct? In the complex UML this stereotype could help with readability. In the end I aim to have a well understood diagram over a 100% strict UML, as I do not derive code from it. Like @qwerty_so complains uint8_t is not a type used in UML. I believe it is clarifying a lot to the reader.
@spaceKelan thank you for this opportunity to clarify. Regarding a) and struct, I have added two "Edit" paragraphs to clarify. Regarding b) you could also keep the type nested but make it public. You could then used it elsewhere but with a fully qualified name, e.g. Parent::Xxx. But I'd go indeed for extracting it from the class. Regarding c) Yes. Regarding uint8_t indeed, qwerty_so is right. However, it is a common practice to assume a language profile and/or an UML library that defines language specific types such as uint8_t or double and use them in the models.
2
  • To me it seems logical, that a struct is somehow similar to a class, sure it is just a class with no operation.

  • healhInfo can not be private has you exposed it to the outside through getHealthInfo() method.

For the containment, that should be something like:

enter image description here

Note that healthInfo should be written HealthInfo. It is recommended that types begin with an uppercase.

3 Comments

Thank you, this helps a lot! When changing the type to uppercase camel back , how would it look like in the Parent UML? - HealthInfo: HealthInfo or - healthInfo: HealthInfo. Only former makes sense to me, please confirm. Do you have any thoughts on having that struct as returntype?
@spaceKelan • since the struct is anonymous, it doesn't have a type name. Change it to struct HealthInfo { uint16_t stepsADay; uint8_t levelsADay; } healthInfo; to give the type the name HealthInfo.
In c++, a struct is a class with all members public by default. It is therefore a fully fledged class that also can have operations. So the first bullet should be reworded to avoid any confusion. In UML you'd typically indicate visibility explicitly. The choice of a struct or a class is therefore an implementation deltail. But excellent containment suggestion.
1
  1. «struct» is not an UML stereotype or keyword. You would need a profile that defines such a stereotype actually destroying UML's language-agnostic way. Better would probably be to not stereotype it and use a simple class. Any coder could guess that this would be used as struct in Cxx languages
  2. You can return any class from an operation.
  3. Nesting is a basic thing in UML and you can show that in different ways. Either with the nesting connector or by enlarging the outer class and rendering the inner one inside the bounds.
  4. This is a matter of coding rules and different from UML design. If you have public attributes you would not need getter/setter. There are a couple of answers here on SO regarding the use of getters/setters.

General remark: UML only has Integer and none of the specific ones you use.

8 Comments

Thank you for your insights! Can you give me an example on how 1. would look like, that would be highly appreciated! Regarding 2. @Christophe answers my concerns regarding the return type. I'll keep 3 in mind but with what former said regarding containment+aggregation. 4 I think is resolved now. Regarding your general remark, that is a good reminder indeed, but I decided to keep it this way for readability purpose.
Not sure what your question regarding 1. would mean. Leaving away the struct and just using a plain class is pretty obvious, isn't it?
well if it would be so clear, I wouldn't have asked for clarification, right? So all I do is, I remove the <<struct>> and all attributes are public and no methods and you mean, that "a coder" would directly know, that is a struct now, right?
Yes, a class with attributes only is clearly a struct to a coder. If not, the coder should look for something else to do for a living.
I really don't get why you are so snappy, but thank you for your clarification.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.