
Avoid getters and setters whenever possible - ingve
https://dev.to/scottshipp/avoid-getters-and-setters-whenever-possible-c8m
======
ninkendo
I have trouble articulating this point, but I never understood the argument
why you’d want getters or setters at all.

From my point of view, your data structures _are_ your API. Attempting to hide
them behind methods so that you can do something clever doesn’t work when it’s
not always you that constructs the data (I’m thinking of things like json
deserialization and accepting data from outside your control.).

All the arguments behind getters and setters seem rooted in this fear that
maybe you’ll change your mind later about your data, but somehow you won’t
have to change the function signatures of your getters/setters when that
happens... this seems so contrived to me it’s insane to design entire
frameworks and paradigms with this as a central idea.

Maybe I’ve been using Go for a bit too long now but I really think the mixing
of data and logic is a fundamental mistake.

~~~
acdha
I’m inclined to this position as well but consider cases where you need values
to be restricted or linked. A setter would allow that to happen reliably every
time without changing your API to require validation calls before doing
anything else.

Getters definitely seem less likely to have cases like that unless there’s
some sort of ordering contract you need to enforce.

How valuable this is will come down to audience: if it’s a small team which is
very familiar with the app, there’s considerably less value than a large app
with a huge team or a library intended for other people, especially non-
experts.

~~~
ninkendo
I guess I don’t think that way.

Having classes where values are “restricted” doesn’t make many sense in my
mind. It’s a flaw in your type system if invalid values are possible... proper
algebraic typing with pattern matching would do an infinitely better job at
this.

And linked values are more smell to me... it’s a sign you have a bad
abstraction if two properties have to be set in tandem or your object becomes
invalid. Can you think of a good example for purposes of discussion?

~~~
acdha
A somewhat contrived example but say you had a user address with state and zip
code values and you wanted to prevent setting a zip code outside of that
state. Or with any kind of state change - e.g. certain statuses are only valid
for paid accounts, you can only archive verified files, etc.

That’s not saying you couldn’t have an API which is set, set, set, validate()
but there’s an aesthetic argument for early enforcement so invalid values can
never be set.

Type systems can’t solve everything but I’d agree that it’s not surprising
that you see these design patterns most in languages like Java which didn’t
have a better way.

~~~
ninkendo
I love this response because it's helping me formulate my objections a little
better.

I don't think a User class should be directly validating the zip lives inside
the state in some sort of setter method... which one do I set first to make it
work? It looks like once I change one I can't change the other any more... I'd
need to have a method that changes both at once. And this is a perfect example
of your data not matching your encapsulation: your encapsulation would want to
express that you need to provide a zip/state pair via some setLocation(zip,
state) method, which does not correspond to any private property you actually
have.

But I definitely would prefer the set,set,set,validate approach more, and I
would even say the validate() belongs in a different class altogether, ideally
as a side-effect-free standalone method. (Basically, I should be able to set
an invalid state and zip property in a vacuum, it's only when I go to validate
it that it returns invalid.)

Going further I would implement any form submission system as taking some
Validatable interface, where the Validatable object in this case would store
your Address object via composition, and would represent the specific logic
for validation for these purposes... but it would all live separately from a
User struct, which would just be a dumb struct.

~~~
acdha
> I don't think a User class should be directly validating the zip lives
> inside the state in some sort of setter method... which one do I set first
> to make it work? It looks like once I change one I can't change the other
> any more... I'd need to have a method that changes both at once.

The idea I had in mind for that admittedly contrived example was roughly that
you'd have an address class and some logic along the lines of setting a field
automatically clears the lower-level fields, but that's not really a practical
design as much as an illustration of the pattern of having complex logic in a
setter.

I should note that this isn't my preference, either – either
set,set,set,validate or updateLotsOfFields(dataStructure) – but I've seen
people who felt otherwise and had reasonable arguments for this kind of
behaviour.

------
laurent123456
This is strange advice. There's no real drawback from using getter/setter and
there are obvious benefits.

And even if you mostly use public properties, there will still be cases where
you'll need getter/setter so you'll end up with a messy mix of getter/setter
and public properties. Better be consistent and always use getter/setter.

~~~
LandR
Getters, yes.

I would argue that everything that can be immutable should be immutable though
unless you have good reason for it to be mutable.

