
|
get/set routines in C?
There was a topic here several weeks ago (can't find it) that discussed the merits of get/set accessor methods in C++ classes as opposed to direct struct member manipulation by the user.
Well, these types of "get/set" routines also pop up C. I know many prominent libraries (like expat and libghttp) use them.
What are your thoughts on them? Are they only acceptable in a true OO language like C++? Are they merely a substandard way of faking OO in a procedural language like C? Are they never acceptable, in either C or C++?
MM Willis
Friday, March 5, 2004
>>> Are they only acceptable in a true OO language like C++?
If they are acceptable in a "true OO language" they're equally acceptable in any other language. There is no reason why they should be good in one language and bad in another.
Said that, based on my observations, set/get methods are unwarranted in about 90% of the cases.
René Nyffenegger
Friday, March 5, 2004
I've seen way too many of these in my life... and inflatory use of them is _always_ a sign the developer hasn't really understood the purpose of objects. In fact, some warning signs:
* lots of Get/Set methods
* huuuge classes
* deep inheritance
* complicated copycon/assop instead of good use of smart pointers
* no or only partial const-correctness
* no sign of any design regarding exception safety
_
Friday, March 5, 2004
* complicated copycon/assop instead of good use of smart pointers
* no or only partial const-correctness
* no sign of any design regarding exception safety
All these three have nothing to do with objects, but rather implementations of object oriented languages. Not all OO languages need have copy constructors, smart pointers, const, or exceptions.
If you'd said "doesn't understand C++", I'd be more inclined to agree (other than perhaps const correctness, I think it's of debatable utility that should be determined on a case by case basis).
Sum Dum Gai
Friday, March 5, 2004
Agreed, got carried away.
_
Friday, March 5, 2004
In OO, if you have a public member variable foo, then where is the sense in writing a SetFoo() method? It's easier for the user of the API to just do MyObj->foo = 10. The class is less cluttered (i.e. the API programmer doesn't need to maintain tons of getters and setters), so the code is easier to test and maintain and is therefore cheaper. The API user does less typing and has a better idea of where that value 10 goes -- into a variable, rather than into a long dark tunnel.
Where the use of setters makes sense is when there is coupling between the variable foo and other data.
As an example, if setting foo on its own would place the system into an inconsistent state (i.e. you'd also need to change the value of bar), then writing a setter for foo (and possibly bar) allows you to ensure that the state of the system remains consistent.
Always think about things from the point of view of the user of the API: they want to use the API to get stuff done -- if you force them to call lots of setters and then read data with lots of getters, then it's likely they'll make a mistake, especially when you forget (or don't have time) to write that documentation. Have a few, simple, interface functions to your code and return the relevant data in a structure. Make those interface functions easy to use, powerful and well-documented.
C Rose
Friday, March 5, 2004
> What are your thoughts on them?
In C I'd imagine that you use get/set when there's a side-effect (e.g. coupling to aother data), or when you don't want the client code to depend on knowing the layout of your struct.
> In OO, if you have a public member variable foo, then where is the sense in writing a SetFoo() method?
In C# you can write ...
class Bar {
public int m_foo;
public int foo { get { return m_foo; } set { m_foo = value; } }
}
... and the caller can write ...
Bar bar = new Bar();
bar.m_foo = 3; //legal syntax
bar.foo = 4; //also legal syntax
In fact, the latter statement is an implicit call to a "set_Foo(int value)" method.
> It's easier for the user of the API to just do MyObj->foo = 10.
The above syntactic sugar makes it equally easy to caller a setter method.
> Where the use of setters makes sense is when there is coupling between the variable foo and other data
For example, if a side effect of set_Foo is that its sets a "bool m_have_unsaved_data" member or similar.
Christopher Wells
Friday, March 5, 2004
There's another reason to use get/set methods in some circumstances: Thread safety. You can use internal locking in getters and setters. Also, you can assert the correctness of arguments, the object state, etc.
Keep in mind also that the fact that you may not currently be doing anything else in these methods than simply getting and setting a member value, doesn't mean that won't change later. Been there, done that, got the t-shirt for modifying all the code I had that directly accessed the public members. Never again.
sid6581
Friday, March 5, 2004
Using get/set methods instead of public fields is a matter of future-proofing.
Sure, your "warpspeed" field may be an int *now*, but what happens when you discover that warp fields actually require complex numbers? If you've exposed public fields, changing this requires breaking existing client code. If you used get/set methods, you'd just need to provide a new pair of get/sets that take complex, and you could transparently keep the old versions, and do the appropriate conversion under the hood.
This is not really THAT important if the class is simply part of your source code - the compiler would flag an incompatible change like that. However, if you're building a library component, this sort of protection is CRITICAL.
This is the reason that .NET languages built properties into the language. You write get/set functions, but the users of your class can still do "drive.warpspeed = 2".
Chris Tavares
Friday, March 5, 2004
>Keep in mind also that the fact that you may not currently be doing anything else in these methods than simply getting and setting a member value, doesn't mean that won't change later. Been there, done that, got the t-shirt for modifying all the code I had that directly accessed the public members. Never again.
I've also done that, and, to me, it's the number 1 reason why all member variables should be private. If you need to acess the data, use a setter/getter. The argument that the caller gets to know that he's putting the number in a variable instead of "long dark tunnel" completely misses the point--the caller shouldn't know, and if the class is properly implemented, he shouldn't care.
I also agree with those who say that not every variable needs a getter/setter, and most of mine don't, but if have to choose between maintaining code that's got too many getter/setters, and code that has too many public variables, I know which I'd choose (having worked on both).
Gav
Friday, March 5, 2004
Please forgive my impertinence, but I thought that using either public variables or public get/set methods contradicts the intent of encapsulation in "objects", and also makes it more difficult to use such objects for transactional or stateless or web-services purposes. Or is this only considered to be true for "business" objects?
puzzled by get/set methods
Friday, March 5, 2004
I recently reviewed some code I wrote several years ago. It's interesting to review your old code to get a sense of your own style.
I noticed that I used to use a lot of getters in the business layer objects of data centric applications. For example, I would I send a business layer object to a data layer object, and the data layer would extract the data using the business layer object's getters, then call a sproc with the data.
Now I ask the question, "how can I design this interface without any getters?"
I usually end up creating objects with SetParameters() methods. By doing so, I've found it makes unit testing of each object a lot easier.
The downside, though is that I sometimes end up with methods with long, long lists of parameters. I don't know if there is anything technically wrong with this, but it sure looks ugly.
Is there a happy medium?
Nick
Friday, March 5, 2004
Puzzled,
Public get/set methods can in fact encapsulate and abstract away the implementation. Maybe today they happen to store in a member variable, but tomorrow they could store on a central server... or they can contain logic, e.g. GetAmount(US_DOLLARS) returns the amount in the given currency.
Rick
Friday, March 5, 2004
Rick,
Thanks for the reply, but they still don't "encapsulate" the fact that there ARE these values that need to be set, regardless of whether the internal implementation of the set method is abstracted away.
It's like accelerating a "car" object either by a sequence of public set methods:
- car.setFuelMixture(...)
- car.setFuelPumpRate(...)
- car.setThrottleAngle(...)
- car.Go()
rather than by using a "task"-oriented method:
- car.Accelerate(TargetVelocity)
Shouldn't the complexity all be encapsulated (and hidden) inside the object's code, and not all exposed as public methods or variables (therefore requiring the caller/client to both understand all the public "internals" of the object plus setting all of them to the correct values and in the right order)?
puzzled by get/set methods
Friday, March 5, 2004
I usually use a lot of getters/setters, but I always try to make them private, since they often expose your implementation.
This is not very relevant in C. However, if I had to program in C, I'd like to have a grep return all the modifications of a "member" : hence the set() method. Except if a modern IDE can find the affectations for me, which is not very likely for C.
Pakter
Friday, March 5, 2004
Puzzled, I'd say you're right: 'Accelerate' is the simpler interface, which the Car class should expose to its auto-pilot UI.
Even so, low-level implementation classes (such as FuelPump) might want to provide get/set accessor methods; for example, FuelPump.set_rate(int) might be better than exposing FuelPump.m_rate because set can do things like verify that the parameter value is within a sensible range (and because its set method would also needs to call a low-level hardware driver).
Christopher Wells
Friday, March 5, 2004
One thing I have very commonly found get/set handy is when the data internal to the class does not precisely reflect the get/set methods
For example, imagine you had a class which represents something which takes a long time to store/retrieve, e.g a file, some complex data structure, some results of complex calculations etc.
Say m_y and m_z are the result of some really slow calculation from m_a and m_b
Consider something like:
class SlowClass
{
private:
double m_a, m_b ;
public:
void SetAB( double a, double b )
{
if ( ( m_a != a ) || ( m_b != b ) )
{
m_a = a ;
m_b = b ;
}
}
double GetY() const
{
// some calculation to get m_y
return m_y ;
}
double GetZ() const
{
// some calculation to get m_z
return m_z ;
}
} ;
vs:
class FastClass
{
private:
double m_a, m_b ;
mutable double m_y, m_z ; // cached values of y and z
mutable BOOL m_bYZUpToDate ;
void UpdateCache()
{
if ( !m_bYZUpToDate )
{
// some complex calculation that takes ages to calculate m_y and m_z from m_a and m_b
m_y = ...etc..
m_z = ...etc...
m_bYZUpToDate = TRUE ;
}
}
public:
FastClass()
{
m_bYZUpToDate = FALSE ;
}
void SetAB( double a, double b )
{
m_a = a ;
m_b = b ;
m_bYZUpToDate = FALSE ;
}
double GetY() const
{
UpdateCache() ;
return m_y ;
}
double GetZ() const
{
UpdateCache() ;
return m_z ;
}
} ;
Now the FastClass potentially wins on performance in several ways:-
1, If a user calls GetY and GetZ repeatedly without doing SetAB, you are doing a whole lot less calculations
2. If you do a SetAB and set it to the same values as you already had, then you don't waste time redoing the same calculation
3. The user of FastClass doesn't really have to worry about using the class in an efficient way, because efficiency is largely self-contained within the class. A user of SlowClass does, because they there is a potential to use the class in a very wasteful way (see points 1 and 2).
Okay, this is just a stupid example, but several points follow:-
(a) The same principle also applies not to calculations, but writing to database, file, complex memory data structure, etc.
(b) In principle you could start with SlowClass and optimized it to FastClass, and it wouldn't have any effect on the code using the class. Omission of the get/set methods, however would be a different kettle of fish.
S. Tanna
Friday, March 5, 2004
Typo in FastClass
first SetAB should be:
void SetAB( double a, double b )
{
m_a = a ;
m_b = b ;
}
second SetAB should be:
void SetAB( double a, double b )
{
if ( ( m_a != a ) || ( m_b != b ) )
{
m_a = a ;
m_b = b ;
m_bYZUpToDate = FALSE ;
}
}
S. Tanna
Friday, March 5, 2004
I may have caused some confusion with my choice of "puzzled by get/set methods" as a moniker. I definitely agree that a get/set method is preferable to directly setting the value of a member variable. My puzzlement is about exposing *either* style in a public interface. The original question was asking about get/set routines in libraries, and methods for manipulation by the "user" (implying from client code).
If the FuelPump class has a ".set_rate(int)" method, shouldn't that still be private (or semi-private) inside the "Car" object and not exposed as part of any public (or library) interface? [After all, you don't want to unintentionally call FuelPump.set_rate(0) just after you have called car.Accelerate(100)!]
puzzled by get/set methods
Friday, March 5, 2004
I was thinking that set_rate would be public in the FuelPump interface; and that FuelPump could be a private class inside the Car class, or a public class at global scope.
The FuelPump member would be private data in the Car class.
The Car class may or may not expose a public method to interact with FuelPump-specific functionality (depending on how 'rich' you want Car's interface to be).
Car is a "user" (or "client") of the FuelPump class, and Driver would be a user of the Car class.
Christopher Wells
Friday, March 5, 2004
I'd consider an accelerate method to be on a "business" logic object -- like in a car crash simulator, your domain objects are cars, barriers, and roads. The user wants to accelerate and move things around. That's where you'd have an accelerate method that abstracts away how it's actually accelerated, because the user doesn't care. So the abstraction is at the Accelerate() level.
In a customer service app though, your domain objects are customer records, because the user wants to pull up, modify, then resave the records. They don't care where or how they're stored, so in that case you abstract at the GetName/PutName level.
So it depends on the app!
Rick
Friday, March 5, 2004
I would use a separate .Validate() function to validate the internal state of an object, and only run functions if the state is correct.
the problem with sets/gets of single values is that if the values are dependent of each other than you cannot set it with one command without corrupt the internal state.
eg.
firstname, lastname should be mandatory, so then you code two getters, to setters. in the setters you check for non emptyness.
the rules changes, so only one is mandatory. now you cannot write
a.firstname = ''
b.lastname = 'y'
because after set firstname your object state will be invalid.
so i prefer calling a validate() after the setters, and this validate can check any logical dependencies within that object.
Monday, March 8, 2004
Recent Topics
Fog Creek Home
|