16

I am not looking for an opinion about semantics but simply for a case where having getters sensibly used is an actual impediment. Maybe it throws me into a never-ending spiral of relying on them, maybe the alternative is cleaner and handles getters automatically, etc. Something concrete.

I've heard all the arguments, I've heard that they're bad because they force you into treating objects as data sources, that they violate an object's "pure state" of "don't give out too much but be prepared to accept a lot".

But absolutely no sensible reason for why a getData is a bad thing, in fact, a few people argued that it's a lot about semantics, getters as fine per-se, but just don't name them getX, to me, this is at least funny.

What is one thing, without opinions, that will break if I use getters sensibly and for data that clearly the object's integrity doesn't break if it puts it out?

Of course that allowing a getter for a string that's used to encrypt something is beyond dumb, but I'm talking about data that your system needs to function. Maybe your data is pulled through a Provider from the object, but, still, the object still needs to allow the Provider to do a $provider[$object]->getData, there's no way around it.


Why I'm asking: To me, getters, when used sensibly and on data that is treated as "safe" are god-sent, 99% of my getters are used to identify the object, as in, I ask, through code Object, what is your name? Object, what is your identifier?, anyone working with an object should know these things about an object, because nearly everything about programming is identity and who else knows better what it is than the object itself? So I fail to see any real issues unless you're a purist.

I've looked at all the StackOverflow questions about "why getters / setters" are bad and though I agree that setters are really bad in 99% of the cases, getters don't have to be treated the same just because they rhyme.

A setter will compromise your object's identity and make it very hard to debug who's changing the data, but a getter is doing nothing.

17
  • 2
    My advice is forget most of the OO theory. Practice. Write a lot of code, and then maintain it. You'll learn far, far, far more about what works well and what doesn't from actually doing something and then going back to it a few months later.
    – jpmc26
    Commented May 5, 2019 at 22:31
  • 1
    @jpmc26 What in the mother of .... I never actually realized that none of us actually do OOP correctly, I've always had this notion of encapsulation strictly relating to an object's fields, but the object itself is a state and is being freely shared. Genuinely everyone is doing OOP wrong, then, or rather, OOP is not possible if the paradigm is objects. I use OOP strictly to express how a system works and never treated it as the holy grail, nor SOLID. They're just tools I use mostly to define a reasonably well-expained (through implementation) of my concept. Any reads on if this is right?
    – coolpasta
    Commented May 6, 2019 at 1:24
  • 2
    --cont, The more I write, the more I realize is programming is all about being able to abstract as much as it's required in order to be able to reason, to others, as well to you about your codebase. Of course, the technical side of this is making sure you do the proper checks / set the right rules. In truth, whoever manages to make others understand their code better wins. Make sure it makes sense to anyone reading it, then make sure it doesn't fail by properly segmenting objects with interfaces, checks and switch to other paradigms when it's easier to reason with them: be flexible.
    – coolpasta
    Commented May 6, 2019 at 1:27
  • 4
    This seems like a strawman to me. I don't believe any serious developer who actually writes usable code would argue that using getters sensibly is a problem. The question implies getters can be used sensibly which further implies getters can be sensible. Are you looking for someone to say there is no sensible use of getters? You'll be waiting for a while.
    – JimmyJames
    Commented May 7, 2019 at 20:58
  • 2
    @coolpasta You're right. I don't really care if it's pure OOP. Just better. Give me a choice between broken code that can be understood by humans vs flawless CPU loving code that is indecipherable and Ill take the human friendly code every time. OOP is ONLY useful to me when it reduces the number of things I have to think about at any one time. That's the only reason I like it over straightforward procedural. Without that you're just making me jump through multiple source code files for no reason. Commented May 8, 2019 at 2:19

6 Answers 6

25

You can't write good code without getters.

The reason why isn't because getters don't break encapsulation, they do. It isn't because getters don't tempt people to not bother following OOP which would have them put methods with the data they act on. They do. No, you need getters because of boundaries.

The ideas of encapsulation and keeping methods together with the data they act on simply don't work when you run into a boundary that keeps you from moving a method and so forces you to move data.

It's really that simple. If you use getters when there is no boundary you end up having no real objects. Everything starts to tend to the procedural. Which works as well as it ever did.

True OOP isn't something you can spread everywhere. It only works within those boundaries.

Those boundaries aren't razor thin. They have code in them. That code can't be OOP. It can't be functional either. No this code has our ideals stripped from it so it can deal with harsh reality.

Michael Feathers called this code fascia after that white connective tissue that holds sections of an orange together.

