Do you enforce Getters / Setters?

RE:

At work they're required, but not allowed to contain any logic. They feel pretty useless.

I think the main use cases are:

  • Allow internals to change without affecting the API (what is an attribute now may not always remain like that).
  • Allow different visibility for read and write, i.e. public getter and protected or non-existent setter.
  • (Technically you could override getters/setters but not attributes, but that seems like a horrible idea in almost any case)

I don't think they should always be used, but sometimes the benefits apply. Some of my opinions:

  • Some classes are just intended to transfer data (value objects, data classes). Public attributes make sense in that case.
  • In general when an attribute X is an implementation detail that you want to encapsulate, getX and setX are not genius names. Describe the behavior, not the storage layout.
  • Various languages have various kinds of sugar for this. Some dynamic ones allow replacing an attribute by a property (without needing ()), like Python @property. Some allow one to attach functions to properties, or even to names that don't have a storage representation. Some call setters when you do x.a = 7 (Kotlin). I generally like this, though it does hide potentially expensive operations.
  • The vast majority of attributes should be immutable, or at least not publicly mutable. So be conservative with setters. Private attribute + getter is a good way for internally-mutable data.
  • If attributes should be publicly mutable, then getters should protect the internal state. Validations are absolutely appropriate in setters.
  • If multiple attributes are interdependent with only some combinations allowed, consider making setting them an atomic operation by only having a method that (validates and) updates all of them.
  • If an attribute is mutable (e.g. list), then a getter should return an immutable view of it. Don't do library.get_books().add_all(my_books). The library should be responsible for validating it's collection of books.
  • If you have a policy of having no logic in getters/setters, probably just don't have getters and setters.
  • It might be wise to not put heavy computations inside getters (or setters), as this surprises people (and they shouldn't have to read the code, encapsulation, remember?).
  • Some people adhere to command/query separation, which I think would suggest not mixing getting and setting (i.e. in Rust-terms, any &mut self method should return nothing). I am not decided about this.
  • I'm hoping most static languages aggressively inline setters and getters so that performance is not important for getters/setters without logic. I might be wrong about that.

So to summarize: prefer a language that has some sugar for getters/setters, try to describe behaviour instead of data, use 'getters'/'setters' to protect validity of internal state, and prefer immutability.

Reply to this…

(3 answers) Take me to the question