~~~
davewritescode
Agreed but immutability can lead to builders and builders lead to lots of code
bloat. Plus, from what I've experienced lots of frameworks depend on java bean
conventions (i.e. getters/setters for everything).

There's really two missing features of java that could solve 90+% of the use
cases for getters/setters.

\- Attributes: annotation or keyword based directives for the compiler to
generate getters/setters for a given instance variables

\- Default + named parameters which would reduce a lot of the pain of multiple
constructors and constructors with a tremendous number of parameters.

~~~
phkahler
Instead of attributes that lead to creation of getters/setters, how about the
simple notion of private or public access to members? I'm not sure which
should be the default, but it sounds like Java programmers would like them
private unless otherwise specified. Why add code when all you want is to
restrict access?

------
meirelles
For Java exists the Project Lombok, it works pretty well for this purpose, you
can use getters and setters without polluting the code with auto-generated
stuff. Have some other cool features like fluent accessors, builders, auto
cleanups for close() and plugins for IDEs. IMHO every Java programmer should
check it out.

~~~
absove
My favorite is immutables
([https://immutables.github.io/](https://immutables.github.io/)). It's really
frictionless and at this point it feels like an extension of the language to
me. It also lets you fall back to mutable types when you need them, despite
the name.

------
simion314
I disagree with the author, getters and setters are a good tool to have and
used in the right place.

Setters are great when you want to do some validation and add an entry in a
log when the setter is used.

There are rules what not to do in getters or in setters so if you use does
practices you are ok.

~~~
chopin
I try to avoid setters whenever possible in favor of constructors taking the
dependency as parameter. That way, reasoning about the lifecycle of
dependencies is much easier.

~~~
simion314
I agree, if you can make the object imutable by setting all it's fields at
construction time then it is great, if you have to update the object then is
good to use setters or functions that can perform validation, it makes it easy
to catch errors earlier.

------
jacques_chester
Overlooked is how they became so ubiquitous: JavaBeans. It was well-meant, but
it's left a lot of unintended pain in its wake (I'm looking at _you_ , JPA
constructors). Without that standard, Java code might feel quite differently
today.

While you can avoid making it worse with idioms like only using constructor
injection, you can't unwrite the billions of lines written to date.

Kotlin makes a great effort to atone for the sins of the past.

~~~
sgift
> While you can avoid making it worse with idioms like only using constructor
> injection, you can't unwrite the billions of lines written to date.

But you can fix them. One line at a time. So many years of atonement still to
do ...

------
CodeArtisan
To me, objects is a data abstraction technique which is about abstracting the
said data under a set of operations known as the interface; You are no more
thinking about the data but about the operations which then allows all kind of
interface abstraction techniques known as design patterns. Once you break that
abstraction, you break everything which is relying on.

Even for small classes where there is no design pattern involved, it is
important to think about the interface first then the data. take for example,
a rectangle, you may think (g/s)etters would be pointless and be tempted to do

    
    
      class Rectangle:
        public x, y, w, h;
        
        public Rectangle(x, y, w, h):
          this.x = x;
          ...
    

Then you need the area

    
    
      class Rectangle:
        ...
        public area():
          return w * h;
    

Then you need to cache the area

    
    
      class Rectangle:
        public x, y, w, h, cached_area;
    
        public Rectangle(w, h):
          this.w = w;
          this.h = h;
          cached_area = w * h;
    
        public area():
          return cached_area;
    

Problem here is that w and h being public, user can corrupt the area, you now
need to rely on (s/g)etters.

    
    
      class rectangle
        ...
        private w;
      
        public setWidth(w):
          this.w = w;
          updateArea();
          
        private updateArea():
          cached_area = w * h;
    

If you had start with an interface only having operations, you would never had
that problem.

    
    
      interface Rectangle:
        getX();
        setX(x);
        getWidth();
        setWidth(w);
        area();
        ...
    
      // implementations are irrelevant to the user.
      class RectangleCached implements Rectangle:
        ...
    
      class RectangleLazy implements Rectangle:
        ...
    
      class RectangleWhatever implements Rectangle:
        ...
    

_One of the great leaps in OO is to be able to answer the question "How does
this work?" with "I don't care"_ (Alan Knight)

Note that another solution in this case would be immutability but this is
another discussion.

------
barrkel
The argument for getters and setters is much stronger when you're writing
libraries that are dependencies for projects you don't control.

If you own all consumers of your code, and it's all part of the same project,
then you can safely refactor from field access to getters and setters if and
when you need to add extra semantics. If you own the consumers but they're not
all part of the same project, you can refactor but it will take longer and be
a multi-step exercise.

If you don't own the consumers, then you need to break compatibility, which
means major version bumps, which will leave some downstream consumers on older
versions, and will probably increase version divergence in your userbase,
fragmenting the ecosystem and increasing your support load.

~~~
benmmurphy
I think it is kind of insane not to recompile when your downstream
dependencies change and the recompilation fixes this problem. Like presumably
it makes sense to run your tests again when you change downstream dependencies
and it is extra work to run tests independently of recompilation so I think
most people's process would be update dependency -> recompile -> run tests. I
just remember Java having lots of gotchas with binary compatibility and I
assume C# has similar issues. It's just easier to not depend on binary
compatibility and always recompile.

I'm guessing this is a result of 'insane' processes where you have people
upgrading dependencies in deployed applications without properly going through
a test process.

(EDIT: oops i thought this was a c# post. but this change in java breaks
source compatibility as well as binary compatibility)

~~~
jacques_chester
My understanding is that C#'s properties were designed pretty much exactly for
this situation.

------
sgift
The first point:

> (...) My question is how often has the working programmer ever had to do
> that? I don't remember ever doing this in all my years of software.

has that hollow ring of "I write my software the way it doesn't allow easy
changes, so I don't do them, so there's no reason to do it." ... there
probably would've been reason to do it more than once, the author just didn't
want to do it because it would've been painful.

Anyway: If you can make your class work without exposing anything then go
ahead. That's great. But if you have to allow external access a getter will
always be better than direct access.

p.s.: Setter means mutable class. Mutable classes are a performance hack.
Sometimes you have to do it, most of the time it's premature optimization.

~~~
sakuronto
> Mutable classes are a performance hack.

Could you please clarify this point? I have no idea what you're trying to say.

~~~
jacques_chester
It's often much faster to modify something in place than to create a modified
copy.

Suppose I have:

    
    
        Point point = new Point(50, 50);
        int x = 100;
        int y = 200;
        Point translatedPoint = point.plus(x, y); // new Point(this.x + x, this.y + y)
    

This code suggests that Point is immutable. Once defined, a point never
changes. This makes a lot of sense -- if you change the values of x and y, you
have a _different_ point, in a mathematical sense.

But newing up an object is _tremendously_ slower than integer arithmetic.
Suppose I can do this:

    
    
        Point point = new Point(50, 50);
        point.translateX(100); // this.x += x
        point.translateY(200); // this.y += y
        Point translatedPoint = point;
    

This is going to lead to translatedPoint much more quickly than the first
code. But it's riddled with troublesome outcomes. Who else was relying on
point? What happens if I change it again? Why am I able to mutate something
that, mathematically, isn't mutable?

The basic reason mutability improves performance is that you can change things
in-place without making a new copy. Most of the stuff being done during the
copy construction is overhead compared to the thing you're changing. The price
might be too high in a performance-sensitive situation.

There are workarounds based on using copy-on-write datastructures with clever
shared bits, but they're not native idioms in Java-land or in most languages
without a strong functional heritage.

------
watwut
I prefer getters setters over Car1 example especially for large classes. I am
quite used to use prefix to filter for methods in IDE - get<thing>,
add<thing>, set<thing>, create<thing>, is<thing>, to<thing>, etc. Often you
know which one of those subgroups you are interested in and can do ctrl-o or
ctrl-space and filter irrelevant options out. The worst option of all is when
there are both getters/setters and public fields.

But most importantly, the real practical differences between these styles are
minimal for all practical purposes. It is all mostly matter of habit. You
might as well argue about which shade of blue is best for syntax highlighting.

------
nwatson
Some Java frameworks let class and interface authors use decorators on getters
/ setters to control how particular class/interface fields are used in some
contexts ... e.g., one might have a field (due to an algorithmic
consideration) in an entity that's useful when constructing POJOs (plain-old
Java objects) that eventually are written to a database, but that particular
field should should not be persisted to the database -- one can use decorators
to control whether field goes to database, or the data type used to persist
the field in the database, etc. It's sometimes useful to treat the getter and
setter asymmetrically.

