Mutators bad... inspectors ok?

Started by
16 comments, last by freeworld 12 years, 8 months ago
I'm doing a little clean up and in my never ending learning experience. I'm back at that good 'ol question. How many accessor functions are too much for an object? I noticed I tend to have alot of classes were the data they encapsulate is actually public, simply for the fact that I don't want to right a bunch of functions just to be able to see what the values of that data are. I try my best to stay away from, changing the data from the outside without using a function specifically for changing that data.... but alot of the data needs to be visible to outsiders.

Sorry I don't have a great example but here's a quick pseudo


class Car
{
engineHP;
engineRPM;
fuel;
MPH;

void PushGasPedal(float intensity);
}



Now in the above example, I might have a dyno machine that needs to be able to see what each of those values are to be able to make a graph... but the dyno shouldn't change any of those values, in fact changing any but the fuel could seriously screw up some calculations. So I would instead use the function PushGasPedal() which should alter all the above variable. expend fuel, increase RPMs, generate horse power and turn that into speed(mph).

so in a case like this, and I know this is pretty small, but would it be good practice to leave them public or would you setup a series of get functions like so??


float const EngineHP();
float const EngineRPM();
float const Fuel();
float const MPH();

// ...etc...

[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
Advertisement

I'm doing a little clean up and in my never ending learning experience. I'm back at that good 'ol question. How many accessor functions are too much for an object? I noticed I tend to have alot of classes were the data they encapsulate is actually public, simply for the fact that I don't want to right a bunch of functions just to be able to see what the values of that data are. I try my best to stay away from, changing the data from the outside without using a function specifically for changing that data.... but alot of the data needs to be visible to outsiders.
...
so in a case like this, and I know this is pretty small, but would it be good practice to leave them public or would you setup a series of get functions like so??




Neither one is "bad". You are just choosing between different tools. Sometimes you need a knife other times a hammer; neither is bad.

You create whatever is needed to make the program.


You should decide up front what kind of object you will have.

You can have plain old data objects, such as a point or a vector or a matrix or some other fixed-form data. There is no reason to make the user go through accessors and mutators for that type of object. The class can still have functions, like ComputeEignevalue, but they don't need to run on private data in the class.

You can also have more complex class objects, such as your really bad car engine example. In that case, you almost never want direct access, and will tend to use accessor and mutator functions. You send it commands and events, and you request information. You ask the engine about it's RPMs, you (almost) never force your way inside the engine and measure the thing yourself.



What is your specific class you are talking about? Is it a plain-old-data container class that anyone can modify, or is it a complex class that you don't want people to touch the innards of? Is this an object where data can be modified with impunity, or where you need choke points to identify and handle and validate every time data is modified?

What is your specific class you are talking about? Is it a plain-old-data container class that anyone can modify, or is it a complex class that you don't want people to touch the innards of? That should help guide you.


I guess the biggest one that comes to mind is my GUI, I was adding a new widget to the system last nite and this got me thinking. I leave all the data public cause it's easier for me to iterate through the widgets and find out there positions, sizes, and so forth for rendering them. Though I don't want them to be altered beyond what their interface allows. For example, all my widgets work off a relative coordinate system... so if the parent widget moves, all it's children stay with it and stay aligned with it. I also use the tree of widgets like a quadtree when checking for mouse interactions. So if the parent is moved without using the interface it wont move it's children. Then if you try to click on one of those children, the children won't be checked unless they still reside within the parents box.
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
This is when I really feel the lack of fine-grained access control in pretty much every language ever.

Basically, you can mitigate some of this using friendship, but the bottom line is that you have to settle for some coarse-grained control at some point. Either you leave things public and trust the hell out of your clients not to screw things up, or you play the safe side of the fence and validate everything via contract-enforced interfaces. Sadly, aside from friendship, there really aren't mechanisms in today's programming languages for properly expressing concepts like "you may mutate these internals of mine, but not those other ones; your colleague is not permitted to know anything about the first set of internals, but has free reign over the second."

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Sadly, aside from friendship, there really aren't mechanisms in today's programming languages for properly expressing concepts like "you may mutate these internals of mine, but not those other ones; your colleague is not permitted to know anything about the first set of internals, but has free reign over the second."


Then what exactly are the "public", "protected" and "private" keywords used for? (in bad langueges like C++)
@freeworld - if you've got a class with a lot of accessors and/or mutators, it's often a sign that the class doesn't have a very solid responsibility.
Often in those cases, I'd just make it a struct with no member functions at all. Then perhaps a useful class can be built around that struct.
e.g. your variables could go into an [font="Courier New"]EngineInfo[/font] struct, which many different parts of code can operate on. e.g. a car can own one as a member and perform a simulation on it, a dyno can recieve a const-reference to one to update it's graph, etc... Doing this also breaks the dependency between [font="Courier New"]Car[/font] and [font="Courier New"]Dyno[/font], as instead, the both have a dependency on [font="Courier New"]EngineInfo[/font].

Just as food for thought, you could consider some completely different designs. For example, at the moment I assume that your dyno knows about the car class (i.e. in [font="Courier New"]dyno.cpp[/font], there is a [font="Courier New"]#include "car.h"[/font], etc)? If so, you could consider inverting the relationship:class Dyno {
void AttachToInput( const float* d );
};

class Car {
Car( Dyno& d ) { d.AttachToInput( &MPH ); }
};
Or eliminating the relationship altogether so neither class is aware of the other:class Dyno {
void AttachToInput( const float* );
};
class Car {
const float& MPH() const { return m_MPH; }
};
void HigherThirdParty()
{
Dyno d;
Car c;
d.AttachToInput( &c.MPH() );
}

@ApochPiQ - you actually can in C++ using an ugly mix of reference-cast operators and sub-class members with friends, etc, etc, but it ends up being extremely verbose. I usually just put a comment on my friend declarations saying which members that friend is supposed to be touching, that is, in the rare cases where friend is justified in the first place.

Usually friend issues simply go away when a design is modified to reduce individual relationships and responsibilities.
How many accessor functions are too much for an object?

I don't know if this is relevant, but as long as you define the accessors inside the class declaration they will automatically be expanded inline by the compiler so VectorWithPrivateMembers.GetX() will yield the exact same code as VectorWithPublicMembers.x. The reason I mention this is primarily if you ask because you are concerned with performance.

Besides that:

- Never declare pointer variables public. Unlike many other data types these can cause a lot of trouble if they are accessed before a valid value has been assigned to them. Using an accessor provides you both the opportunity to verify the pointer before returning it and to take action accordingly if it is invalid.

- If all the possible values you can assign to some variable (without making some deliberate type-casting hack) are acceptable to the program it is usually safe to declare them public. An example would be a 3D vector class; unless you deliberately access the address of the 3 components manually and force them to become NaNs, all the values you can assign to them from within the code are valid (or at least they should be). That said, always keep in mind that whenever an object is allocated at run-time there is no guarantee its memory space will initially contain valid data.

- Never declare pointer variables public. Unlike many other data types these can cause a lot of trouble if they are accessed before a valid value has been assigned to them. Using an accessor provides you both the opportunity to verify the pointer before returning it and to take action accordingly if it is invalid.


If that's your concern, you should just use some type of smart pointer.
I trust exceptions about as far as I can throw them.

[quote name='ApochPiQ' timestamp='1311632228' post='4840223']
Sadly, aside from friendship, there really aren't mechanisms in today's programming languages for properly expressing concepts like "you may mutate these internals of mine, but not those other ones; your colleague is not permitted to know anything about the first set of internals, but has free reign over the second."


Then what exactly are the "public", "protected" and "private" keywords used for? (in bad langueges like C++)
[/quote]



[source lang="cpp"]class BigStuff
{
private:
int Foo;
std::string Bar;

private:
float Baz;
bool Quux;

public:
friend class FooBarAccess;
friend class BazQuuxAccess;
};

class FooBarAccess
{
public:
void Frobnicate(BigStuff& stuff)
{

stuff.Foo = 42;
}
};

// So far so good...

class BazQuuxAccess
{
public:
void Frobnicate(BigStuff &stuff)
{
stuff.Foo = 0;
// Hey! You're not supposed to do that!
}
};[/source]

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

[source lang=cpp]template<class T>class Protected
{
protected:
T v;
operator T&() { return v; }
operator const T&() const { return v; }
T& operator=( const T& o ) { v = o };
};

class BigStuff
{
private:
class : public Protected<int> { int& operator=( int o ) { v = o; return v; } friend class BigStuff; friend class FooBarAccess; } Foo;
class : public Protected<std::string> { std::string& operator=( const std::string& o ) { v = o; return v; } friend class BigStuff; friend class FooBarAccess; } Bar;

private:
class : public Protected<float> { float& operator=( float o ) { v = o; return v; } friend class BigStuff; friend class BazQuuxAccess; } Baz;
class : public Protected<bool> { bool& operator=( bool o ) { v = o; return v; } friend class BigStuff; friend class BazQuuxAccess; } Quux;

public:
friend class FooBarAccess;
friend class BazQuuxAccess;
};[/source]FTFY -- generates a compile error for your test case when BazQuuxAccess tries to read Foo
God it's ugly though.

This topic is closed to new replies.

Advertisement