0

I want to create a UML class diagram to represent a Kotlin class. I have come across a few options and came up with two alternatives. However, I'm unsure which of the two is the most appropriate and accurate. Can someone help me determine which UML diagram is correct or better for this Kotlin class?

Here is the Kotlin class I want to represent:

class Student(val name: String, var address: String) {

    private var numCourses: Int = 0

    private val courses: Array<String> = Array(MAX_COURSES) { "" }

    private val grades: IntArray = IntArray(MAX_COURSES)
    companion object {
        const val MAX_COURSES = 30
    }

    fun printGrades() {
        print(name)
        for (i in 0 until numCourses) {
            print(" ${courses[i]}:${grades[i]}, ")
        }
        println()
    }

    fun calculateAverageGrade() = grades.sum().toDouble() / grades.size

    fun addCourseGrade(course: String, grade: Int) {

        require(numCourses < MAX_COURSES) {
            "A student cannot take more than $MAX_COURSES courses"
        }

        require(grade in 0..100) {
            "Grade must be between 0 and 100"
        }

        courses[numCourses] = course
        grades[numCourses] = grade
        numCourses++
    }

    override fun toString() = "name($address)"

}

Option 1: With getters and setters

All the class' attributes are private and the public ones get public getters/setters:

Uml 1

Option 2: using some additional UML notations

I used these two answers on Stackoverflow for the <<get/set>> annotations (see here) and {readOnly} attribute (see here).

Uml 2

Christophe
  • 68,716
  • 7
  • 72
  • 138
Ali Dehkhodaei
  • 426
  • 3
  • 15
  • You don't seem to get the point in UML: it's language agnostic. If you want to code then code. If you want to model use UML. But don't code in UML. – qwerty_so Jul 16 '23 at 09:17
  • @qwerty_so I am really confused, Please more explain. – Ali Dehkhodaei Jul 16 '23 at 09:40
  • @AliDehkhodaei Thank you for this interesting question for which you made some research! I made some edits to facilitate the reading. Please feel free to edit in case I would have misrepresented anything. – Christophe Jul 16 '23 at 10:25
  • @qwerty_so I fully agree. But apparently, OP doesn't try to code in UML but simply to document an existing code. And to be faire, UML is also agnostic about its usage ;-) By the way OP links to one of your interesting answer that I discovered thanks to his link ;-) – Christophe Jul 16 '23 at 10:29
  • I also find this for c# https://github.com/pierre3/PlantUmlClassDiagramGenerator. It provide some details for uml like readOnly and <><>. – Ali Dehkhodaei Jul 16 '23 at 10:40
  • @Christophe The intention to show getter/setter is due to the wish to show coding details. I'd rather close this question for being a duplicate of Gert's answer. (btw. you clobbered the link(s)) with your edit. – qwerty_so Jul 16 '23 at 11:19
  • @qwerty_so I rather think that Gert's answer is not relevant here, and it is more about showing in UML some language related particularities that are in the gray zone (like [here](https://stackoverflow.com/q/470097/3723423) for C# properties). – Christophe Jul 16 '23 at 12:25
  • @AliDehkhodaei Interesting GitHub link. As you see, the generator adds the get/set stereotype, only for the properties that define it. There's no need to define them for a public attribute. – Christophe Jul 16 '23 at 12:38
  • But in my example name and address have implicit getter and setter. Don't should i define that get/set? – Ali Dehkhodaei Jul 16 '23 at 12:52

3 Answers3

2

Let's keep it simple. I read that Kotlin properties are mutable when defined with var and read only when defined with val. I see in your code two public and three private properties. Looking at the UML definition of a property and UML rules for accessibility, this translates very simply to:

+ name: String {readOnly}
+ address: String 
- numCourses: Int=0
- courses: String[MAX_COURSES] 
- grades: Int[MAX_COURSES] 

Some more explanations:

  • There is no need to add anything about getters and setters here, if you don't implement getters and setters explicitly. The question you were referring to was for a design where all properties are kept private and are exposed via explicit getters and setters. You don't do this, so you don't need it.
  • In your code you don't define the array to be of size of magic number 30. So keep this good practice also in UML and use a multiplicity of MAX_COURSES which is a constant value.
  • there is an important semantic difference between Kotlin arrays and UML multiplicities. In Kotlin, the array is read only, because one array is created, and this array object of fixed size is not modified. However, you can still modify the objects it contains. In UML there is no array; the multiplicity tell that there are MAX_COURSES properties (without details of how this is implemented). If you would use {readOnly}, it would apply to the all the MAX_COURSES elements, meaning that you could only define their values once and not change them afterwards (i.e. stay forever ""): this is not what you want to express.

