fundamental design question

BlitzMax Forums/BlitzMax Beginners Area/fundamental design question

Drakim(Posted 2007) [#1]
Hia, my first topic here :)

Now, after falling in love with OOP, I have a problem now that I've no idea on how to solve in a good way. Mind you, I can solve it, but my solution is really bad. Thus, I want to draw upon the collective mind power of the community! :D (although I have a feeling the solution is pretty basic and easy)

I'm making a game, were there is one type that is a container for all my objects. This type is called "universe", and each object are things like "ship" and "rock". These objects are stored in a TList field in the "universe".

(don't know how to make those fancy code thingies, sorry)
-----------------
SuperStrict

Type TUniverse

Field ObjectList:TList

Method AddRock()
Local newrock:TRock = TRock.Create(4)
ObjectList.AddLast(newrock)
End Method

End Type


Type TRock

Field Size:Int

Function Create:TRock(Size:Int)
Local CreateObject:TRock = New TRock
CreateObject.Size = Size
Return CreateObject
End Function

End Type
-----------------

This goes fine, and I think this is how most people would do it (althought I've not seen many examples).

But then comes the problem I'm facing, what if a method within TRock needs to access the TUniverse that it's currently inside?

I could solve this by having a field inside TRock:
"Field ParentUniverse:TUniverse"
which holds what universe the TRock is currently under. However, isn't this a very bad solution, since I'm creating a circle reference? Doesn't memory leaks arise from that in BlitzMax?

Is there a nicer better way I can do it? Or am I at fault for designing the system like this from the start? What way would I use instead?

Thanks in advance. <3


Diordna(Posted 2007) [#2]
Well, in theory, the rock shouldn't need to know anything about the universe. The universe should act on the rock.

Others are free to call me an idiot. I have no credentials.


tonyg(Posted 2007) [#3]
There is something about having an Addrock method in Universe that seems very very wrong in OOP terms.
Does it matter that it is a rock?
What if you have a Universe with trees, cows, ships, planets, hospitals etc?
As for your parent problem. How many Universe's are you going to have? What is Rock ever going to need to know about the Universe it is in and, if it does, can't it use a GetUniverse function or use a UniverseData type if you have more than one?
Finally, is it really bad to have a pointer to its parent Universe when the Rock only appears in a list which happens to be an attribute of Universe?
I think you should give that a go as I suspect, once removed from the list, the Rock will be garbage collected when out of scope which will release the pointer to Universe.
<edit> Without really knowing your design plans its trick to suggest anything concrete.


Drakim(Posted 2007) [#4]
Diordna:
Really? Because, this would create a lot of awkward ways of doing things in my eyes.

For example, when the rock breaks into two, I have a method inside TRock called "BreakIntoTwo()". Now, this method creates a new TRock, but I'm unable to access the TUniverse's ObjectList from the original TRock, thus I cannot add this new rock to the list. If I do the circular cheat, I could do this:

--------
Method BreakIntoTwo()
Self.Size :- 2
Local newrock:TRock = TRock.Create(2)
Self.ParentUniverse.ObjectList.AddLast(newrock)
End Method
--------
But as I said, this is ugly >> (at least I think so)


But, if I was to do things as you say, and do things though the TUniverse, then wouldn't that defeat the purpose of having an own type for the TRock? What use is it to have a TRock if all the TRock methods needs to be within the TUniverse? TRock would be nothing but a collection of variables, with all it's methods shattered around the code. That is nothing like the OOP I fell in love with D:


Who was John Galt?(Posted 2007) [#5]
Global theUniverse:TUniverse


Drakim(Posted 2007) [#6]
tonyg:
Sorry, but this example is just something quick and dirty that I made up for this post. I can assure you that most of your fears about the code (such as the rock being created inside the universe as a method) doesn't exist in my real working project.

It's simply that in my haste to make things easy and understandable, I've introduces quite some idiotic things here in my example, but I don't think it should change the actual problem at hand.

As for how many universes I'm going to have? Four! :p
(you can warp between diffrent plains, thus, I need a type to remember all old objects should you warp back)

Now, you think a pointer to the parent universe isn't bad? Might be, but there is something inside me that tells me I'm making something evil and wicked when I do. Could it just be me? D:


Nomen luni:
That won't work at all, there isn't just one TUniverse


Who was John Galt?(Posted 2007) [#7]
Nomen luni:
That won't work at all, there isn't just one TUniverse
Haha okay mate... you're challenging my concept of reality here!


Vilu(Posted 2007) [#8]
That won't work at all, there isn't just one TUniverse


Shouldn't you call it TMultiverse, then? ;)

edit:
Ok, an attempt to be serious:

I don't see a problem having a pointer to the universe the rock is in. I'm having things like that in my current project: A space combat/trading game with multiple AI pilots (TPilot), each flying around a bunch of sectors (TSector) and all the time knowing which sector they are in. The only way to accomplish that is to have a field in TPilot to store the sector in. In my opinion, it's very practical, intuitive and OOP-like.


Drakim(Posted 2007) [#9]
Hehehe, the idea is that there are 4 diffrent Universes, layered upon each other. By going though black holes, you pop up in the next Universe.

So, I'm making a system were you can quickly switch from one TUniverse to the next TUniverse in a flash, and when you do, the old TUniverse is kept saved so that if you travel back, things are as you rememer them. (with some small changes depending on how long since you last visited).

So, when an object in an universe does something that affects the universe, it needs to be able to access it's parent universe, without a global, since a global will only allow one universe at a time.

Edit: You replied with an edit? >>

It was pure luck that I saw your edit. :0

Anyway, okay, I'll stick with this method. But it still hurts in my heart.


Vilu(Posted 2007) [#10]
You replied while I was editing ;)


Drakim(Posted 2007) [#11]
So, one last question.

I know the memory garbage collector deletes an object if there is nothing that points to the object anymore (no variables or no lists that contains the object).

However, does the memory garbage collector also spare an object when an object itself refers to another object?

Such as in my example, the TUniverse keeps the TRock safely within the TList. And the TRock also links back to it's TUniverse parent with a field named ParentUniverse.

Will this ParentUniverse field keep the TRock alive after it gets removed from the TList? Since, this reference is kinda the "wrong way" so, you can't access the rock anymore anyway.

So, will it be deleted or not?

(sorry if what I said didn't make sense)


Who was John Galt?(Posted 2007) [#12]
In your example, the universe will be deleted, and next time round, so will the rock it refers to, if the universe was the only thing that referenced it.


Drakim(Posted 2007) [#13]
Oh, sorry, If forgot to mention, the universe is mainained within a variable, so it's safe.

I'll rephrase the question here:

1. Universe refers to rock
2. Rock refers to Universe
3. Universe is "safe"

Now, what would happen if we took away 1? Would the rock's reference to the universe keep the rock alive? Or does it need something to refers to itself to not be deleted?


Who was John Galt?(Posted 2007) [#14]
If it's not referenced, it dies. Full stop!

If you have two way references, the objects keep each other alive, which is something you need to keep in mind when cleaning up.


Czar Flavius(Posted 2007) [#15]
Think of it this way... if the universe refers to the rock, and that is the only reference, then the only way you can access the rock is through universe. If the reference in universe is removed, then you have no way of being able to access rock anymore, regardless of whether it exists or not. Even if it was still alive, you would have no way of using it. In this case the garbage collector will delete the rock because you have no way of using it anymore.

So don't worry. A good program design is where, when objects are no longer required, they go out of scope/lose their references "naturally" and just... disappear. Rather than one having to manually order that they are deleted.


tonyg(Posted 2007) [#16]

Oh, sorry, If forgot to mention, the universe is mainained within a variable, so it's safe.

I'll rephrase the question here:

1. Universe refers to rock
2. Rock refers to Universe
3. Universe is "safe"

Now, what would happen if we took away 1? Would the rock's reference to the universe keep the rock alive? Or does it need something to refers to itself to not be deleted?


Universe doesn't refer to Rock! Universe refers to a list. Rock happens to be on that tlist. If the only reference to Rock is that tlist then, once removed from the list, the Rock will be removed at the next garbage collection. If Rock refers to Universe the reference is removed when the Rock is auto-deleted. As long as Universe has something else pointing to it it won't be removed. In this case I would ensure there are a list or array of Universes.


Czar Flavius(Posted 2007) [#17]
Universe doesn't refer to Rock!


Sorry, I meant ultimately/through some chain of lists or arrays :)


tonyg(Posted 2007) [#18]
Sorry Czar, I wasn't clear but was referring to the post from Drakim. I edited the post.


FlameDuck(Posted 2007) [#19]
Hehehe, the idea is that there are 4 diffrent Universes, layered upon each other.
Have a SuperUniverse/Multiverse class that contains all these Universes, and make that a Singleton.

space combat/trading game with multiple AI pilots (TPilot), each flying around a bunch of sectors (TSector) and all the time knowing which sector they are in.
Why do they need to know that?

The only way to accomplish that is to have a field in TPilot to store the sector in.
No it isn't.

In my opinion, it's very practical, intuitive and OOP-like.
Well you're entitled to your opinion but you're wrong. Cyclic references are an indication of poor software design, increases complexity for no reason, and leads to more difficult to maintain software (as it creates an unnecessarily high degree of coupling).

Would the rock's reference to the universe keep the rock alive?
No. Only references to the rock are counted.


QuietBloke(Posted 2007) [#20]
Going back to your earlier post. You want logic for a rock so that when it is hit it will break into two rocks.

Your universe as I understand it has many different type of Entities ( rocks, ship, etc ).

A possible way to do this is for the universe to handle the creation of the smaller rocks.
You could for example have an additional field in all your entities which contains an int... call it DieProcess for example. The rock could contain a value 1.
The universe will check this field when an object dies. A 1 will tell it that as well as removing the entity from the universe it must also create n number of type TMedium rocks in it's place.
Your Medium rocks will have a value of 2 as the DieProcess which the universe will know that it needs to create a Small Rock when it dies.

Finally the smallRock will have zero which tells the universe to do nothing except remove the entity.

In this way the Entities still each determine what to do when they die.. only now they specify what to do from a list of possible things that the universe can offer.

Anyway... thats just one possible way of doing it.. probably not the best but its the best I can come up with at the moment.


Drakim(Posted 2007) [#21]
FlameDuck:
I don't see how making a Multiverse class would help me. How would each rock know which universe in the Multiverse they are in? If they have a field that remembers it, then we are where we started again. I am still faced with the same problem.

FlameDuck:
I was thinking of this solution, but it seems like it would make my code very hard to read, and very illogical. Shouldn't every rock method be under the rock, and not under the universe? If I'm going to have everything under the universe, I might as well drop OOP.


Czar Flavius(Posted 2007) [#22]
Each rock should "know" which universe it is in by simple fact of which list it is in. It doesn't need to know that there are any other universes out there. You give the rock all the data it needs to know to perform its actions as a parameter to its method. So when universe calls a rock method, it gives it the required info from its own fields to the rock as a parameter.


Dreamora(Posted 2007) [#23]
Alternatively you could have a type global "currentUniverse" which you update before performing an operation. if you work with rocks of a single universe at a time, this will save time compared to the pushing through methods

Or if you insist to store it on the rock: make a TMap in the universe where each universe gets a name and store the <name,TUniverse> pair into that map. now the rock can retrieve its universe dynamically if it needs to know about it.


Drakim(Posted 2007) [#24]
Czar Flavius:
This means that I must pass the universe with every single method I make. Is this really effective complared to simply saving a pointer to the universe within the rock?

Dreamora:
Hmmh, The currentUniverse system just might work. I never use more than one Universe at the time. Thanks!


FlameDuck(Posted 2007) [#25]
How would each rock know which universe in the Multiverse they are in?
They don't. But the multiverse knows which universe each rock is in.

I was thinking of this solution, but it seems like it would make my code very hard to read, and very illogical.
Which solution?

Shouldn't every rock method be under the rock, and not under the universe?
Yes. And every Universe method under the universe, so to speak.


Czar Flavius(Posted 2007) [#26]
Method doRockThing(universe:TUniverse)

There you go :P


siread(Posted 2007) [#27]
The way I see it, your SplitRock method is basically another AddRock method, and you don't really want duplicates of the same thing. If you are adding rocks to the universe then it should be the universe that creates them.

How about...

Type TRock
   Global list:TList
   Field splitrock:Int

   Function UpdateAll()
      For local r:TRock = EachIn list
         r.Update()
      Next
   End Function

   Method Update()
      If WasHit() Then splitrock = true
   End Method

   Method WasHit()
       ' Check for missile hits
   End Method

   Function SplitRocks()
      For local r:TRock = EachIn list
         If r.splitrock
            r.splitrock = false
            return true
         EndIf
      Next

      Return false
   End Function
      
End Type


Then every loop of TUniverse you do:

   While TRock.SplitRocks()
      AddRock()
   Wend



Drakim(Posted 2007) [#28]
Ah, you see, there is no splitrock method. I just made an example. Most of the methods on the rock is stuff like moving and colliding.

But, anyway, I think I've gotten my question answered, so the topic is kinda over. thanks everybody!