Do you enforce Getters / Setters?

Do you enforce the usage of them? Why would you say that they are better style than public fields?

public class Metadata {
    private String id;

    // ...

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    // ...
}

// ...

Metadata myData = new Metadata();
myData.setId("abc");

vs.

public class Metadata {
    public String id;
    // ...
}

// ...

Metadata myData = new Metadata();
myData.id = "abc";
Write your answer…

3 answers

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…

Share your programming knowledge and learn from the best developers on Hashnode

Get started

The getter setter logic esp in the java language originates in the bean concept to allow discoverability.

to me the question of value objects vs variable objects is very important. it's along the lines of Mark a value object should be immutable and can have public reads.

As soon as we go for variable objects I kinda differ from his point of view where a getter and setter still should only contain set and get logic to define the communication of change but the idea of having explicit validation logic in a data object .... I would rather prefer validation decorators along the lines of Design By Contract or what mark mentioned with python decorators.

Although I would rather prefer a Validator outside so I can compose the logic. This is a mental design question.

So the main problem of mutation is control of change and public means no control which also makes it harder for the compiler to reason. (depends on the language)

The problem of coupling logic to your data is as always context and polymorphism / subtyping. It's a language specific type-cohesion concepts where in one classification certain rules apply and not in the other.

Which can lead to bloat in the getters / setters. But than lets be real for a second ... it's very unlikely outside an algebraic concept that those problems arise and it really depends on the language concept.

Please do correct me if I am wrong. I can think of some cases where the previous statement isn't valid.

In PHP for example I insist on them for example because I cannot enforce scalar types on fields (yet).

Also the access for the debugger does not work on properties so the loss of 20% performance for function method call deference is a price I am willing to pay to ensure that my code:

  • has a predictable message + mutation pattern
  • has enforced types by a membrane

although I hate the bloat of getters and setters they are a constraint in this particular language design.

The getter and setter problem solution in dart for example is interesting, if you don't access a property it's private as soon as you do it's public.

I think this also helps the compiler to do certain optimizations since it's based on behaviour.

In an OO approach that is about messaging, mental models and mutation a getter and setter can be perfectly reasonable. It depends heavily on the language, the concept and the goals.

Everything else would be imitation and not understanding and if you only imitate it sounds more like religion than reason :)

Show all replies

Mark yes it kinda is but the compiled code and the public API is different since dart gets transpiled to javascript and those fields are not exposed there. :D

Reply to this…

I haven't used a current version of IBM's WSAD/RAD (whatever they call it today) or IntelliJ IDEA for about 10 years. Since retiring to no more than temp, part-time freelance, I use nothing but Eclipse Classic (Java JEE version) with plugins for a few special, extra things related to UIE, PHP, and version control. However, back in my corporate days (when dinosaurs walked the Earth) the defaults for both WSAD and IntelliJ were to provide TODO auto-stubs for both getters and setters any time you declared a variable in class scope. I note those platform defaults by way of observing that the JEE aspects embedded in the assumptions of the IDEs' creators may have unduly influence developers to think of public mutators and accessors as non-optional.

I think Mark got it right. If a field/variable can, well, vary under manipulation by its parent or close relatives, but should be protected from direct manipulation by strangers, then you offer a getter. MAYBE you provide a non-operational setter to make clear to other developers that there should be no mutation allowed by external calls (I think lack of setter documents that just as well, but I don't write the coding bible for every shop).

There are cases in which public setters are entirely reasonable and correct. Make sure yours is one of those cases before creating that setter.

Bottom line, I can go either way with no strong (pun intended - <strong>strong</strong>) leaning, but I want there to be a shop-wide bible about these sorts of "standards" and lightning strikes upon heretics. At least locally, let's all be on the same page.

Reply to this…