------
osullivj
IMHO getters & setters can be a bad smell, symptomatic of classes that are
just dumb aggregations of data that have no significant behaviour. I've seen
them most often in Java - a single paradigm lang that forces every method or
data item to be a class member. And then, because our data item is a class
member, it must be encapsulated. Multi paradigm langs offer better approaches
to data aggregations. Python, for instance, gives us dictionaries and named
tuples as well as classes.

~~~
larzang
Generic data types aren't necessarily a better approach. My primary language
for many years has been PHP, where the default approach used to be just to
pass generic maps around for all data, and it was hell.

You cannot tell if a map passed to you has any of the members you expect,
hasn't had additional unexpected members added, or anything else about it
without parsing it, it's a mystery bag. There are times when a mystery bag is
fine and you're going to be inspecting it anyway, but most of the time your
code has at least some expectations about the structure of your data, and for
that you need real specific types to maintain any kind of sanity. Otherwise,
you're re-validating data over and over or writing code that relies on hope
and faith instead of correctness.

~~~
spdionis
Why use actual objects and rely on static anlysis when you can write tens of
unit tests that ensure the maps/tuples passed around always contain the
correct data structures? _And_ you get the added benefit of 100% test
coverage.

~~~
gruez
now you have to add (manual) type checks in your tests instead of your
compiler doing it for you

