Hacker News new | ask | show | jobs
by DonaldPShimoda 2618 days ago
In Kay's OO the only way to interact with an object was through method passing. It was important the the internal state of an object was kept private at all times.

Getters/setters are technically message-passing methods, but they undermine the design goal because they more or less directly expose internal state to the public world.

But we see getters/setters used constantly. People don't use OO in the way Kay intended. Yes, methods are the implementation of the whole "message passing" thing Kay was talking about, but we see them used in ways he did not intend.

5 comments

In my experience, getter/setter abuse is always an attempt to use classes as a structs/records.

I wonder if we had different syntax for those cases we'd have less of them.

But then, again, it's very convenient to be able to add a method to a class that was previously a dumb struct.

Maybe I am a complete philistine but is that really a bad thing or just something which goes against their categorism? I get that there are some circumstances where setters would break assumptions but classes are meant to be worked with, period.
Objects are meant to have a life cycle in which the state should only be changed by the object itself. Setters violate this idea by allowing the sender of the message direct control over the state of the object.

A simplistic example: account.deposit(100) may directly add 100 to the account's balance and a subsequent call to account.balance() may answer 100 more than when account.deposit(100) was called. But those details are up to that instance of the account not the sender of those messages. The sender should not be able to mutate account.balance directly, whether it be via direct access to the field or through the proxy of a setter.

Well... a setter is the object changing its own state. That's why the setter has to be a member function.

I would say instead that an object shouldn't have setters or getters for any members unless really necessary. And by "necessary", I don't mean "it makes it easier to write code that treats the object as a struct". I mean "setting this field really is an action that this object has to expose to the external world in order to function properly". And not even "necessary" because I coded myself into a corner and that's the easiest way out I see. It needs to be necessary at the design level, not at the code level.

It depends, most of the time is better to have separate functions that transform your data rather than have methods and state conflated together. But obviously it depends from the context.
Yeah there are no hard and fast rules but a lot of time transformations can be in the object as well. If I need a function to transform Foo to Bar I could just as easily send a toBar() message to an instance of Foo.
I think c# really got the best of both worlds with extensions methods, where you can actually define functions that act on a object but are separated from the actual class definition. I still think that pure functions and especially higher kinded types are better probably, although I have no direct experience with Haskell type classes, scala implicits and ocaml modules..
It's not exactly a bad thing, it's just that you're using a hammer (class) when what you actually need is a screwdriver (struct/record).

Abusing getters/setters is breaking encapsulation (I said abusing, light use is ok). If you're just going to expose all the innards of the class, why start with a Class?

The whole point of object orientation to put data and behavior together. That's probably the only thing that both the C++/Java and the Smalltalk camp agrees on.

Separating data and the behavior into two different classes breaks that. You're effectively making two classes, each with "half of a responsibility". I can argue that this breaks SRP and the Demeter principle in one go.

Another thing: Abuse of getters/setters is often a symptom of procedural code disguised as OOP code. If you're not going to use what is probably the single biggest advantages of OOP, why use it at all?

-

Here's an answer that elaborates on this that I like:

https://softwareengineering.stackexchange.com/questions/2180...

> The whole point of object orientation to put data and behavior together

