To Hell With Setters and Getters

How Accessor Methods Are a Waste of Time and Often Hinder Encapsulation

Image for post
Image for post
Alan Kay who coined the term object-oriented programming was inspired by biological cells and how they communicated when thinking about encapsulation.

At at a former workplace I ask colleagues: “why exactly do we bother with setters and getters for our parameter classes?” The reason for the question was that we had classes used for serialization, where every variable had a setter and getter, but there was never any business logic in these classes.

Fortunately I was not put up against the wall and shot for heresy. But nor could anyone actually give a good reason for why we used setters and getters for every single attribute of dumb data objects.

I could of course have asked this question about Java classes, Python classes or almost any other object oriented language. Within software development there are certain beliefs which assume religious conviction.

People will criticize you for not using setters and getters with the argument that: Without setters and getters your class is not properly encapsulated. You make the poor class naked and vulnerable. Let us try to articulate the classic argument in favor of setters and getters:

If you don’t have setters and getters for the properties of your object, then you can’t change the internal data representation of that object without breaking code depending on that object.

The huge gaping logical hole in this argument is that advance refactoring tools are now prevalent everywhere. Replacing direct variable access with a setter or getter function is trivial. Hence what is the point of prematurely adding setters and getters to classes?

Even before widespread use of refactoring tools, this was not a significant problem. You could simply mark a variable as private, and the compiler would loudly tell you which places you performed direct member access. If you use a statically typed language such as C++ or Java that is.

Why Does It Matter?

Many will argue there is not cost to writing accessor methods (setters and getters) because modern IDEs can do it for us. Why am I making a stink about this?

Let us look at a simple example to illustrate the problem. We could write a simple class like this and be done:

struct Rocket {
int stages;
float fuel;
RocketEngine engine;
bool reusable;
};

Instead to be good object-oriented citizens we force ourselves to write stuff like this:

class Rocket {
public:
Rocket();
virtual ~Rocket();

int GetStages() const;
void SetStages(int stages);
float GetFuel() const;
void SetFuel(float fuel);
RocketEngine& GetEngine();
const RocketEngine& GetEngine() const;
void SetEngine(const RocketEngine& engine);
bool GetReusable() const;
void SetReusable(bool reusable);

private:
int stages_ = 0;
float fuel_ = 0.0;
RocketEngine engine_;
bool reusable_ = false;
};

Together with a corresponding implementation file:

Rocket::Rocket() {

}

Rocket::~Rocket() {
}

int
Rocket::GetStages() const {
return stages_;
}

void
Rocket::SetStages(int stages) {
stages_ = stages;
}

float
Rocket::GetFuel() const {
return fuel_;
}

void
Rocket::SetFuel(float fuel) {
fuel_ = fuel;
}

RocketEngine&
Rocket::GetEngine() {
return engine_;
}

const RocketEngine&
Rocket::GetEngine() const {
return engine_;
}

void
Rocket::SetEngine(const RocketEngine& engine) {
engine_ = engine;
}

bool
Rocket::GetReusable() const {
return reusable_;
}

void
Rocket::SetReusable(bool reusable) {
reusable_ = reusable;
}

So instead of 6 lines of code we end up with around 60 lines of code. 10x as much code to satisfy the OOP gods. That is not free, even if the IDE writes it all for you. It is 10x as much code you have to relate to, possibly read, reason about and maintain.

This is madness and fortunately the software development community has started to slowly realize this. Look at source code in Google’s Go programming language. They seldom use accessors. If you define a simple point type like this:

struct Point {
float x;
float y;
};

There is no need for accessors. You are not suddenly going to need to change this Point type into being defined in terms of polar coordinates. Although that is the sort of excuses people invent. Anyway a refactoring tool can add those accessors should you need them in the future. Never in my career have I changed a basic type like this after tons of code has been written.

Other languages such as Ruby, C# and Swift have done away with the need by simply making the syntax for accessing a property look the same as accessing a variable directly. Then you can add an accessor later without having to do any actual code change.

