4 ms·
Let's go point by point. > No, I disagree, because the right behavior is not determined by a single object but by a combination of factors Sure, and you can a
by trashburger 3y ago
Let's go point by point.
> No, I disagree, because the right behavior is not determined by a single object but by a combination of factors
Sure, and you can ask one or more of the objects involved about all of those factors!
> The check that the "enemy" is even a valid target (who decides that? The player? What if certain enemies are immune to slashing attacks? The sword then? What about the enemy making the decision? What if it depends on the armor the enemy is wearing, does the armor decide?)
Answer: All of them! Let's walk through it step-by-step:
- The player wants to attack the entity it's pointing at: `aPlayer attackEntityCurrentlyPointedAtIn: world.`
- In order to do this, the player asks the world what it's pointing at first: `entity: world entityPointedAtBy: self.` If nothing is returned, then the player would simply play the shoot animation of the wielded weapon.
- If an entity is returned, the player asks the enemy to be attacked by itself: `entity getAttackedBy: self`.
- The enemy checks whether it's the right kind of entity first (attacking a prop might do nothing, for instance). Afterwards, it asks the source entity about the various factors you talked about (what kind of attack the source's currently wielded weapon would produce, what the armor is, etc.) and responds whether it could be attacked and how much damage was produced. Maybe the enemy could even deal damage back! `source dealDamage: 123 Source: damageSources reflection.`
> The calculation of the position of where the sword impacted the enemy (who calculates that? it depends on the animation calculation results, the size and shape of the sword and of the enemy, etc.)
We already would need the animation context in order to make sense of the animation that would be produced by the attack at this point, so we can just thread it through our messages and ask where on the target entity the strike would happen. Because we would be asking the wielded weapon about the details of what kind of animation it would produce, it can add in any details and modifiers you'd like.
> The sound that plays depends on the type of equipment the enemy has (leather armor vs steel armor) and on the weapon of the player (a sword or a hammer would have different sounds).
> The actual playback of the sound depends on all of the decisions above (the animation, the collision, the materials involved in the collision).
Let's just ask the attack source about what kind of material it is, and then what it would produce if it hit us, and then ask the sound to play:
weaponMaterial: source wieldedWeapon material.
"Assuming you need this level of detail"
ownMaterialAtStrikePoint: strikeSurface material.
weaponMaterial soundForStrikingAgainst: ownMaterialAtStrikePoint
; play: audioContext.
> At no point, in any of this, are there messages between these objects.
I think I was able to prove this wrong. ;)
> The entities that have to make these calculations depend on multiple data sources, none of which belong to them, that is, the behavior is not associated with the data.
This isn't how the real world works though. Every action has a fundamental source and a target. Being able to model how different things interact in our system in such a natural manner like message passing can make everything so much clearer.
> An animation doesn't render itself (rendering requires a rendering context, mesh data, texture data, shaders, etc., who owns them?). A sound doesn't play itself. Collisions don't calculate themselves.
But they do! They are the best at rendering, playing and calculating themselves, because they're the information expert: they hold all the details. The code that sends the `play:` message need not know anything about whether the sound is a waveform or Opus; it need not know whether the sound object is actually a `pitchChange` object that modifies the original sound effect. All it needs to know is that the object can be `play:`ed.
> Inert objects don't inherit behavior because they have no behavior, systems do.
A system is simply a hierarchy of objects, as I detailed above, so they absolutely can have behavior.
> Even in Smalltalk you see this issue happening. Ints and Doubles respond to messages to implement arithmetic, but for example reading an integer from a stream is a method of the Number class that takes an integer radix and a Stream as input. Why is it not a method of the integer radix or of the stream? Why can't different streams decide how to parse themselves into integers? Because the whole concept of associating behavior here is silly.
Actually, it makes much more sense than a `stream readInt` ever could. Why does a stream know how to read an integer? Why does it need to know how an integer looks, and how to create one? It is much more natural to ask an integer to take in a stream and create its own representation, IMO.
- sirwhinesalot 3y agoYou've created an extremely inefficient architecture to do what some simple functions and some bytes can accomplish. What was the benefit of the exercise? About the stream, first there's the fact that it's the Integer class that implements readFrom (and that class has no internal data beyond its type, so it's just a namespace) and the fact that in order to create the integer, the class has to see the data that comes out of the stream (it's just calling getters). If you think this is object-oriented I have a bridge to sell you.
- trashburger 3y agoI'm not sure how this is anymore inefficient than what a functional design would come up with. Is asking the objects about their behavior any more inefficient than dispatching yourself based on the characteristics of that object? > What was the benefit of the exercise? I don't know, you were the one that asked about it. What was your point? > first there's the fact that it's the Integer class that implements readFrom (and that class has no internal data beyond its type, so it's just a namespace) Ah, but it's the _trait_ that would implement that behavior (my original post said that classes are just an over-simplified way to look at object programming, and I stand by that. I'm working within a prototype-based framework). So the integer's behavior would be provided by the trait, as well as the behavior to create it. > and the fact that in order to create the integer, the class has to see the data that comes out of the stream I'm not sure how that goes against what I said? Indeed it would. > If you think this is object-oriented I have a bridge to sell you. You can have non-object programming style bits in an object programming language. I'm not even sure what your original point was if this is what you're saying: that an object programming environment contains procedural or functional styled bits? I don't disagree with that, but a functional-style programming language will contain object programming-style bits just the same.
- sirwhinesalot 3y agoMy apologies if I wasn't clear, my point is only the full blown object oriented style of programming (meaning one where everything in the program is modeled as an own entity with its own state that responds as it wishes to a set of messages) is silly. The "everything is an object" idea. Objects with no associated behavior or state aren't objects, *by definition*, and there's plenty of code out there that benefits from data with no associated behavior and behavior with no associated data. This happens even in true OO languages like smalltalk. Attempting to model everything as "objects" (read: own private state + behavior) not only may have performance implications due to poor cache usage and multiple indirections, it also makes modelling certain concepts harder (see: object-relational mismatch). The example I gave is a clear example of the latter, since it is very much a relational problem. (Eric Lippert has some very good articles on the matter). That's not to say objects aren't valuable (they appear as a pattern even in functional languages) nor that "live" software isn't a great idea. Personally I think a lot of software would benefit from extensibility, which means plugins at least, which in turn means objects (private state + responding to a set of messages as they wish)