Additional remarks:

  • For a Kotlin property xyz with getters and setters using a backing property, you do not need anything else than the standard UML notation of derived property: /xyz. UML specifications explain that:

    But where a derived Property is changeable, an implementation is expected to make appropriate changes to the model in order for all the constraints to be met, in particular the derivation constraint for the derived Property. The derivation for a derived Property may be specified by a constraint.

  • If you would define in your class explicit getters and setters and backing field, you could use «get», «set» stereotypes to inform that there could be some operations behind the scene. For properties in interfaces, I'd do it systematically, to clarify what the implementing classes have to provide.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thank you very much, Only for interfaces? `If you would use Kotlin properties in an interface` – Ali Dehkhodaei Jul 16 '23 at 12:37
  • @AliDehkhodaei "or if you would defined...." - What I wanted to say is in an interface i'd do it systematically (because the classes implementing interface must know what to provide) - but that in the other cases, I'd use it when getters and setters are fedinef explicitly. – Christophe Jul 16 '23 at 12:41
  • The challenge in your question, is that you start with the code and want to document it. So you'd need simple transcription rules. In many cases, however, from a design perspective you would document in UML some constraints (e.g. `{hour>=0 and hour<=24}` ) and the the setters are only an implementation detail (i.e. how the constraint is enforced in the code) – Christophe Jul 16 '23 at 12:52
  • Thank you. 1. Please edit - grades: String[MAX_COURSES] to - grades: Int[MAX_COURSES]. 2. Can you show `backing property` in uml with example? – Ali Dehkhodaei Jul 17 '23 at 09:12
  • @AliDehkhodaei There is no reason to ask additional questions in the comment. If you have such: open a new question. – qwerty_so Jul 17 '23 at 09:23
  • @AliDehkhodaei Thank you for pointing out the Int issue. I'd be glad to show getter or getter/setter with backing property if you give me an example code in another question. I could also elaborate on the backing field, if you give a real life example (eventually in the same new question) – Christophe Jul 17 '23 at 18:14
0

As written in your first link, there is no standard such as <<get/set>> or {readOnly}, but nothing stops you from using it (also shown here) if it is OK within your team.

Your first variant is vanilla (i'll call) UML, except name & address should be public.

Jemshit
  • 9,501
  • 5
  • 69
  • 106
  • Why did you add the following methods when I don't have them? ```| - getNumCourses(): Int | | - setNumCourses(): Unit | | - getCourses(): Array | | - getGrades(): IntArray |``` – Ali Dehkhodaei Jul 16 '23 at 09:07
  • @AliDehkhodaei you are right, i mistook private fields for having a backing field (getter/setter) – Jemshit Jul 16 '23 at 09:19
  • 1
    May I suggest to read the link related to the `{readOnly}` until the end? You will find out that it's 100% standard UML. Then, it's worth mentioning that adding stereotypes (e.g. `«my new stereotype»`) is a standard way to enhance UML. Any decent modeling tool allows you to define your owns - the only question is to see if there is already some commonly used stereotypes for this specific issue (and «get» «set» seems to win the battle agains the less precise «property», also for other languages: https://stackoverflow.com/q/470097/3723423 ) – Christophe Jul 16 '23 at 10:41
0

UML as such is language agnostic. It is meant to model ideas on a relative abstract level - without having to think about programming language (PL) specific details. It is possible to augment a UML model so it targets a certain PL. However, it's rarely a good idea. When you make a model, you abstract from a concrete thing making it easier to handle.

One might get tempted to "re-code" in UML when documenting existing code. Not a good idea since you miss the point.

Having said this, I can only join what Christophe said in his answer and his UML is likely the simplest to express the essence.

tl;dr As Einstein said: make things as simple as possible, but not simpler. Sometimes, that is difficult...

qwerty_so
  • 35,448
  • 8
  • 62
  • 86