Sign in to follow this  

Mutators bad... inspectors ok?

This topic is 2334 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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
[code]

class Car
{
engineHP;
engineRPM;
fuel;
MPH;

void PushGasPedal(float intensity);
}

[/code]

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??

[code]
float const EngineHP();
float const EngineRPM();
float const Fuel();
float const MPH();

// ...etc...

[/code]

Share this post


Link to post
Share on other sites
[quote name='freeworld' timestamp='1311629528' post='4840209']
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??
[/quote]



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?

Share this post


Link to post
Share on other sites
[quote name='frob' timestamp='1311630323' post='4840213']
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.
[/quote]

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.

Share this post


Link to post
Share on other sites
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."

Share this post


Link to post
Share on other sites
[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."
[/quote]

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

Share this post


Link to post
Share on other sites
@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:[code]class Dyno {
void AttachToInput( const float* d );
};

class Car {
Car( Dyno& d ) { d.AttachToInput( &MPH ); }
};[/code]Or eliminating the relationship altogether so neither class is aware of the other:[code]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() );
}[/code]
@ApochPiQ - you actually [i]can[/i] in C++ using an ugly mix of reference-cast operators and sub-class members with friends, etc, etc, but it ends up being [i]extremely[/i] 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.

Share this post


Link to post
Share on other sites
[quote name='freeworld' timestamp='1311629528' post='4840209']How many accessor functions are too much for an object?[/quote]
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.

Share this post


Link to post
Share on other sites
[quote name='Dragonion' timestamp='1311683638' post='4840481']
- 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.
[/quote]

If that's your concern, you should just use some type of smart pointer.

Share this post


Link to post
Share on other sites
[quote name='wolfscaptain' timestamp='1311678437' post='4840449']
[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."
[/quote]

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]

Share this post


Link to post
Share on other sites
[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.

Share this post


Link to post
Share on other sites
I think I just threw up a little in my mouth.


Of course, some people are going to see this and say, "hey, C++ is so great! It lets you do stuff like this to express rich semantics!"

My reaction, though, is "hey, C++ is disgusting! It requires you to do stuff like this to express rich semantics!"

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1311730336' post='4840905']My reaction, though, is "hey, C++ is disgusting! It requires you to do stuff like this to express rich semantics!"[/quote]
Have you ever considered using a purely functional programming language like Haskell ([url="http://haskell.org"]http://haskell.org[/url])? One of my professors at the university (mathematics) was very fond of this language, and as far as I know it uses a very clean and "mathematically correct" syntax.

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1311730336' post='4840905']hey, C++ is so great! It lets you do stuff like this to express rich semantics![/quote]I know, right!

You can even [url="http://thedailywtf.com/Articles/The_Secret_to_Better_C.aspx"]make your own keywords[/url]![source lang=cpp]#define hidden( type, name, access_rights ) \
class { type v; [color="#808080"][font="Consolas,"] [/font][/color]\
type& operator=( const type& o ) { v = o; return v; } \
operator type&() { return v; } [color="#477280"][font="Consolas,"] [/font][/color]\
operator const type&() const { return v; } \
type& operator=( const type& o ) { v = o } \
access_rights } name
#define visible(type) friend type;

class BigStuff
{
hidden( int, Foo, visible(BigStuff) visible(FooBarAccess) );
hidden( std::string, Bar, visible(BigStuff) visible(FooBarAccess) );
hidden( float, Baz, visible(BigStuff) visible(BazQuuxAccess) );
hidden( bool, Quux, visible(BigStuff) visible(BazQuuxAccess) );
};[/source]

Share this post


Link to post
Share on other sites
[quote name='Dragonion' timestamp='1311770392' post='4841067']
[quote name='ApochPiQ' timestamp='1311730336' post='4840905']My reaction, though, is "hey, C++ is disgusting! It requires you to do stuff like this to express rich semantics!"[/quote]
Have you ever considered using a purely functional programming language like Haskell ([url="http://haskell.org"]http://haskell.org[/url])? One of my professors at the university (mathematics) was very fond of this language, and as far as I know it uses a very clean and "mathematically correct" syntax.
[/quote]

Have you ever used Haskell? It's module design is probably the only thing vaguely related to access levels, and that isn't really super full featured (compared to say... it's type system).

And really, why consider disgusting things to express semantics when you can use a language that requires disgusting things to express such straightforward concepts as state, or IO? (kidding!)

Share this post


Link to post
Share on other sites
[quote name='Dragonion' timestamp='1311770392' post='4841067']
[quote name='ApochPiQ' timestamp='1311730336' post='4840905']My reaction, though, is "hey, C++ is disgusting! It requires you to do stuff like this to express rich semantics!"[/quote]
Have you ever considered using a purely functional programming language like Haskell ([url="http://haskell.org"]http://haskell.org[/url])? One of my professors at the university (mathematics) was very fond of this language, and as far as I know it uses a very clean and "mathematically correct" syntax.
[/quote]



[url="http://epoch-language.googlecode.com/"]Using other people's broken languages is for weenies.[/url] [img]http://public.gamedev.net/public/style_emoticons/default/tongue.gif[/img]

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1311781607' post='4841146'][url="http://epoch-language.googlecode.com/"]Using other people's broken languages is for weenies.[/url] [img]http://public.gamedev.net/public/style_emoticons/default/tongue.gif[/img][/quote]
I can't say I have ever heard of the Epoch language before; I take it you are somehow involved in this project?

[i]True genius is not being able to create a perfect language, but being able to create perfect software with a broken one[/i] =P

Share this post


Link to post
Share on other sites
[quote name='Dragonion' timestamp='1311798535' post='4841284']
[i]True genius is not being able to create a perfect language, but being able to create perfect software with a broken one[/i] =P
[/quote]
True genius is sitting on a beach somewhere warm while someone does both of those for you 8-)

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1311680865' post='4840454']
@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].
[/quote]

This is actually the approach I'm tinkering with right now. Instead of using a chain of inheritance to contain the widgets within a root widget. I'm thinking of using a sorta' factory class "Menu" that manages widgets, instead of the widgets managing themselves.

Share this post


Link to post
Share on other sites

This topic is 2334 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this