5 ms·
Personally, I have come to the conclusion that object-level encapsulation is not a good design principle, but rather an antipattern because it complects data wi
by taffer 5y ago
Personally, I have come to the conclusion that object-level encapsulation is not a good design principle, but rather an antipattern because it complects data with code [1].
OOP tries to manage global mutable state by partitioning and encapsulating it into objects. However, the only way to prevent two unrelated objects from manipulating the same part of the state is to create a tree, i.e., a strict hierarchy between all objects in the system. This combination of data and code in a strict hierarchy means that all data access patterns are baked into this dependency tree, making later, unforeseen changes to the software extremely difficult without taking shortcuts in the dependency tree or having to refactor the entire application.
If, on the other hand, you treat your data as just data, preferably flat and immutable, and keep the code that acts on it separate, then you won't run into this problem. You will be able to change the parts of the code that act on the data structures independently.
[1] See Rich Hickey, Simple Made Easy: https://www.youtube.com/watch?v=oytL881p-nQ https://www.youtube.com/watch?v=oytL881p-nQ
- ChrisMarshallNY 5y agoAgreed, but, for me, I am not really a data programmer. I tend to work in state and identity (classic UI and communications). For these, that characteristic is actually an advantage. Nowadays, centralized data processing is the big deal for software engineering (as it was, fifty years ago). I'm just a humble app developer, and I'll license stuff that real data programmers do, if I need it.
- urthor 5y agoExactly, the biggest single advantage of OO is the ability to create and manipulate insanely complicated data structures simply and easily. Because it's a way to define a whole bunch of methods AND assign all aspects of thinking about the state of the data to those methods to the author... it's a brilliant tool for designing some insane complexity. Which is exactly what you need for MVC paradigms. There are multiple tools and they are for different jobs, imagine that.
- vendiddy 5y agoI've programmed in OOP for many years and recently have been using a functional language at work. I don't see a major in advantage to coupling data and methods because something similar can be accomplished based on how you organize the code. If you put all the functions in one file and the data structures in another file, things become quite a bit more flexible to extension and composition.
- ChrisMarshallNY 5y agoProbably true for data processing programming. The thing about UI programming (and a lot of comms programming, too), is that the ability to completely abstract all the factors relevant to an entity is pretty important. Good UI is very complicated. As someone above pointed out, it can be insanely complicated. Comms programming is basically the same thing. It's pretty much a requirement to be able to abstract all the particulars of an interface behind an identity/state wall. I used to write procedural programs that ran an entire GUI (and device control), and OO was like a gift from Santa. Applications that used to take weeks, and were bug farms, suddenly took days, and hardly had any bugs. We can, arguably, do without inheritance (the part of OO that everyone loses their bottle over), but that encapsulation is pretty damn important. This means that we can add a button to an interface with a few drags on an interface description screen, and a copy/paste (if we're eschewing inheritance) of a simple class or struct definition. I like inheritance, though. It helps me to refactor the code down to a fairly manageable (and debuggable) scope, speeds up development, and helps me to keep quality very high. It's like everything. We need to be good at what we do, and have a good command of our tools; whatever they are. FP is certainly not for the faint of heart. The tech industry is absolutely obsessed with taking almost completely unskilled, junior, developers, and getting them to produce release-quality code, unassisted by experienced architects. Vast resources have gone into trying to make this work. It is not new. It has been going on for as long as I have been in the field. And it always ends in tears.
- mytailorisrich 5y agoOne of the aims of OO design is specifically to make future and unforeseen changes easier by hiding data and enforcing interfaces. If you treat your data "as just data" with free for all access you revert to the mess that led to the emergence of OO design. This is much worse for maintainability. > keep the code that acts on it separate, then you won't run into this problem. You will be able to change the parts of the code that act on the data structures independently That's exactly what should happen with OO (that's one of the purposes of encapsulation).
- lincpa 5y agoAre you anti-DB?
- taffer 5y agoOO aims at making future and unforeseen changes easier, I just don't think it achieves its aim. I am also not sure that a discussion on HN is the best medium to discuss the problem in depth. Nevertheless, I will try to give a practical example: Suppose you have parts (part_id, description, quantity_on_hand) and suppliers (supplier_id, name). Also, each part is manufactured by multiple suppliers and each supplier manufactures multiple parts. How do you model this? Do you let parts reference suppliers or suppliers reference parts, or do both reference each other? Or do you define a third class PartsSuppliers that manages the references? There is no formal method in OO that tells you what is a sound design choice and what is not. Let's say you chose the latter option (PartsSuppliers) and you need to write a method that computes statistics about the parts. Where do you place this method? You need to add it to PartsSuppliers, because no one else is allowed to have private references to Parts, otherwise you would break PartsSuppliers' encapsulation. No matter what design decisions you make in OOP, you will always have to make a tradeoff between encapsulation of state and extensibility.
- mytailorisrich 5y ago> There is no formal method in OO that tells you what is a sound design choice and what is not. System architecture is hard. OO design is a set of principles that helps you design a system better by making it easier to maintain and modify. It does not tell you how you should model your objects. To come up with a good model is usually not straightforward. In fact your example is not an issue specific to OO design. This is a general issue of relationships ('many to many') and there are a number of design principles to help (see database design principles as that's a typical scenario in databases). > Where do you place this method? You need to add it to PartsSuppliers, because no one else is allowed to have private references to Partts That's not true, but as you say, this is too vast a discussion.
- baryphonic 5y ago> However, the only way to prevent two unrelated objects from manipulating the same part of the state is to create a tree, i.e., a strict hierarchy between all objects in the system. I see this claim from time to time, and perhaps it's true in typical Java, C++ or even Python, but I don't think I've ever seen anything close to a proof of it. I suspect it is false, since an interface boundary that leaks no state is possible to implement and can give freedom to the designer regarding internal state. > If, on the other hand, you treat your data as just data, preferably flat and immutable, and keep the code that acts on it separate, then you won't run into this problem. You will be able to change the parts of the code that act on the data structures independently. This claim is true only insofar as the underlying state being tracked doesn't change much through the lifecycle of the program in development, which is a reasonably good assumption for a game, but not for most other applications. The problem is that "data" by definition does not capture all of its own invariants. I have personally witnessed long-lived codebases suffer from brittleness when multiple areas of code must read from and manipulate the same underlying data structure. Inevitably, some programmer on the team forgets one of the invariants, since they aren't specified in code (which would make it OO), and then we have a production bug. The solution is then usually to add another "if" statement somewhere. The solution thus makes the code harder to understand and thereby increases the likelihood of this kind of bug related to this particular structure recurring.
- taffer 5y ago> [...] an interface boundary that leaks no state is possible to implement and can give freedom to the designer regarding internal state. Your suspicion is justified. As long as the objects only send immutable messages to each other, encapsulation remains intact. But then you have something closer to an actor system than what people typically think of when they say OOP. Once you pass references to mutable objects, all bets are off. The ability to specify which states are allowed and which are not, is not a special feature of OOP. In the functional and relational paradigms, there are types and constraints that specify in a declarative way what states should be possible. Types and constraints are enforced by the runtime and are not based on (leaky) encapsulation.
- 5y ago