Ironically I see many people using such languages prematurely write code for accessor that don’t do anything, even if direct access has exactly the same outcome and looks syntactically identical. Which makes it hard to not wonder if the whole setter-getter thing is a religious cult.

Accessors Are Not Encapsulation

At the heart of the problem with accessors is that a majority of object-oriented developers seem to think that encapsulation is simply about making sure internal data is not accessed directly.

However that is mixing up a language feature aimed at building encapsulation with the concept of encapsulation. A common way to illustrate this is with e.g. a stack:

struct Stack {
void push(int value) {
values[++top] = value;
}

int pop() {
return values[top--];
}
private:
int top = -1;
int values[CAPACITY];
};

Notice how there are no setters and getters for the variables top and values. If we had added getTop() and setTop(index), that would not have promoted encapsulation. Quite the contrary we would have exposed a detail about how our stack type is implemented. We would have allowed users of our stack to change the top of the stack to a value which does not match the number of push() and pop() being called. Hence we would have broken invariants.

Had we added setValues() and getValues() accessors we would not have maintained encapsulation but exposed how values are stored in the stack.

The bottom line is that encapsulation is not about accessing internal variables through method calls, but hiding the internal structure and creating an interface which maintains the correct relationship between the internal variables.

Should You Ever Write Accessors?

By now, you might think that I am completely against ever using accessors. But that is not the point I want to make. My point is that OOP design should not be taught as stone tablet commandments which must be obeyed to satisfy the OOP gods.

No rule should be set in stone. You should be able to make the case for the choices you make. Here I will make the case for when to use accessors .

Libraries Used by Third Parties

If you’re code is a library used by others, then you are more likely to need encapsulation because you can’t do a simple refactoring of customer code. However in this case, simple encapsulation through accessors might not be enough, since you often want to maintain binary backwards compatibility.

In C++, if you change the internal state of your objects by adding or removing variables, then you risk changing the size of you’re objects. Say my Point class contains two integers for x and y coordinates. Imagine a customer has an array of these point objects, and I update my library by adding a z coordinate.

struct Point {
float x;
float y;
float z; // New variable added
};

If my library is dynamically linked at runtime and the customer has not recompiled his code, then x coordinate of the second point p2 will be a the position of the z coordinate of the first point p1:

Point points[3] = {p1, p2, p3};

Seen from that perspective, OOP encapsulation in C++ is rather weak. In e.g. Objective-C this is not a problem, as all objects are heap allocated, and you communicate with them through message passing. I suspect Java would not have this problem either, since anything which is not a primitive type is accessed through references (essentially pointers).

You Want to Limit Access

To start off with setter and getters which do nothing, is rather pointless. However it is quite sensible to make a variable private and only provide a getter. E.g. a collection usually has a size property. You don’t want the user of your collection to be able to arbitrarily change the size property unless the number of elements have actually changed. Hence you want size to be private and provide a getter method, say getSize().

One frequently wants to make immutable objects. It can be achieved by letting the user set the values of all its properties through the constructor. Later those properties can only be accessed by getters (no setters).

Your Type is Part of an Abstraction

In my programming language of choice, Julia, you tend to specify functions as taking abstract strings as arguments, because Julia supports many types of strings. Then you can’t access say a length variable directly, because the length of the string could potentially be calculated as needed, as with C style string, or it could be a stored variable as with Pascal. The algorithms the user writes should not have to care about this. Hence you need to provide a length() getter to abstract away this difference.

The same goes for collections. Their implementation could vary considerably but you still want to provide a standardized interface to determine the number of elements in the collection, iterating over them etc.

Conclusion

If you find yourself wondering whether you should write setters or getters, you got to ask yourself some questions about the code your are writing and the type (class) you are implementing. Is this for internal use only, or will customers see this type?

Is it a read-only property? If you don’t decide this up front there is no point in writing accessors as it won’t help you later. E.g. if you provided a setter, when the member-variable ought to be read only then your “encapsulation” bought you nothing.

Is your type a specialization of an abstract concept? E.g a file on Unix is quite abstract as it could represent files on a hardisk, a device, a process, in memory file etc.

Only when you have a sensible reason, should you actually write the setters and getters.

Written by

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store