3 ms·
>the default case is that you want everything public I'd say that's only true for objects whose purpose is to merely group semi-related data together for copy/
by kgeist 5y ago
>the default case is that you want everything public
I'd say that's only true for objects whose purpose is to merely group semi-related data together for copy/transfer/storage, nothing more. If your object has invariants/rules, it's a completely different story.
>legitimate uses of "private" exist, but are rare
So if you have a domain object with certain invariants (and any serious project has plenty of those), who makes sure its invariants are always upheld? If everyone is allowed to put whatever they want inside your object, it's easy to end up with inconsistent/corrupt data. Ideally, you want all the logic which deals with object mutations and their validation to be concentrated in one single place, and force all clients to use it, to avoid copypasting same code every time you mutate an object (which is error-prone). That single place can be the object itself, in the constructor or the setters, with fields made private (or const) to defend against misuse. Of course, you can validate object state in a separate function and treat your objects as just dumb bags of data, but then we aren't talking about OOP anymore (where the argument "private vs. public" actually makes sense).
- yakubin 5y agoI've partially responded to that already[1][2]. Let me add to that, that when you group together data, a lot of invariants are preserved by the logic you already wrote for the types you compose your types of. Most basic grouping of objects is a struct. It's a product type. Often what I want really is the product of all the states of the component types, without eliminating any combinations. And in the case of sum types: I practically always want all the states. All the invariants are already handled in the types being summed. Not for transfer/storage, but because it logically makes sense. Another thing is that "make invalid state unrepresentable" is a good guideline, but it can lead you into a corner, where you write a lot of code just to make sure that your type system proves that a certain error is not representable by a type, when this error is trivial, unlikely and would take seconds to debug at runtime (maybe would even be so obvious, as to not need any debugging at all). Just because a certain invariant is true in a domain, doesn't mean that it needs to be encoded in the type system. We need to choose what's the most productive use of our time as developers. Those are trade-offs. I do not deny the usefulness of ADTs (which is what "private" is all about). I question whether it's the right default. My experience tells me it isn't. Whether you call my style of programming "OOP", is irrelevant to me. This term, after decades of evolution, doesn't mean anything anymore. I certainly don't treat it as a badge of quality and don't consult any definition of it before judging whether a particular technique in programming is useful or not. [1]: <https://news.ycombinator.com/item?id=30112738 https://news.ycombinator.com/item?id=30112738> [2]: <https://news.ycombinator.com/item?id=30113932 https://news.ycombinator.com/item?id=30113932>