Author Topic: pedantic object oriented question  (Read 70262 times)

joeclark77

  • Rogueliker
  • ***
  • Posts: 90
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #30 on: April 11, 2013, 08:31:04 PM »
Pathetic n00bs, all! Real programmers write the 0s and 1s directly.

As always,
Minotauros
No, no, real programmers program with copper, silicon, and a soldering iron.

guest509

  • Guest
Re: pedantic object oriented question
« Reply #31 on: April 12, 2013, 01:31:43 AM »
Tubes. I use tubes.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #32 on: April 12, 2013, 08:03:09 AM »
No, I think the question is a good one. Here's my take on it.

It's best to minimize how spread out your data is across your code, and keep things general. So for example, I would not have references pointing backward (a Critter knowing which map it's in), only forward. A map has a list of levels, a level has a list of things, each thing has a list of their things, etcetera. You can see that this is very general -- everything is a thing, and they can contain other things.

Then for the cases where you want to look at the map and see what's where, or look at a thing and see where it is, I would write the appropriate methods/functions.

It's true that if you want to ask a thing where it is, you need to do a lookup in a more roundabout way than simply querying a property on the thing (though the complexity is well hidden by the method/function anyway). But I would argue that most of the time this will not be a performance critical part of your code. If it is, you can always optimize. Ultimately I think this approach has benefits for OO concepts like reusability, encapsulation and minimizing inter-dependencies.

Hmm. I'm curious: if the critter object doesn't know its map, then what do you do with something as basic as this: movement. Suppose your "Critter" object has a "move()" method, which, well, makes the critter move. Seems reasonable, no? But then you have to check for walls, etc. (you don't want your critter to move through the walls (unless it's a ghost or something)!) which means you need access to the map. Plus, if the map contains a reference to the critter attached to the tile it's standing on, that needs updating. So what should one do? What should that call to "move()" do?

Also, where should the "critters" be stored, anyway? Currently, in my program I have them "owned" by the map, since they're on the map and most would be associated with a map, anyway (when you save out a map, you have to save out all critters on it). How should it be?
« Last Edit: April 12, 2013, 08:10:04 AM by mike3 »

TheCreator

  • Rogueliker
  • ***
  • Posts: 370
  • Karma: +0/-0
    • View Profile
    • Fame
    • Email
Re: pedantic object oriented question
« Reply #33 on: April 12, 2013, 08:12:26 AM »
Hmm. I'm curious: if the critter object doesn't know its map, then what do you do with something as basic as this: movement. Suppose your "Critter" object has a "move()" method, which, well, makes the critter move. Seems reasonable, no? But then you have to check for walls, etc. (you don't want your critter to move through the walls (unless it's a ghost or something)!) which means you need access to the map. Plus, if the map contains a reference to the critter attached to the tile it's standing on, that needs updating. So what should one do? What should that call to "move()" do?

It just updates X and Y of the critter, I suppose. To actually move the critter on the map, you'd have to call something like map.MoveCritter (which would, in turn, call critter.Move). Makes sense to me.
Fame (Untitled) - my game. Everything is a roguelike.

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #34 on: April 12, 2013, 09:49:26 AM »
Hmm. I'm curious: if the critter object doesn't know its map, then what do you do with something as basic as this: movement. Suppose your "Critter" object has a "move()" method, which, well, makes the critter move. Seems reasonable, no? But then you have to check for walls, etc. (you don't want your critter to move through the walls (unless it's a ghost or something)!) which means you need access to the map. Plus, if the map contains a reference to the critter attached to the tile it's standing on, that needs updating. So what should one do? What should that call to "move()" do?
...

In that particular design the move() method should be in the map rather than the critter because the map is the only object with the required context to actually perform the operation. The only way the critter could really know how to move is by accessing the map anyway (by a cached map reference in the critter or the map being passed as an argument to an update() method).

This doesn't occur to a lot of people because standard teaching of OOP is just hideously bad. The focus on the intuitive real world concept of objects just leads to tears in the end. Intuitively it feels like the move() method should be on the critter because critters move but that's not how you should decide where methods live.

Because a critter can't stand in the same cell as other critters you need more contextual information about how to perform the action (i.e. where the other critters are). Also a move can fail for reasons beyond a critter being in the way, e.g. lava, walls. The only place where all the data is at hand is the map object, so move() should go there.


mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #35 on: April 12, 2013, 10:17:32 AM »
Hmm. I'm curious: if the critter object doesn't know its map, then what do you do with something as basic as this: movement. Suppose your "Critter" object has a "move()" method, which, well, makes the critter move. Seems reasonable, no? But then you have to check for walls, etc. (you don't want your critter to move through the walls (unless it's a ghost or something)!) which means you need access to the map. Plus, if the map contains a reference to the critter attached to the tile it's standing on, that needs updating. So what should one do? What should that call to "move()" do?
...

In that particular design the move() method should be in the map rather than the critter because the map is the only object with the required context to actually perform the operation. The only way the critter could really know how to move is by accessing the map anyway (by a cached map reference in the critter or the map being passed as an argument to an update() method).

This doesn't occur to a lot of people because standard teaching of OOP is just hideously bad. The focus on the intuitive real world concept of objects just leads to tears in the end. Intuitively it feels like the move() method should be on the critter because critters move but that's not how you should decide where methods live.

Because a critter can't stand in the same cell as other critters you need more contextual information about how to perform the action (i.e. where the other critters are). Also a move can fail for reasons beyond a critter being in the way, e.g. lava, walls. The only place where all the data is at hand is the map object, so move() should go there.

So what's a better intuition to use, then?

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #36 on: April 12, 2013, 11:16:33 AM »
The only place where all the data is at hand is the map object, so move() should go there.

