Hacker News new | past | comments | ask | show | jobs | submit login

My impression of ECS from a distance is that it is antithetical to data-hiding.

That there are good reasons in terms of memory layout to locate all of those values which would have been member variables in large arrays so that external functions can iterate over them efficiently, but that in so doing, private members become impossible.

What am I missing?




You aren't missing anything. But you also aren't asking yourself the meta-question "what is the point of data hiding?"

The answer to that meta-question is "to preserve assumed relationships between data." `std::vector` stores a capacity, and that capacity damn well better correspond to the length of the last array it allocated or you will get a segmentation fault. So you hide it.

The OO thing that ECS is pushing back against is a tendency to group things together solely because they are in one-to-one correspondence, when they are otherwise unrelated. In a game, there is no constraint that needs to satisfied between a player's HP and their position on the map. So why "hide" this data together in an aggregation?

If you want to permit only a few functions to operate on your big array 'o stuff, you can always do something like

    class sensitive {
      static allowed_operation(sensitive& s, context& c);
    private:
      double here[N];
      int be;
      bool dragons;
    };
Similarly, if you need to do some really complicated computation on everything about a player, you can define Objects as aggregations of indices into the arrays 'o stuff (where typically people think of an Object as a big-ass struct of structs, often implicitly due to Inheritance), where the data you care about hiding is not the stuff from the array, but the indices:

    class Player {
      GetComponent() { return arrComponent[index]; }
    private:
      size_t index;
    };


Thanks for the examples shoehorning data hiding into ECS! They are revealing even if only appropriate in esoteric circumstances.

> But you also aren't asking yourself the meta-question "what is the point of data hiding?"

My answer to that question has always been that data hiding is necessary for the sake of modular independence in large systems. This is a principle which applies across all of engineering, not just software — see the "starter motor" example elsethread.

Implementation details need to stay hidden so that you need only concern yourself with local effects when making changes — instead of needing to keep the entire system in your head because any change might impact any tiny detail anywhere at all.

Nevertheless, I agree that in some circumstances it makes sense to expose the data structure as an API, and that ECS offers a compelling approach and set of conventions as to how you would go about that.


> My answer to that question has always been that data hiding is necessary for the sake of modular independence in large systems.

Yes. But not on a granularity of every object. Just like you don't put a padlock on every pocket, to protect your left hand from grabbing stuff from a right pocket.

Hiding data has a real cost, just like inventing abstractions and interfaces for every tiny thing. That's the core reason why OOP-software is so bloated and always feels so "heavy".

The right granularity is much coarse: modules, API layers, data-stores. Much closer to service in "micro-service", than "object".


You are right, it's shoehorning. That's because data-hiding just isn't as much of a concern in ECS.

In ECS the things you talk about are achieved differently.

In ECS, implementation details are local to the systems that process the relevant data. You don't store implementation details in the data you pass to the higher abstraction.


That's exactly the point. This whole data-hiding thing has way too many downsides.

If you consider your app a machine that transforms A to B, you don't want data-hiding in the first place. You want to be honest with the data you have and data you need.

Also in most web apps: Your data will escape your precious objects (mostly as JSON).

And don't confuse data-hiding with having objects in a consistent state (i.e. mutating data). Two different things.


> That's exactly the point. This whole data-hiding thing has way too many downsides.

The usual argument against OOP that I hear is that implementation inheritance is brittle. That, I understand and agree with.

But data hiding being bad? I'm not persuaded. The whole divide-and-conquer aspect of breaking modules into smaller parts requires not exposing unnecessary details as interfaces.

You have a starter motor in a car. If it's only connected to the motor via a couple wires, then it's easy to design a better one and install it. But if it has dozens of wires connecting to every which part of the engine, improving it is much harder.

> Also in most web apps: Your data will escape your precious objects (mostly as JSON).

My "precious objects"? :( Can we please discuss this subject without getting into a flamewar?


Sorry, forgot this wasn't reddit.

Well, will your data escape or not? As long as you are staying in OOP-land, everything is as it should be: Objects passing messages to each other, as politely as possible (trying not to bring in the concurrency aspect)

But then, there comes a REST endpoint around and wants your data. Now it is out in the open and subject to inspection and change. So what have you gained by using data-hiding? Shouldn't you be able to pass that data around openly in the rest of your code as well?

Maybe I shouldn't say data. I should say values. Immutable ones. Values can be passed around safely. They can be inspected by anyone. Accessed by multiple threads etc. That allows for abstractions that are impossible to achieve with stateful objects.

In OOP we keep our state hidden in the objects, have methods manipulate it, but that means these object can't be a value. They can change anytime. You can't even observe those state transitions (unless you code for them, which gave us bean-style properties...)

To stay with your motor example: If you assemble a motor from values and start it, it will continue to work, even if someone installs a new part. You can't break the motor.

You can even save the motor to disk at any time, load it back and you still have a working motor.

Yes, all of this could be achieved by using objects talking to each other, but it is vastly more difficult (try to snapshot a consistent state of an object graph, for example)


> The whole divide-and-conquer aspect of breaking modules into smaller parts requires not exposing unnecessary details as interfaces.

ECS takes a fundamentally different approach. OOP is about breaking things down, ECS is about building things up.

Like in ECS, it's possible to dynamically toggle whether an entity has a particular field or not.


Data-hiding is a language feature but I do get your point. I'm not sure if I really need data-hiding.

In your example, would some of the values be private while others public? Like in ECS, they are either all private or all public which kinda makes sense.


For the sake of loose coupling, which allows modules to be improved in large systems without having to keep the entire large system in your head, data hiding is necessary as a general engineering principle.

From my perspective, ECS looks like an optimization where you make the engineering tradeoff to sacrifice data hiding. For very good reasons! Memory locality is super important when iterating over elements the way it is typically done in ECS, such as when rendering objects in a game.


The way you do things in ECS is that you query all things that have particular field. As a result, you don't really need to remember what type do the field you are operating on really have, you are operating on the columns of a db, not the row of a db.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: