Public variables bad practice vs Getters and Setters functions?
First of all, a struct
is completely equivalent to a class
, but with the default member access being public
rather than private
.
Now, in Object Oriented Programming (OOP), it's not considered good practice to have public
data members (variables), because that makes all your code dependent on the internals of the class
, and thus breaking a primordial principle of OOP, and that is...
Holy and Sacred Encapsulation
Encapsulation is the coding philosophy that states that a class should englobe both data and the code that manages it in a single tight entity. That is, you don't access data directy, but rather you use methods from the class
to manipulate such data. This has several design advantages, such as that you'll know that no code except the one inside the class may incorporate bugs with respect to the manipulation of such information.
Now, get()
ers and set()
ers, otherwise known as accessors, are a complete lie! With accessors, you're tricking yourself into thinking that you're respecting encapsulation, when you're rather breaking it! It adds bloat, unnecessary verbosity, bugs, and everything but encapsulation. Instead of having a class Person
with unsigned getAge()
and void setAge(unsigned)
, have it with a unsigned getAge()
and a void incrementAge()
or however you want to call it.
Now, to your question's core...
"Plain old" structs
Encapsulation is not always desired. Although you should (usually) not do this on header files (again, for at least some bit of encapsulation), you may create static plain old struct
s that are private to a single translation unit. My recommendation is to make them even "older" than they already are, i.e...
- All data members are
public
. - No methods.
- No constructors (except implicit ones).
- Inheritance is always public, and only allowed from other plain old
struct
s. - I repeat, don't put them on header files!
Now, another use for plain old struct
s is (ironically) metaprogrammatic exporting of constexpr
data and types, otherwise known as modern-hardcore-template-metaprogramming-without-having-to-type-public
-everywhere, for example...
template<bool B, typename T>
struct EnableIf {};
template<typename T>
struct EnableIf<true, T> {
typedef T type;
};
template<bool B, typename T>
using SFINAE = typename EnableIf<B, T>::Type;
In my experience people use getters/setters excessively for no good reason.
One can think of two major kinds of classes: the ones grouping together related data and the others providing behaviour.
Behaviour classes must be encapsulated with no public data members.
Data classes normally should have data members public and no behavior.
The grey area between these two is mutable data classes with invariants or dependencies between members, e.g. if member a
is 1, then member b
must be in range [1-10]
. For such cases usage of getters/setters may be justified. For immutable data classes the constructor must establish the invariant.
It's certainly difficult to control the internal consistency of your object if you make the data members public.
What works well is to use constructors to set up the state of an object, then use public functions to retrieve values of member variables, but only if that's needed. If you need to mutate the object after construction, then provide very specific methods for that purpose.
If, however, your object is no more than something that aggregates orthogonal data types, then use public access for all members: a struct
works well for that.
Note that the only difference between a struct
and a class
is that in the former, the default access is public
whereas in the latter it is private
.