I struggle with this question very often and couldn't find any clear solution. I think I know the motivation of getters/setters.
Prior Information:
When realizing real life data, usually the data is encapsulated in more than one layers. For example:
// 1st stage data types ------------------------------ struct Cartesian { int32_t x; int32_t y; int32_t z; } struct GeoLocation { double_t latitude; double_t longitude; int32_t altitude; } // 2nd stage data types ------------------------------ struct Drone { Cartesian baseOffset; // m Cartesian velocity; // m/s } struct Plane { GeoLocation location; // semicircle Cartesian velocity; // knots } // 3rd stage data types ------------------------------ struct Swarm { Plane base; Drone member[10]; }
In C++, I use classes instead of structs (because why not?). And the problem arises when a data about Swarm[3].member[8].velocity.x
is received over some communication channel. Realize that there can be more than one swarm in a system.
Requirement:
By MISRA C++ rules, a function cannot return non-const reference to any class-member because the member should not be changed without that class' permission/knowledge.
Question:
When I use getters and setters, I cannot say "Swarm[3].member[8].velocity.x
"; instead I may say this in several ways:
1. This is not allowed as get() functions returns const reference and cannot call set().
Swarm[3].getMember(8).getVelocity().setX(5)
; (orset("X", 5)
)
2. This method carries all burden into the Swarm class. Although the code seems shorter for who is calling Swarm class, it is very heavy to code and to do maintenance in the background in case of a change.
Swarm[3].setMemberVelocity(8,X,5)
3. This method is somewhat in between, but here the problem is you might be sacrificing efficiency because every time a new data arrives first you create a temporary variable, get it, fill it and set it.
Cartesian tempVelocity = Swarm[3].getMember(8).getVelocity();
tempVelocity.x = 5;
Swarm[3].setMemberVelocity(8, tempVelocity);
Which one of these 3 methods are the best? Or are there any alternatives that I might use?
Thanks in advance.
There is clear need for plain old data, standard layout and trivially copyable. That kind of data makes it simpler to achieve that binary layout of it is portable and compatible with other programming languages. MISRA requires that kind of data to be declared using keyword struct
.
As side effect such data makes it also possible to write code like:
swarms[swarmNo].drones[droneNo].velocity.x = 5;
That kind of deep chain navigation is still considered code smell and it has also name "train wreck pattern". However it at least does not have any unneeded get
, set
and ()
bloat to type.
All the rest of data should follow object oriented principles. There swarm itself has responsibility to do its maneuvers by commanding its drones and drone itself has responsibility to follow (or to refuse) such commands. Nothing should be able to simply set some individual property of something out of blue from outside.
MISRA requires that kind of data to be declared using keyword class
. The non-static data members should be made private and non-const handles to these members should not be returned by public member functions.