Your first UML model says that a complex number is composed of 2 doubles. This is definitively an acceptable way to it.
Your second UML model does not represent the corresponding code: you don't show inheritance but a navigable association, and you forget to tell about the additional member. The following would more accurately represent the code:
However, this does not represent the reality: a complex is not a kind of double.
For example, if x
is a double, I can always assume that x*x >= 0
. So for any subtype of double, I'd assume that this fundamental assumption remains true. This is exactly what Liskov's substitution principle means with its rule about post-conditions. So, as the square of a complex can be negative, the complex is not a proper subtype of a double. Transposed to OOP, complex
should not inherit from a double
.
Your second option however unveils a fealing that there must be something in common between doubles and complex. Well, they are both numbers. And this you can very well express with the following model:
Now, you may just continue, with modelling the number theory, starting with defining the number interface. This could be a nice step if you want to write a symbolic calculator
Additional note: Liskov's substitution principle was first defined by Barbara Liskov and Jeanette Wing, long before R.C. Martin took it over into his SOLID principles. It's not a dogmatic OOP rule, but a fundamental work on typing theory. Bjarne Stroustrup in his book "The design and evolution of C++" acknowledged for example that protected
members allows to shortcut Liskovs's history constraint and that this was the cause of very nasty bugs that are hard to debug. So it's definitively not an obsolete principle;-)