May I politely disagree based on my long-ago experience with dylan, which has multi-methods (<https://en.wikipedia.org/wiki/Multimethods>). This allowed the action on the data (the methods) to be defined separate from the data. I strongly feel that it was OO done right, and it felt right. You can read about it on the wiki link but it likely won't click until you play with it.

I'd like to give an example but it's too long ago and I don't have any to hand, sorry.

It’s a different semantic in my opinion. Even in mutable objects it’s better to have setters that act only on the field that they are supposed to mutate and do absolutely nothing else. If you need a notification you can raise an event and then the interested parties will react accordingly. By mutating directly an unrelated field, or even worse, call an unrelated method that brings complete havoc to the current object state, in the setter you are opening yourself to an incredible amount of pain.
I disagree, slightly. A setter (or any method, for that matter) has to keep the object in a consistent state. If it can't set that one field without having to change others, then it has to change others.

Now, if you want to argue that an object probably shouldn't be written in the way that such things are necessary, you're probably right. And if you want to argue that it should "just set the one field in spirit" (that is, that it should do what it has to to set the field, but not do unrelated things), I would definitely agree with you. But it's not quite as simple as "only ever just set the one field".

> Getters/setters are technically message-passing methods, but they undermine the design goal because they more or less directly expose internal state to the public world.

No, they don't, because “more or less” is not actually directly. Particularly, naive getters and setters can be (and often are) replaced with more complex behavior with no impact to consuming code because they are simply message handlers, and they abstract away the underlying state.

> No, they don't, because “more or less” is not actually directly.

I disagree.

Consider a `Counter` class, intended to be used for counting something. The class has one field: `Counter.count`, which is an integer.

A setter/getter for this field would be like `Counter.setCount(i: Int)` and `Counter.getCount() -> Int`. There is no effective difference between using these methods and having direct access to the internal state of the object.

A more "true OOP" solution would be to use methods with semantic meaning, for example: `Counter.increment()`, `Counter.decrement()`, and `Counter.getCount() -> Int`. (Yes, the getter is here because this is a simple example.) These kinds of methods are not directly exposing the internal state of the object to be freely manipulated by the outside world.

If your getter/setter does something other than just get/set, then it's not really a getter/setter anymore — it's a normal method that happens to manipulate the state, which is fine. But using getters/setters (in the naive, one-line sense) is commonplace with certain people, and I feel that their use undermines the principles Kay was getting at.

I have seen side effects for completely unrelated fields in setters. Heck, I’ve even witnessed side effects in bloody getters. This is the reason why now I’m a huge fan of immutable objects. Actually nowadays I became a fan of functional languages with first class immutability support.
> but they undermine the design goal because they more or less directly expose internal state to the public world.

This has always been my problem with getters and setters. It's a way of either pretending you are not or putting bandaids on the fact that you're messing with the objects internal state. For objects with dynamic state this is really bad. The result is racy or brittle.

> Getters/setters are technically message-passing methods, but they undermine the design goal because they more or less directly expose internal state to the public world

If they do, that's your fault for letting them. I guess you mean when people chain stuff thus

company.programmers.WebDevs.employ('fred')

where .programmers and .WebDevs is an exposed internal of the company and programmers department respectively? (I've seen lots of this, and in much longer chains too. We all have). In which case please see the Principle of Demeter <https://en.wikipedia.org/wiki/Law_of_Demeter> which says don't do this. Wiki article is good.

I doubt any language can prevent this kind of 'exposing guts' malpractice, it's down to the humans.

I remember reading that Alan Kay said when he saw the Linda model (<https://en.wikipedia.org/wiki/Linda_(coordination_language)>) he said it was closer to what he wanted smalltalk to be.

> I doubt any language can prevent this kind of 'exposing guts' malpractice

Actually, true OOP languages do prevent this. Internal state is completely private and cannot be exposed externally. The only way to interact with an object's state is through its methods — which means the object itself is responsible for knowing how to manipulate its internal state.

Languages like Java are not "true" OOP in this sense, because they provide the programmer with mechanisms to allow external access to internal state.

Internal state should be kept internal. You shouldn't have a class `Foo` with a private internal `.bar` field and then provide public `Foo.getBar()` and `Foo.setBar()` methods, because you may as well just have made the `.bar` field public in that case.

Also, FWIW, I did not downvote you. I dunno why you were downvoted. Seems you had a legitimate point here, even if I disagree with it.

> Internal state should be kept internal.

I'm not sure that's a proven model. It's a proposed model, for sure. Since you can't protect memory from runtime access, you can't really protect state, so it's a matter of convention which Python cleverly baked in (_privatevar access).

Ah sorry, I was speaking in the context of Kay's OOP! In retrospect my phrasing made it seem like I was stating an opinion as fact, but what I meant was just that Kay's OOP mandated that internal state could not be exposed and was very opinionated on the matter.
Why downvoted? I don't mind being wrong but would like to know where and why.