Live data from Hacker News

The faster you unlearn OOP, the better for you and your software

dpc.pw

61–70 of 252 posts

Re: The faster you unlearn OOP, the better for you and your software

#61
post #2

>The vast majority of essential code is not operating on just one object – it is actually implementing cross-cutting concerns. Example: when class Player hits() a class Monster, where exactly do we modify data? Monster's hp has to decrease by Player's attackPower, Player's xps increase by Monster's level if Monster got killed. Does it happen in Player.hits(Monster m) or Monster.isHitBy(Player p). What if there's a cl…

> when class Player hits() a class Monster, where exactly do we modify data? Monster's hp has to decrease by Player's attackPower, Player's xps increase by Monster's level if Monster got killed. Does it happen in Player.hits(Monster m) or Monster.isHitBy(Player p). What if there's a class Weapon involved? Do we pass it as an argument to isHitBy or does Player has a currentWeapon() getter?

I don't see how this is a problem unless you think programming objects correspond with physical objects.

My first thought is make a separate Swing class representing the player swinging their weapon. There's probably an even better way to do it but this gets around the issues mentioned above.

    class Player {
      fun takeSwing {
        swing = new Swing(
          this.currentWeapon,
          this.location.offset(this.direction)
        )
        if swing.killedMonster {
          this.xp += swing.monster.level
        }
      }
    }

    class Swing {
      fun new(weapon, location) {
        this.weapon = weapon
        this.monster = findMonster(location)
        if this.monster != null { this.monster.takeHit(this) }
        this.killedMonster = this.monster != null && this.monster.dead
      }

      fun damage {
        return this.weapon.baseDamage
          + this.weapon.bonusDamage
      }
    }

    class Monster {
      fun takeHit(swing) {
        this.hp -= swing.damage - this.defence
        if this.hp 

Re: The faster you unlearn OOP, the better for you and your software

#62
post #57
post #26

> The vast majority of essential code is not operating on just one object – it is actually implementing cross-cutting concerns. Example: when class Player hits() a class Monster, where exactly do we modify data? Monster's hp has to decrease by Player's attackPower, Player's xps increase by Monster's level if Monster got killed. Does it happen in Player.hits(Monster m) or Monster.isHitBy(Player p). What if there's a c…

This does read nicely in most cases. There are minor issues when you venture off and decide that it would be cool if a monsters could damage your weapon. Something like Monster.hits(Weapon).with(Weapon). You'll start wondering if Weapon should inherit from the player base class. If not, you'll spend time wondering how to share the code to keep things dry. Obviously there are many solutions even within OOP, but one mi…

I think damage to the weapon could still be handled in the Hit class, because Hit has a reference to Player and Monster and Weapon. You could even create different types of Hit classes that could execute different kinds of Hit behavior like this.

In that case it may be better to rename it to HitStrategy?

Re: The faster you unlearn OOP, the better for you and your software

#64
post #51

Earlier quoted context omitted.

Great point... but doesn't it continue to prove the author's point that this is needlessly complex? It strikes me as bad design if something as simple as an action (hit) now needs to become it's own class that is instantiated merely to execute a single function and then have to delete itself. That just feels like insane overhead/boilerplate, no? The argument here is that it's far better to have player and monster be…

Hit is an event that occurs, so not sure why it should escape OO even though in this example even though it's only decreasing ints. I routinely code event objects rather than modifying another class directly by the delta that should occur. OO isn't good for continuous time and events but it does add ergonomic convenience for the developer in this case (there could be other information on the hit, a historical list of…

You could even serialize the Hit classes and throw them on a message bus, and have them automatically applied across the network or something.

Re: The faster you unlearn OOP, the better for you and your software

#65
post #26

> The vast majority of essential code is not operating on just one object – it is actually implementing cross-cutting concerns. Example: when class Player hits() a class Monster, where exactly do we modify data? Monster's hp has to decrease by Player's attackPower, Player's xps increase by Monster's level if Monster got killed. Does it happen in Player.hits(Monster m) or Monster.isHitBy(Player p). What if there's a c…

> It does read kind of nicely IMO... And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance?

>And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance?

Hits don't usually happen that often in games relative to everything else the engine is doing unless you're writing something like MMO server code that processes hits from thousands players.

Yours is a classic case of over-optimizing, I think.

Re: The faster you unlearn OOP, the better for you and your software

#66
post #38

Earlier quoted context omitted.

Great point... but doesn't it continue to prove the author's point that this is needlessly complex? It strikes me as bad design if something as simple as an action (hit) now needs to become it's own class that is instantiated merely to execute a single function and then have to delete itself. That just feels like insane overhead/boilerplate, no? The argument here is that it's far better to have player and monster be…

The Hit class may be a Singleton and only instantiated once. Creating a class is like 3 actual lines. Also at least in Java the Hit class can be an inner class of the Person class. It just feels like name spacing in that case. It is needless complex if the code is simple. This solution is more flexible that just a pure function. If you have different players who use different hit strategies how do you handle that wit…

Ultimately the answer is to use a language in which passing a function reference is as easy as writing a singleton that implements an interface and passing a reference to that. (Or easier—in any language with decent function-passing support, writing a function and referencing it is generally going to be easier because fewer things are happening.)

OO makes sense if the different types of behavior map to object identities and inheritance trees, because it generates a chain of implicit if statements that capture this logic. If they don't—e.g., a hit changes behavior depending on whether the attacker is proficient in the weapon, rather than depending on the weapon class or attacker class itself—you're likely better off writing the if statements yourself.

Re: The faster you unlearn OOP, the better for you and your software

#67
post #12
post #2

>The vast majority of essential code is not operating on just one object – it is actually implementing cross-cutting concerns. Example: when class Player hits() a class Monster, where exactly do we modify data? Monster's hp has to decrease by Player's attackPower, Player's xps increase by Monster's level if Monster got killed. Does it happen in Player.hits(Monster m) or Monster.isHitBy(Player p). What if there's a cl…

> What does the non-OOP solution for this problem look like? function hit(player, weapon, monster) { This is a major problem with single dispatch , a popular OOP implementation choice, but not the only one. Common Lisp and C++ have multiple dispatch, which is a generally accepted solution to this problem. It seems like a lot of the challenges you're facing has to do with object-oriented design not being a good fit fo…

I don't think C++ has a dynamic multiple dispatch mechanism. You can craft one with the visitor pattern or some precompiling or macro sheanigans. I'd love to hear about the best way of doing it.

Re: The faster you unlearn OOP, the better for you and your software

#68
post #65

Earlier quoted context omitted.

> It does read kind of nicely IMO... And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance?

>And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance? Hits don't usually happen that often in games relative to everything else the engine is doing unless you're writing something like MMO server code that processes hits from thousands players. Yours is a classic case of over-optimizing, I think.

Over-optimizing as in, write the one obvious line instead of 15 lines of crap that are also slower?

Re: The faster you unlearn OOP, the better for you and your software

#69
post #38

Earlier quoted context omitted.

Great point... but doesn't it continue to prove the author's point that this is needlessly complex? It strikes me as bad design if something as simple as an action (hit) now needs to become it's own class that is instantiated merely to execute a single function and then have to delete itself. That just feels like insane overhead/boilerplate, no? The argument here is that it's far better to have player and monster be…

The Hit class may be a Singleton and only instantiated once. Creating a class is like 3 actual lines. Also at least in Java the Hit class can be an inner class of the Person class. It just feels like name spacing in that case. It is needless complex if the code is simple. This solution is more flexible that just a pure function. If you have different players who use different hit strategies how do you handle that wit…

Languages that force you to use the singleton pattern really ought to just let you write A FUNCTION

Re: The faster you unlearn OOP, the better for you and your software

#70

Earlier quoted context omitted.

> It does read kind of nicely IMO... And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance?

> And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance? at least in C++, why would they ? it'll all gets inlined and you can do it so that there isn't any memory allocations if you know all of your cases at compile time (of course most of the time you want to be able to add new weapons, behaviours, etc, at runtime but in game engines…

It's nice that some C++ compilers can optimize this out. It means that it only costs maintainer cycles (less straightforward invocation), and compiler cycles. As every C++ programmer knows, there is an unlimited amount of them.
Post reply on HN