Have you actually implemented movement like that? It doesn't sound logical. Game objects move, not the map. Objects move on the map and it ok to get data from the map to objects so they can see what kind of tile is there etc.

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #37 on: April 12, 2013, 02:12:15 PM »
So what's a better intuition to use, then?

All other things being equal you put the method where the relevant objects are in scope. So in the running example of moving a critter, the relevant objects are the other critters and the cells you could be moving over or through. The place they are owned is the map so put the method there.

Like all coding guidelines there's pros and cons. One of the cons is that you can end up with very fat classes with lots of methods and variables if you aren't careful.

Have you actually implemented movement like that? It doesn't sound logical. Game objects move, not the map. Objects move on the map and it ok to get data from the map to objects so they can see what kind of tile is there etc.

Yes, ever since I was taught this and many other things by a grey beard programmer many moons ago I've done this kind of thing on several projects. It does seem to make the code more robust but it does cause some gasps of disbelief from other programmers. I've also been lazy and not followed this guideline and I end up regretting on but small and trivial programs.

I like to think the reasons I've given for putting the method in the map are pretty logical. In fact I would say it's more logical than the natural language noun and verb intuitions commonly used. If you've ever looked into relational database normalisation the ideas have some overlap.

A move() method on the map doesn't mean the map is moving but that the map is moving things (which it owns). Also because moves can conceivably fail (wall or critter in the way) it's more of a request than an imperative command. So a more descriptive (but less pretty) method signature would be tryMoveCritter(critter, x, y) : bool.

Quendus

  • Rogueliker
  • ***
  • Posts: 447
  • Karma: +0/-0
  • $@ \in \{1,W\} \times \{1,H\}$
    • View Profile
    • Klein Roguelikes
Re: pedantic object oriented question
« Reply #38 on: April 12, 2013, 06:11:26 PM »
I eventually settled on that paradigm (map has the move() method) for my current game, and regret not using it before.

eclectocrat

  • Rogueliker
  • ***
  • Posts: 81
  • Karma: +0/-0
  • Most of your personality is unconscious.
    • View Profile
    • Mysterious Castle
    • Email
Re: pedantic object oriented question
« Reply #39 on: April 12, 2013, 06:45:59 PM »
My two pennies:

Objects are worthless in isolation, while it is good to avoid spurious uplinks to containing data structures, an entity (creature, npc, etc...) is almost just a lump of data unless it interacts with the map, items, other creatures, whatever.

Don't be afraid to put uplinks to containing objects. If your items need to know what race of creature is wielding them, there are many fancy design ways to figure it out, but if(_holder->race == RACE_ELF) is probably the easiest and least error prone.

I have entities with move as a member function, and it basically delegates to the map to do the physical moving, but then the entity can override the virtual move function to do something before or after the move, while if you have a monolithic move function for the map, handling pre-post ops on a type by type basis gets nuts.

BTW, C and Lua is a great combo. My engine is C++ and Lua, and I'm thrilled about it.

PS> There is no 'right' way, only wrong ways, and tradeoffs.

Ex

  • IRC Communications Delegate
  • Rogueliker
  • ***
  • Posts: 313
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #40 on: April 12, 2013, 08:24:44 PM »
If you ask me, objects should control themselves and their own variables. Every time I've ever implemented a function outside of an object that directly controls or affects an object I've always regretted it. That means move() inside the object, along with anything else that affects the object similarly being inside the object. Of course, it's a matter of taste, but it follows one of the most common principles of object oriented design.

Move especially makes sense for being inside an object because as Krice said, objects move, the map does not. Further, let's say you have a function in an object that wishes to move the object. If the object doesn't contain it's own move function, then it has to ask the map to move it. That's like asking the map to rename an object from a function inside the object being renamed... Once you start having functions like that, objects start to become C style structs which defeats the purpose of using objects in the first place.

I also disagree with the idea that there are wrong ways. Better ways, absolutely, but not wrong ways. It's only wrong if it doesn't work.

eclectocrat

  • Rogueliker
  • ***
  • Posts: 81
  • Karma: +0/-0
  • Most of your personality is unconscious.
    • View Profile
    • Mysterious Castle
    • Email
Re: pedantic object oriented question
« Reply #41 on: April 12, 2013, 08:38:32 PM »
... It's only wrong if it doesn't work.

Agreed, I'm a pragmatist, but today at work, a bunch of people definitely did the WRONG DANG THING! It's my job to clean up the poopy code they wrote the WRONG DANG WAY!

Uuuhhhh.... *sigh of disgust* if you have to maintain code, then there is definitely a wrong way, but in my own codebase, I've been satisfied several times by terrible code held together by tape and staples.

Eben

  • Rogueliker
  • ***
  • Posts: 339
  • Karma: +0/-0
  • Controversializer
    • View Profile
    • SquidPony!
Re: pedantic object oriented question
« Reply #42 on: April 13, 2013, 05:52:46 PM »
I've used both the "monsters move themselves" and the "map moves everything" approaches. I prefer the map having control because it has the proper context to make decisions.

If this bothers you then consider the game to be a board game and the map is a player that has control over all of the enemy figures.

george

  • Rogueliker
  • ***
  • Posts: 201
  • Karma: +1/-1
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #43 on: April 13, 2013, 08:02:26 PM »
I have entities with move as a member function, and it basically delegates to the map to do the physical moving, but then the entity can override the virtual move function to do something before or after the move, while if you have a monolithic move function for the map, handling pre-post ops on a type by type basis gets nuts.

I don't think having 'move' owned by the map is wrong per se but I think you can make a stronger argument for something like eclectocrat's design here.

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #44 on: April 13, 2013, 08:59:42 PM »
This is really a good reminder on how people can fuck up everything. Even simplest things. Then they call it the right way to do things.