This is a wonderful way to think about it. It explains why it's ok to have both kinds of code in the same code base. Without this perspective many new programmers cling to their ideals hard, then have their hearts broken and give up on these ideals when they hit their first boundary.

The ideals only work in their proper place. Don't give up on them just because they don't work everywhere. Use them where they work. That place is the juicy part that the fascia protects.

A simple example of a boundary is a collection. This holds something and has no idea what it is. How could a collection designer possibly move the behavioral functionality of the held object into the collection when they have no idea what it's going to be holding? You can't. You're up against a boundary. Which is why collections have getters.

Now if you did know, you could move that behavior, and avoid moving state. When you do know, you should. You just don't always know.

Some people just call this being pragmatic. And it is. But it's nice to know why we have to be pragmatic.


You've expressed that you don't want to hear semantic arguments and seem to be advocating putting "sensible getters" everywhere. You're asking for this idea to be challenged. I think I can show the idea has problems with the way you've framed it. But it also think I know where you're coming from because I've been there.

If you want getters everywhere look at Python. There is no private keyword. Yet Python does OOP just fine. How? They use a semantic trick. They name anything meant to be private with a leading underscore. You're even allowed to read from it provided you take responsibility for doing so. "We're all adults here", they often say.

So what's the difference between that and just putting getters on everything in Java or C#? Sorry but it's semantics. Pythons underscore convention clearly signals to you that you're poking around behind the employees only door. Slap getters on everything and you loose that signal. With reflection you could have stripped off the private anyway and still not have lost the semantic signal. There simply isn't a structural argument to be made here.

So what we're left with is the job of deciding where to hang the "employees only" sign. What should be considered private? You call that "sensible getters". As I've said, the best justification for a getter is a boundary that forces us away from our ideals. That shouldn't result in getters on everything. When it does result in a getter you should consider moving the behavior further into the juicy bit where you can protect it.

This separation has given rise to a few terms. A Data Transfer Object or DTO, holds no behavior. The only methods are getters and sometimes setters, sometimes a constructor. This name is unfortunate because it's not a true object at all. The getters and setters are really just debugging code that give you a place to set a breakpoint. If it wasn't for that need they'd just be a pile of public fields. In C++ we used to call them structs. The only difference they had from a C++ class was they defaulted to public.

DTO's are nice because you can throw them over a boundary wall and keep your other methods safely in a nice juicy behavior object. A true object. With no getters to violate it's encapsulation. My behavior objects may eat DTO's by using them as Parameter Objects. Sometimes I have to make a defensive copy of it to prevent shared mutable state. I don't spread mutable DTO's around inside the juicy part within the boundary. I encapsulate them. I hide them. And when I finally run into a new boundary I spin up a new DTO and throw it over the wall thus making it someone else's problem.

But you want to provide getters that express identity. Well congrats you've found a boundry. Entities have an identity that goes beyond their reference. That is, beyond their memory address. So it has to be stored somewhere. And something has to be able to refer to this thing by it's identity. A getter that expresses identity is perfectly reasonable. A pile of code that uses that getter to make decisions that the Entity could have made itself is not.

In the end it's not the existence of getters that is wrong. They are far better than public fields. What's bad is when they are used to pretend you're being Object Oriented when you're not. Getters are good. Being Object Oriented is good. Getters are not Object Oriented. Use getters to carve out a safe place to be Object Oriented.

15
  • This is exactly why I asked and I'm not sure if my conversation is visible with Robert, I was also trying to find out a specific case where it clearly hurts me to use "sensible getters". I, for myself disagree with setters because at least for myself, it's very hard to attribute ownership to an action like this, I used to use setters in everything and by the time I realized that even 1 setter, when affected by too many objects kills me...I had to re-write everything. Sensible use of getters, after exhaustive testing for so long, seriously have no down-sides if you're not a purist.
    – coolpasta
    Commented May 5, 2019 at 13:23
  • 1
    @coolpasta you can create data owner objects. Data owner objects are data objects, but they are designed and named so that they (clearly) own the data. Of course these objects will have getters.
    – rwong
    Commented May 5, 2019 at 14:47
  • 1
    @rwong What do you mean by clearly? If data is passed to me from something, by value, clearly, my object now owns the data, but by semantics, it yet doesn't, at this point, it simply got the data from someone else. It then must perform operations to transform that data, making it its own, the moment the data is touched, you must make semantic changes: You got data named as $data_from_request and now you operated on it? Name it $changed_data. You wish to expose this data to others? Create a getter getChangedRequestData, then, ownership is clearly set. Or is it?
    – coolpasta
    Commented May 5, 2019 at 15:41
  • 1
    "You can't write good code without getters." This is utterly wrong, as is the notion that boundaries inherently need getters. This is not pragmatism, just laziness. It is quite a bit easier to just throw data over the fence and be done with it, thinking about use-cases and designing a good behavioral api is hard. Commented May 5, 2019 at 17:58
  • 2
    Excellent answer! Commented May 5, 2019 at 21:47
