Why use getters and setters/accessors?

Viewed 512776

What's the advantage of using getters and setters - that only get and set - instead of simply using public fields for those variables?

If getters and setters are ever doing more than just the simple get/set, I can figure this one out very quickly, but I'm not 100% clear on how:

public String foo;

is any worse than:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Whereas the former takes a lot less boilerplate code.

39 Answers

Thanks, that really clarified my thinking. Now here is (almost) 10 (almost) good reasons NOT to use getters and setters:

  1. When you realize you need to do more than just set and get the value, you can just make the field private, which will instantly tell you where you've directly accessed it.
  2. Any validation you perform in there can only be context free, which validation rarely is in practice.
  3. You can change the value being set - this is an absolute nightmare when the caller passes you a value that they [shock horror] want you to store AS IS.
  4. You can hide the internal representation - fantastic, so you're making sure that all these operations are symmetrical right?
  5. You've insulated your public interface from changes under the sheets - if you were designing an interface and weren't sure whether direct access to something was OK, then you should have kept designing.
  6. Some libraries expect this, but not many - reflection, serialization, mock objects all work just fine with public fields.
  7. Inheriting this class, you can override default functionality - in other words you can REALLY confuse callers by not only hiding the implementation but making it inconsistent.

The last three I'm just leaving (N/A or D/C)...

If you don't require any validations and not even need to maintain state i.e. one property depends on another so we need to maintain the state when one is change. You can keep it simple by making field public and not using getter and setters.

I think OOPs complicates things as the program grows it becomes nightmare for developer to scale.

A simple example; we generate c++ headers from xml. The header contains simple field which does not require any validations. But still as in OOPS accessor are fashion we generates them as following.

const Filed& getfield() const
Field& getField() 
void setfield(const Field& field){...} 

which is very verbose and is not required. a simple

struct 
{
   Field field;
};

is enough and readable. Functional programming don't have the concept of data hiding they even don't require it as they do not mutate the data.

There is a difference between DataStructure and Object.

Datastructure should expose its innards and not behavior.

An Object should not expose its innards but it should expose its behavior, which is also known as the Law of Demeter

Mostly DTOs are considered more of a datastructure and not Object. They should only expose their data and not behavior. Having Setter/Getter in DataStructure will expose behavior instead of data inside it. This further increases the chance of violation of Law of Demeter.

Uncle Bob in his book Clean code explained the Law of Demeter.

There is a well-known heuristic called the Law of Demeter that says a module should not know about the innards of the objects it manipulates. As we saw in the last section, objects hide their data and expose operations. This means that an object should not expose its internal structure through accessors because to do so is to expose, rather than to hide, its internal structure.

More precisely, the Law of Demeter says that a method f of a class C should only call the methods of these:

  • C
  • An object created by f
  • An object passed as an argument to f
  • An object held in an instance variable of C

The method should not invoke methods on objects that are returned by any of the allowed functions. In other words, talk to friends, not to strangers.

So according this, example of LoD violation is:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

Here, the function should call the method of its immediate friend which is ctxt here, It should not call the method of its immediate friend's friend. but this rule doesn't apply to data structure. so here if ctxt, option, scratchDir are datastructure then why to wrap their internal data with some behavior and doing a violation of LoD.

Instead, we can do something like this.

final String outputDir = ctxt.options.scratchDir.absolutePath;

This fulfills our needs and doesn't even violate LoD.

Inspired by Clean Code by Robert C. Martin(Uncle Bob)

From my experience, it is ideal to set variables as private and to provide accessors and modifiers to each variable.

This way, you can create read only variables, or write only variables depending on your requirements.

Below implementation shows a write only variable.

private String foo;
public void setFoo(String foo) { this.foo = foo; }
private String getFoo() { return foo; }

Below shows a read only variable.

private String foo;
private void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
Related