Hah, this reminds me a lot of ECS (Entity-Component-System) architectures [1]
The first time I read about ECS I thought it was a completely crazy idea, but after trying it I realized that I was able to have extreme low-coupling and complete separation of concerns. It was great for hacking things, but in the long term it was also great for maintenance and code reuse! I never felt this way about OOP, ever.
In this case however it seems that the "Entity Management" part is ad-hoc, with Editors themselves putting things on the dictionaries. Ooops :)
EDIT: I think one can get a lot of mileage out of something like this if the dictionary management code is properly encapsulated in an isolated module/class. Just an idea.
ECS is a hodge-podge of concepts, each implementation mixing in different proportions some or all of the following: relational data model, data-oriented architecture, flexible recombination at runtime, removing the fixed inheritance graph, cache-friendly flow.
What's in the article is really closer to "relational data model" than all of these above. All these dictionaries are essentially implementing SQL selects. The "relational data model" lens to viewing ECS is that it's essentially giving each component its own table, and doing plenty of joins.
I find this really interesting, and my current side project is a roguelike game that focuses only on that part - relegating all game ECS into an in-memory SQLite database. I decided to go that way after trying to improve my last attempt at an ECS framework, only to realize I'm essentially hand-coding SQL indexes, and that's not something I want to do when I'm prototyping a game.
They both are bringing concepts from relational algebra/databases into memory management for globals, but different parts:
An ECS is fundamentally a column store for one really wide table. It’s optimized for performing batch operations on sparsely-populated columns in that table.
This is more like defining a database index: these dictionaries are using some kind of computed result as the key, allowing you to lookup objects by this result without a linear scan through all relevant objects.
And then before you know it your prototype is in production, you've been promoted or left the company, and it is some poor bastard that you've never mets problem!
I understand this is for rapid prototyping and not for production use, as the author clearly stated. But it did remind me of an anti-pattern I see all the time at work that does make it's way into production, often for the same reason.
I work primarily in Python, and one thing I see all the time is using dictionaries instead of method arguments. The thought is that when the method evolves over time, you can just add a field to the dictionary. What ends up happening is this dictionary argument is passed up and down the call stack for every nested method call, and sometimes elements are added, edited, or deleted on the way.
Whenever I have to do a refactor of methods like this, it is awful trying to figure out the state of the dictionary, what was originally supposed to be in it, and if it's returned, which parts are actually relevant.
Wouldn't it be much more appropriate to use immutable dictionaries in this case (or just not mutate them by convention)? Clojure does this and it seems to work pretty well.
Yes but at that point, designing the structure of the dictionary and not allowing any down-stream method calls to alter it would require the same amount of work/forethought as actually redesigning the method to take X defined inputs and return Y values (and documenting it).
Initial effort when refactoring might be the same -- or even bigger -- but it will save you time afterwards. The added peace of mind is not to be underestimated either.
This is where good docstrings come in handy. Personally, I think a dict with parameters is better than an endlessly growing method parameters. You can control it a bit further by using tuples instead of dicts for the same idea.
I think this is basically the best feature of Clojure: its defrecord form creates a new class but that class implements all the interfaces necessary to treat it like a dictionary.
So...fields, then? Except with a leaky scope and no encapsulation?
This can be useful when you want to keep an association to an object but you want to ownership of that list of associations in a different object but I certainly would not make these global.
As a backend guy, what I see is that I could assign an id to each editor and reference everything by the id. It's about the same thing internally, of course, but a bit more orthodox for those weirded out by objects as dictionary keys. Plus I'd probably have the file path and such on the editor object, then only labels and buttons outside the editor would be kept out of the editor objects. Voila, a pretty classical architecture.
This seems like absolutely horrible advice. Building something quickly doesn't mean disregarding every development common sense since we invented these things to help us.
What's next? Forget numbers, just String everything because you'll save time serializing?
The first time I read about ECS I thought it was a completely crazy idea, but after trying it I realized that I was able to have extreme low-coupling and complete separation of concerns. It was great for hacking things, but in the long term it was also great for maintenance and code reuse! I never felt this way about OOP, ever.
In this case however it seems that the "Entity Management" part is ad-hoc, with Editors themselves putting things on the dictionaries. Ooops :)
EDIT: I think one can get a lot of mileage out of something like this if the dictionary management code is properly encapsulated in an isolated module/class. Just an idea.
[1] https://en.wikipedia.org/wiki/Entity_component_system