13

Getters violate the Hollywood Principle ("Don't call us, we'll call you")

The Hollywood Principle (aka Inversion of Control) states that you don't call into library code to get things done; rather, the framework calls your code. Because the framework controls things, broadcasting its internal state to its clients is not necessary. You don't need to know.

In its most insidious form, violating the Hollywood Principle means that you're using a getter to obtain information about the state of a class, and then making decisions about which methods to call on that class based on the value that you obtain. it's violation of encapsulation at its finest.

Using a getter implies that you need that value, when you actually don't.

You might actually need that performance improvement

In extreme cases of lightweight objects that must have the maximum possible performance, it's possible (though extremely unlikely) that you can't pay the very small performance penalty that a getter imposes. This won't happen 99.9 percent of the time.

14
  • 1
    I understand and I'll fly with your truth, that I don't need to know.. But I've reached a spot where I need to. I have a Generator object that loops through all my Items objects, then calls getName from each Item to do something further. What is the issue with this? Then, in return, the Generator spits out formatted strings. This is within my framework, for which I then have an API on top of that people can use to run whatever the users provide but without touching the framework.
    – coolpasta
    Commented May 5, 2019 at 0:40
  • 5
    What is the issue with this? None that I can see. That's essentially what a map function does. But that's not the question you asked. You essentially asked "Are there any conditions under which a getter might be inadvisable." I replied with two, but that doesn't mean you abandon setters altogether. Commented May 5, 2019 at 0:45
  • 1
    You're asking the wrong guy that question. I'm a pragmatist; I do whatever best suits my specific programs, and don't put much stock in "principles" unless they serve my purposes. Commented May 5, 2019 at 0:46
  • 3
    @jpmc26: Frameworks. Not libraries. Commented May 6, 2019 at 1:07
  • 2
    A framework is an attempt to turn a general purpose language into a domain specific one. A library is a collection of reusable algorithms. Either one will ask you to depend on it. It's up to you if you want to hard code either dependency or make the dependency easily substitutable. Commented May 8, 2019 at 2:37
2

Getters Leak Implementation Details and Break Abstraction

Consider public int millisecondsSince1970()

Your clients will think "oh, this is an int" and there will be a gazillion calls assuming that it is an int, doing integer math comparing dates, etc... When you realize that you need a long, there will be a lot of legacy code with problems. In a Java world, you'd add @deprecated to the API, everybody will ignore it, and you are stuck maintaining obsolescent, buggy code. :-( (I assume other languages are similar!)

In this particular case, I'm not sure what a better option might be, and the @candiedorange answer is completely on point, there are many times when you need getters, but this example illustrates the downsides. Every public getter tends to "lock" you into a certain implementation. Use them if you truly need to "cross boundaries", but use as little as possible and with care and forethought.

7
  • This is true, but what is the pattern solution to enforcing a data type / structure to an object's variable, as well to anything that uses the getter for that variable?
    – coolpasta
    Commented May 5, 2019 at 15:45
  • 2
    This example illustrates a different phenomenon, primitive obsession. Nowadays most libraries and frameworks have classes that represent timepoint and timespan respectively. (epoch is a particular value of timepoint.) On these classes, they provide getters such as getInt or getLong or getDouble. If classes that manipulate time are written so that they (1) delegate time-related arithmetics to these objects and methods, and (2) provide access to these time objects via getters, then the problem is solved, without needing to get rid of getters.
    – rwong
    Commented May 5, 2019 at 17:46
  • 1
    To summarize, getters are part of the API, and therefore getters need to be designed with sufficient considerations to all consequences, including past, current, and future ones. But that doesn't lead to a conclusion of avoiding or minimizing the number of getters. In fact, if there is a future need to access a particular piece of information for which a getter was omitted, it would have required a library-side API change and code change. Thus, the decision as to whether a particular getter should be implemented or avoided should be based on the domain and intent, not on preventive reason.
    – rwong
    Commented May 5, 2019 at 18:02
  • 1
    "millisecondsSince1970" stopped working for 32 bit ints before the end of January 1970, so I doubt there will be much code using it :-)
    – gnasher729
    Commented May 6, 2019 at 15:31
  • 1
    @rwong Somewhat correct, but you assume that the preferred libraries that represent timepoints are stable. Which they aren't. E.g. moment.js
    – user949300
    Commented May 6, 2019 at 16:41
