Thanks for providing a great example of useful getters. If you're computing a derived property that depends strictly on the data at hand and its attributes, getters are useful. When designing code, follow the Getter Idempotency Principle:
* Given a reference to a data entity, invoking a getter on the data should always return the same result, no matter how many times it is invoked, no matter how the rest of the program or world state evolve.
There are others situations. In those situations explicit code is preferable:
* Change an attribute of a data. Do not use a setter, period. Create a data copy with the attribute changed instead. Even GUI toolkits nowadays are using immutable data patterns with great success, see React.
* Use a data entity as a facade for reading data from a 3rd party API. Instead of using an object that pretends to be data but isn't via a heavy getter, use the 3rd party API explicitly to instantiate your immutable data graph.
* Use a data entity as a facade for updating a 3rd party API. Don't use a setter, instead create an updated copy of your data, then explicitly invoke the 3rd party API with the updated data copy.