My policy: OOP is like salt. Use little and that's great. I only allow a single inheritance layer, and ideally no inheritance at all.
My policy: OOP is like salt. Use little and that's great. I only allow a single inheritance layer, and ideally no inheritance at all.
That's been my observation as well - there's no free lunch and DRY isn't free either, certainly not if you use inheritance chains to achieve it.
I'm starting to think that the only programming axiom that will survive in the end is KISS.
And what an acronym given the context!
NSM would be the minimum element set of concepts shared by all human cultures, which can be used to derive the rest of concepts, and it's heavily based on anthropological field studies.
And it includes the numbers one, two and many, (also few, some & all). So that indicates some special meaningful distinction for us in that jump from two repetitions to start abstracting away.
But the single responsibility principle and the idea that you should divide your code in components where the lower components should be abstract by the point of view of the higher ones are almost never wrong.
Not by coincidence, those last two are consequences of how people think. While KISS is kind of a physics law.
In my experience it is nigh impossible to design-pattern your way out of a language simply being unable to express what you need it to express without incurring a severe performance penalty (anathema for game development) or resorting to code generation (which is itself prone to ending up as a Gordian knot).
No it's not. I'm talking about having nominal typing but easy support for delegation (e.g. look at how Kotlin does it).
But as the other reply said, patterns are a language smell - the language needs to support that style and make it idiomatic. Otherwise, if you give people the choice between doing it right but verbosely and doing it wrong but more quickly, guess which one they'll choose. https://www.haskellforall.com/2016/04/worst-practices-should...
You still don't necessarily need to model the graph at the object level though. You could model your graph as a set of pairs, ie edges. If performance was more of a concern you could do it as a map where each object was a key and the values were a list of the connected objects. You could also separate out the value from the graph behavior and have a GraphNode object that wraps each value object.
The bigger point though is when you have networks of objects that are relying on each other to perform functions, that's where the problem is, and where you start to rely on mocks and stubs for testing, which really should be considered a smell in its own right. Your better off making the interface between objects values rather than having hard references between objects as described in this talk: https://www.destroyallsoftware.com/talks/boundaries. Once the interfaces between your objects are values instead of references you're free to connect your objects together in any way you need and are not stuck with the fixed graph as originally designed.
> Once the interfaces between your objects are values instead of references you're free to connect your objects together in any way you need and are not stuck with the fixed graph as originally designed.
Absolutely, same can be said about relational data representation for example.
I have a gorilla class and a banana method on the class, can I ever use banana without the gorilla? No. The simple act of having a class screws it all up.
Make the banana a pure function, or one that is defined in terms of a polymorphic parameter that it mutates.
func banana(x: animal){...}
banana(gorilla);
The way to do it via OOP is base class methods and importing the method to all children via inheritance which is just bad and universally reviled among even practitioners of OOP. class Animal()
{
banana()
}
class Gorilla() : Animal {
}
Technically there are slight fair advantages and disadvantages to the OOP way when you don't account for inheritance and polymorphism but all that is gone once you do account for them.The trouble is C++ and similar object models poor support for composition and implicitly penalizing uniform object structure.
(E.g. call multiple base methods in a subclass - pain ensues. Add virtual calls to the mix, it gets really iffy.)
Even Python and Ruby with explicit mixins but the old model get hairy.
I'm not being critical either, I'm seriously curious how somebody would implement this behavior in Go. Like you say, just struct with methods is a very appealing mental model since you have fewer moving parts, but how does it deal with a scenario like this which is very common in game dev?
I'm slightly confused about what you're trying to describe, but maybe it's something like this?
https://play.golang.org/p/smPyWvBvWWp
We can make both Food and Animal statisfy the "Animateable" interface.
Go also has a syntactic shortcut which is similar to inheritence, but more explicit. If you include a field in a struct without any name, it is considered "embedded", and you can call methods on the field directly, without referencing its name, which ends up looking the same, at the call site, as inheritence.
https://play.golang.org/p/cIMqQqE0eVc
But everything is still explicitly laid out in the struct -- it's just a simple struct, no inheritence shenanigans.
I think interheritence is a mistake, but interfaces are the bees-knees. Even in C# (which is my main language these days, 'cuz that's what you use if you're making a game in unity), I just use interfaces, not implementation inheritence.
(And even interfaces I don't use much -- but when you need it, you need it!)
The idea I was going with Animateable was not animation (poor wording on my part), but effictively giving the properties of a creature to a thing, ie magically animating it. So I have an ability in-game that lets me turn anything into a creature, thus merging the Creature interface with whatever other interface this has. From what I'm getting from you, that means every single thing in the game must implement the Animateable interface.
Now lets do another one, I want a Metalic property. Say these are things that when hit by lightning will give off lightning damage. Then there's a curse called Midas that will let me turn anything metallic.
So I have a Wooden Hammer object which I midas curse mixing in metalic, then a wizard animates it so that it has the properties of a creature, and I have a creature that when I hit with lightning gives off sparks that I can also use to hammer nails.
Point being, in this world you're basically requiring every single thing to implement every single interface which sounds like a nightmare, where with more of an ECS style system, you just merge in the new behavior and call it a day.
(I would say interfaces/inheritence are useful for simple high level abstraction of basic concepts with multiple implementations (here's an interface for a compression algorithm, or a RNG, or whatever), not so useful for trying to model complex game-world relationships. And to me, the useful part is the interface, whereas inheritence mixes in two unrelated concepts: implemementation-sharing and interface.)
But you don't need either of those for ECS, right?
If I understand it correctly, ECS at its core is basically an optimization of the struct-of-arrays datastructure. E.g., something like this is a super-basic ECS:
struct World {
Comp1[] Comp1
bool[] HasComp1
Comp2[] Comp2
bool[] HasComp2
...
int NumEntities // all arrays have this length
}
I'm a big fan in general of struct-of-arrays vs collection-disparate-structs-with-pointers. Besides being way faster, it tends to make code clearer, too. ECS seems like a more efficient and convenient evolution of that pattern.At least from what I've read. No real experience. Take with 13 grains of salt. :-)
P.S. If you feel like it, I'd be happy to explore some concrete code (you first!). It's always very interesting to me to try to explore all possible ways to express some concept in code.
By structural typing. It’s a form of satisfying an interface by composition.
edit: You cannot, however, explicitly declare that a "Chicken" is supposed to be a Food and/or an Animal. And you can't directly inherit implementations; to make Chicken directly use a generic function you need to do something like implement "baseSpoil(Food)", and then have the body of "spoil(Chicken)" explicitly call "baseSpoil(self)".
As mentioned in another reply, there is a shortcut. You can declare that the Chicken structure includes (anonymous) Food and Animal fields. Then, if you call "spoil(myChicken)", Go will automatically replace it with "spoil(myChicken.Food)".
Nobody on the face of the earth has ever used Functional Reactive Programming. It's a made up concept. In fact it's also definitely not one of the concepts that inspired the most popular pattern in React.
Of course it's relatively easy to build shiny clean wrappers around the dirty work that others have had to do on your behalf.
First off FRP is actually a term. Functional Reactive Programming. It is 100% a functional paradigm that gasp by DEFINITION cannot be procedural. So you truly didn't know what you were talking about here: https://en.wikipedia.org/wiki/Functional_reactive_programmin...
Second there are many languages/frameworks that use this paradigm. React partially uses this paradigm. But I will list two popular ones that strictly follow it... just note that there are more. Much more.
Take a look at elm. Elm is a fully functional UI library and language that is 100% pure, functional and has no OOP. Believe it or not ELM is not some toy, it is production ready and actually measurably faster than react. This language is what popularized the FRP pattern which is partially used by React today. See here: https://elm-lang.org/examples/mario
Note the load time and latency on the mario game. React doing the same thing will be slower.
Another language is ReasonML. ReasonML is essentially a language that compiles to the browser and has one to one correspondence with another functional language... OCaml.
ReasonML is Created by the creator of React, Jordan Walke. Coupled with React as framework, ReasonML is essentially the ideal GUI paradigm that Jordan would recommend everyone to use in the ideal world. However due to the fact that everyone is use to javascript, Jordan instead as a first step, ported FRP concepts over to a language called JSX (essentially JavaScript mixed with html) and is slowly nudging the world in the direction of GUI programming using the functional style: https://reasonml.github.io/
Make no mistake the creator, of the most popular GUI framework in the world is a supporter of the pure functional paradigm, and he is heavily and successfully pushing the functional style programming of GUIs into the mainstream.
OOPs being the dominant paradigm for GUIs has, in the past five years, become a false statement.
You're right on the wrapper part though. Assembly language is essentially a procedural language so every functional thing that exists on top of it, is a wrapper. But I mean this is a pointless observation.
How does it run on the browser?
I can't speak to ReasonML, as I have not used it, but my impression also is that it takes advantage of other people's GUI libraries considering that I've heard Revery compared to Flutter (and Flutter outsources its rendering to Skia as well as relies on rather imperative RenderObjects beneath the functional reactive layers). Please correct me if I'm wrong.
I would be very interested indeed to see a GUI framework built from the actual ground level up in an entirely functional reactive manner. Do you know of any?
Unfortunately it's discontinued, probably rightly because it's definitely not for teamwork. But really, it doesn't depend on anything OOP in the strict sense.
MVU is more towards functional rather than imperative programming.
I would agree to that as a goal, but not as a dogma. It depends what you build, I guess.
I have a player, NPCs, enemies, and wild animals. Should they have any class hierarchy at all? If my code matches the game world, what happens when I was to add golems and let mages turn castle's into entities that act just like an NPC would? Should my class design be so tightly coupled with the elements of my world? I don't think so but what alternative works best?