1

I think the first key is to remember that absolutes are always wrong.

Consider an address book program, which simply stores people's contact information. But, let's do it right and break it up into controller/service/repository layers.

There's going to be a Person class that holds actual data: name, address, phone number, etc.. Address and Phone are classes, too, so we can use polymorphism later on to support non-US schemes. All three of those classes are Data Transfer Objects, so they're going to be nothing but getters and setters. And, that makes sense: they're not going to have any logic in them beyond overriding equals and getHashcode; asking them to give you a representation of a full person's information doesn't make sense: is it for HTML presentation, a custom GUI app, a console app, an HTTP endpoint, etc.?

That's where the controller, service, and repository come in. All of those classes are going to be logic-heavy and getter-light, thanks to dependency injection.

The controller is going to get a service injected, which will expose methods like get and save, both of which will take some reasonable arguments (eg., get might have overrides to search by name, search by zip code, or just get everything; save will probably take a single Person and save it). What getters would the controller have? Being able to get the concrete class's name might be useful for logging, but what state will it hold other than the state that's been injected into it?

Similarly, the service layer will get a repository injected, so it can actually get and persist Persons, and it might have some "business logic" (eg., maybe we're going to require that all Person objects have at least one address or phone number). But, again: what state does that object have other than what's injected? Again, none.

The repository layer is where things get a little more interesting: it's going to establish a connection to a storage location, eg. a file, an SQL database, or an in-memory store. It's tempting, here, to add a getter to get the filename or the SQL connection string, but that's state that's been injected into the class. Should the repository have a getter to tell whether or not it's successfully connected to its data store? Well, maybe, but what use is that information outside of the repository class itself? The repository class itself should be able to attempt to reconnect to its data store if need be, which suggests that the isConnected property is of dubious value already. Further, an isConnected property is probably going to be wrong just when it would most need to be right: checking isConnected before attempting to get/store data doesn't guarantee that the repository will still be connected when the "real" call is made, so it doesn't remove the need for exception handling somewhere (there are arguments for where that should go, beyond the scope of this question).

Consider also unit tests (you are writing unit tests, right?): the service isn't going to expect a specific class be injected, rather it's going to expect that a concrete implementation of an interface is injected. That lets the application as a whole change where the data is stored without having to do anything but swap out the repository being injected into the service. It also allows the service to be unit tested by injecting a mock repository. This means thinking in terms of interfaces rather than classes. Would the repository interface expose any getters? That is, is there any internal state that would be: common to all repositories, useful outside of the repository, and not injected into the repository? I'd be hard-pressed to think of any myself.

TL;DR

In conclusion: aside from the classes whose sole purpose is to carry data around, nothing else in the stack has a place to put a getter: state is either injected or irrelevant outside of the working class.

2
  • I find this line of thinking very similar to the debate about the proper use of C# properties. In short, properties (which can be getter-only or getter-setter-pair) are expected to uphold a certain contract, such as "what you set is what you get", "getter should be mostly side-effect-free", "getter should avoid throwing errors", etc. Contrary to your conclusion, there are many object states that are beneficial to be exposed as properties (or getters). Examples are too numerous to quote here.
    – rwong
    Commented May 5, 2019 at 17:55
  • 2
    @rwong I would be interested to know some examples, let's say for a "normal" "enterprise" application implemented by one team. Let's also assume for simplicity's sake there are no third parties involved. You know, a self-contained system, like a Calendar, Pet shop, whatever you want. Commented May 5, 2019 at 18:49
1

What is one thing, without opinions, that will break if I use getters sensibly and for data that clearly the object's integrity doesn't break if it puts it out?

Mutable members.

If you have a collection of things in an object that you expose via a getter, you are potentially exposing yourself to bugs associated with the wrong things being added into the collection.

If you have a mutable object that you are exposing via a getter, you're potentially opening yourself up to the internal state of your object being changed in a way that you do not expect.

A really bad example would be an object that is counting from 1 to 100 exposing its Current value as a mutable reference. This allows a third party object to change the value of Current in such a way that the value could lie outside the expected bounds.

This is mostly an issue with exposing mutable objects. Exposing structs or immutable objects isn't a problem at all, since any changes to those will change a copy instead (usually either by an implicit copy in the case of a struct or by an explicit copy in the case of an immutable object).

To use a metaphor that might make it easier to see the difference.

A flower has a number of things which are unique about it. It has a Color and it has a number of petals (NumPetals). If I'm observing that flower, in the real world, I can clearly see its color. I can then make decisions based on that color. For example, if color is black, do not give to girlfriend but if color is red, give to girlfriend. In our object model, the color of the flower would be exposed as a getter on the flower object. My observation of that color is important for actions that I will perform, but it does not impact at all on the flower object. I should not be able to change the color of that flower. Equally, it does not make sense to hide the color property. A flower generally cannot prevent people from observing its color.

If I dye the flower, I should call the ReactToDye(Color dyeColor) method on the flower, which will change the flower according to its internal rules. I can then query the Color property again and react to any change in it after the ReactToDye method has been called. It would be wrong for me to directly modify the Color of the flower and if I could then the abstraction has broken down.

Sometimes (less frequently, but still often enough that it's worth mentioning) setters are quite valid O-O design. If I have a Customer object it is quite valid to expose a setter for their address. Whether you call that setAddress(string address) or ChangeMyAddressBecauseIMoved(string newAddress) or simply string Address { get; set; } is a matter of semantics. The internal state of that object needs to change and the appropriate way to do that is to set the internal state of that object. Even if I require a historical record of addresses that the Customer has lived in, I can use the setter to change my internal state appropriately to do so. In this case it does not make sense for the Customer to be immutable and there is no better way to change an address than to provide a setter to do so.

I am not sure who is suggesting that Getters and setters are bad or break object oriented paradigms, but if they do, they're probably doing so in response to a specific language feature of the language that they happen to use. I get the feeling that this grew out of the Java "everything is an object" culture (and possibly extended itself to some other languages). The .NET world doesn't have this discussion at all. Getters and Setters are a first class language feature that are used not only by people writing applications in the language, but also by the language API itself.

You should be careful when using getters. You do not want to expose the wrong pieces of data and nor do you want to expose data in the wrong way (i.e. mutable objects, references to structs that can allow a consumer to modify internal state). But you do want to model your objects so that the data is encapsulated in such a way that your objects can be used by external consumers and protected from misuse by those same consumers. Often that will require getters.

To summarize: Getters and Setters are valid object-oriented design tools. They're not to be used so liberally that every implementation detail is exposed but nor do you want to use them so sparingly that an object's consumers cannot use the object efficiently.

-1

What is one thing, without opinions, that will break if I use getters...

Maintainability will break.

Any time (I should say almost always) you offer a getter you basically give away more than was asked of you. This also means that you have to maintain more than was asked, ergo you lower the maintainability for no real reason.

Let's go with @candied_orange's Collection example. Contrary to what he writes a Collection shouldn't have a getter. Collections exist for very specific reasons, most notably to iterate over all items. This functionality is not a surprise to anyone, so it should be implemented in the Collection, instead of pushing this obvious use-case on the user, using for-cycles and getters or whatnot.

To be fair, some boundaries do need getters. This happens if you really don't know what they will be used for. For example you can programmatically get the stacktrace from Java's Exception class nowadays, because people wanted to use it for all sorts of curious reasons the writers didn't or couldn't envision.

11
  • 1
    Counterexample. Consider a function that needs to iterate two Collections simultaneously, as in (A1, B1), (A2, B2), .... This requires an implementation of "zip". How do you implement "zip" if the iterate-over function is implemented on one of the two Collection - how do you access the corresponding item in the other?
    – rwong
    Commented May 5, 2019 at 18:21
  • @rwong The Collection has to implement zip with itself, depending how the library is written. If it doesn't, you implement it and submit a pull request to the library creator. Note that I agreed that some boundaries do need getters sometimes, my real problem is getters are everywhere nowadays. Commented May 5, 2019 at 18:28
  • In that case, that implies that the library has to have getters on the Collection object, at least for its own implementation of zip. (That is, the getter has to have package visibility at least.) And now you depend on the library creator accepting your pull request ...
    – rwong
    Commented May 5, 2019 at 18:49
  • I don't quite understand you. The Collection obviously can access its own internal state, or to be more precise any Collection instance can access any other's internal state. It's the same type, no getters needed. Commented May 5, 2019 at 18:53
  • I hear you too, but then again, what exactly is the better solution to getters? I know it's out of the scope of the question.
    – coolpasta
    Commented May 5, 2019 at 19:25

Not the answer you're looking for? Browse other questions tagged or ask your own question.