~~~
spdionis
You probably didn't get the sarcasm :).

------
mannykannot
I have sometimes wondered why none of the mainstream OO languages implemented
an access class of publicly readable but only privately modifiable.

~~~
akmarinov
Does Swift count?

private(set) is a thing

~~~
mannykannot
Sure, Swift counts - thanks for the correction. Maybe those with experience of
it could comment on whether they found this feature useful in practice.

------
sigi45
I prefer actions over getter and setters. The object should take care of
itself (ddd style).

But xml and db to object mapper often need them :(

------
klmr
tl;dr: “Don’t use getters and setters” shouldn’t be a goal to strive for. Good
OO design should be the goal, and the reduction of getters and setters ( _and_
of public data members) is a consequence of that.

I fundamentally agree with the author: Getters and setters often break _Tell,
don’t ask_ , increase the public interface of a class (and expose
implementation details), and add mutable state, which makes reasoning about
the program’s state harder — for getters, this is often mitigated by defensive
copying, which has its own set of issues (it can introduce _glaring_
inefficiencies in the code base, which, in many cases, could have been
entirely avoided).

The extreme case is when getters and setters lead to quasi-classes [1]. All
this is uncontroversial when you think about it (and is backed by a vast
consensus in the literature and by what little evidence we have in evidence-
based software engineering). On the other hand, I agree with the author that
accessors are sometimes a practical solution and shouldn’t be banned entirely.

 _But_ I don’t think the author states the case well at all. There’s too much
talk about _feelings_ , which is a poor substitute for arguments. This starts
with the first example that’s provided: Contrary to what the article claims,
there’s of course a difference between `Car1` and `Car2`. Namely, `Car2` can
add logic to its accessors without creating introducing API changes. `Car1`
cannot do this: If I for instance need to ensure that `engine` is never set to
`null` by the user, or I need to add logging, or I need to add deferred
loading, etc, I need to make `engine` `private` and add public accessors.

Future-proofing isn’t always relevant but it unfortunately tends to become
relevant unexpectedly. Anyway, neither example is probably very good code and,
conversely, either piece of code can be appropriate. But they’re emphatically
not the same.

I also don’t think the “list of options” at the end is particularly useful
because it seems to be a substitute for properly thinking about the
architectural design of the code (which doesn’t need to happen up-front, it
can happen iteratively): Many accessors are simply never necessary and were
instead added “just in case”, or because the programmer thinks about objects
as “bags of data” instead of, more appropriately, “[black] boxes with a
specific observable behaviour”. This simple change of perspective will
drastically reduce the number of public accessors in a code base; but this
happens as a side-effect of better OO design, rather than a goal in itself.

[1]
[http://www.idinews.com/quasiClass.pdf](http://www.idinews.com/quasiClass.pdf)

