Little OOP Design Problem

BlitzMax Forums/BlitzMax Programming/Little OOP Design Problem

Tibit(Posted 2006) [#1]
I'm hunting a elegant solution to a complex OOP design and yet it is still important to keep it simple.

So the idea I'm working with is to make use of objects in
a way that most parts of the code can be reused and
modular to a maximum.

EDIT: Look 4 posts below, ignore this following example.




Here is a selected part of the overall idea:
Type TShip
	Field Form:TRender2D = TRender2D.CreateForm( Self )
This is the visual representation of the ship, lying in the render-object called Form which looks like this:
Type TRender2D

	Field Owner:TObject
	Field X,Y
	Field Size = 4

	Function CreateForm( Owner:TObject )
		Local Render:TRender2D = New TRender2D
		Render.Owner = Owner
	EndFunction
	
	Method Update() ' Update position from Owner       
		DrawOval X-Size/2,Y-Size/2,Size,Size 
	EndMethod
			
EndType
'This is where the problem lies, I can never know for sure that the owner object (TShip in this case) have fields called X or Y. However anything that can be draw to the screen have to have a position and optinally even rotation.

I see two solutions to this, first is to embed the renderobject in TShip or in a TEntity type, and force the object to use the image's X,Y values. The downside is that it would mean any update code such as speed would look like: image.X :+ speed, and I want it to be X:+ Speed..

Another way is to have an entity type that Tship and any other object to be rendered have to inherit. And then I can pass TEntity to the Renderobject and it can use Owner.X. Which is pretty elegant, and for this example it would most likley be the perfect solution. Consider us having some other object that is not derived from TEntity, but it still has a X and Y field, or XPoss, YPoss. I would like to still be able to render that object (this way) without changing that object. Int pointers seemed to be removed, perhaps someone could suggest any smart way to do this, or any general tips?


Dreamora(Posted 2006) [#2]
The later way would be the way to go.

Don't see why there should be an object on screen that does not inherit from TEntity as Tentity in full abstraction is a point with position and rotation ... so anything that can be positioned and rotated ... :-)
Do you have a specific example what might fall in there?


FlameDuck(Posted 2006) [#3]
Or TShip could just extend TRender2D?


Tibit(Posted 2006) [#4]
Hm.. Yes, I guess anything who are to be drawn can extend TEntity, or extend TRender2D directly.

It does make sense that all objects that are to be rendered extends (somewhere in the line) from some renderobject. The basic of the idea of rendering was that ANY 2D object to be rendered need to have a position X,Y and a direction, deciding that by inheritance actually seems to be a logical step when I think about it.

I'll see where this leads from here :)

Thanks for the help


Tibit(Posted 2006) [#5]
Unfortunatly extending Render2D is not an option and it only works in the short run. I need a way to pass an object's field to another objects field. Doing it with as much OOP and as little code as possible.

This code below works, and it does exactly what I try to do. Yet I would like to make it simpler, less code, or in a more elegant way.

See below, is it possible to accomplish the same thing with more OOP or some smart technique? Note especially that I have to create a new find method in every new plant-type I create, which is a bit annoying ;)



H&K(Posted 2006) [#6]
Well for a start why are you overriding the Name Field in the base TPlant?
If TTree extends TPlant it already has a field name

Second you sould be overriding the New() method each child type of TPlant, 1) to call the earlyer news 2)create a Tlist for each type


Tibit(Posted 2006) [#7]
Overriding the name field is not a part of my final solution, it was a lazy way to not require to set the "default" name in the new() method. It works just the same I think.

Second the TTree and TFlower should probably be abstract.

The idea is that each of this objects is created once and all is put in a list once. A list with a lot of different objects of all different types.

The problem I have is that I need to search a TList for a object-TYPE not a specific object instance.

For example in TFlower I just want ANY TFlower that is in the global TList. When I have it I have successfully linked my Flower-Reference to the Base-Flower.

I'm not sure how to explain it really.. perhaps this is as good as it gets..


H&K(Posted 2006) [#8]
I still dont get why you dont have a tlist of all TPlants (for example), and Another Tlist for all TTree. So that All TTrees are in the TPlant list, but in there own list as well.

And I dont think that overriding Name is the same as using the base name, For one it means that there is an extra pointer in each variable (Ie each instance is bigger)


Tibit(Posted 2006) [#9]
Yea, but this example is to show the problem. I'm not really using plants like this :)

However if the above can be simplified by some smart inheritance then I could apply it to my engine the way I want it.

It might be that there is no way to put the eachin loop in TPlant, then I'll just have to live with it.


H&K(Posted 2006) [#10]
To be honest Ive been reading the thread, and I only joined in when I felt I understood what you wanted to do. But I still Dont.
If we look at your first post, Every type that contains render must have a x and a y , because render2d has an x and a Y. So I didnt understand the Problem.
The you gave a simplyfied example, that had (in my opinion) several oop mistakes.
I think that the problem is that you think OOP means simpler code, when in reality OOP means More code.

IF YOU ARE IN THE HABIT of putting each new type instance into a Tlist, then do this for them all.
Or you can put the Eachin Loop into Tplant by
Putting,
field WhatType into the base
then
setting whattype in each inherited type
const type_plant =2
new() WhatType = Type_plant
(I cannot be bothered with syntax)

Then when you loop though all the base types, one of its field tells you what the child actualy is


Cajun17(Posted 2006) [#11]
Someone already had a similar problem. Keeping a single list and being able to get a list of a type including children. I suggested using an adjacency list which is a list of lists (or an array of lists in this case) and I made a sloppy little example.

It's the 2nd to last post
http://www.blitzbasic.com/Community/posts.php?topic=51012

I think that's more or less what you're looking for, but I'm not quite sure.


Tibit(Posted 2006) [#12]
Great thread, a lot to read. It seems it is about the same problem I am struggeling with. I'll comment in a while. Thank you very much Cajun!


Tibit(Posted 2006) [#13]
If I could find a way to check:
If A:TPlant equal the very same derivied object-type B:TPlant, then.. For example If Palm(A) = Palm(B), but in general for any specific TPlant-type.

Cajun, this is your code from that thread. It might be a solution but to me it looks very complex. Can you explain it?

If I'm going to use it, what I am particulary interested in is to make any "new" types you make as simple as possible. In other words to keep as much of the "engine" in the inherited types, instead of all objects that is derivied.

Hm..




Cajun17(Posted 2006) [#14]
Sorry about the late reply. I was trying my hand at some linux installation and I had... troubles. Finally got it to work though.

Anyway this can probably be cleaned up (a lot). To use it as is though you need to do the following.
-The setupRelation() method only needs to be implimented once in the first object, and needs to be called once for the first created object of a type. This is just so the list knows what is a parent of what.
-The init method could be replaced by a new method... i can't remember why i didn't do that in the 1st place.
-The ForList function could be implemented once I think.
Both of the following could probably be implemented as global mapping of some kind so not every object would be carrying the same information.
-p_Type is the id of the parent object should be set in the new method
-o_Index is used internally by the list

It's late right now so in the morning I'll rework it and see what I can to to make it more userfriendly and efficient.


Tibit(Posted 2006) [#15]
Thanks, take your time. I got another idea to add next and previous fields to the super type and kinda make my own list system, but this one looks more promising and seems to work already :)


Tibit(Posted 2006) [#16]
Making my types into lists did not solve anything, but I think I have manage to get on the right side of things anyway. I have not understood and tested the Adjacency List Technique, but the above I tried to do is simply not possible in Blitzmax, perhaps not in any OOP language. I think the final solution looks much better, and is also much easier to manage and work with.

Cajun, I'm still interested in Adjacency Lists. If you rework it a bit, perhaps you could write a few sentences explaining the advantage of using it and perhaps some example situations where it would be more beneficial than a normal List.


Cajun17(Posted 2006) [#17]
To be honest I haven't touched a computer since I last posted and it feels kinda good :) ,but I'll get to it after I write something about why you would want to use them and reinstall bmax.

Adjacency Lists are mostly used for what the name implies, finding something that's adjacent so something. For example lets say you have a map of the world and we want to keep track of flights between major cities we would need a list for each city of destinations, but most of the time to get from city A to city D you can't get right there in one motion. You have go through C and B. With an AdjList you'd know that A->C->B->D with minimal searching and checking.

In the same way in a hirearcy of types you want to know all the children of A including the children of the children it's not obvious whose a child of whom in a plain old list with a constant. You'd have to loop though the whole thing every time to find what you're looking for if you even know exactly what you're looking for.

You need a child of A and let's say that B,C,E,F,J,X extend A. You would have to not only check every item in the list but check every item in the list for every child...
If _Type = B Then.... Else If _Type = C Then... etc.

Bottom Line: Adjacency lists are good for knowing how an object relates to another if at all. For a small number of object instances ( < 50 ) or object types ( <5 ) I would consider doing without, but for large amounts of data it's exponentially faster than a notmal list.

Extra Info: Also cost can be implemented into the list so that you could find a cheapest route like in the city/flight example. Or take an RTS game the AI would want to know not only cost in time to move from A to B but also potential damage cost so the cheapest route could mean 2 different things.


Cajun17(Posted 2006) [#18]
Ok I went through it and some of the things I thought I could get rid of were because I've been in C++ for the past 9 months. Anyway I was able to remove a field from the base AdjType that needed to be extended so that brings the foot print down to 8 bytes and if you turn them into shorts 4 bytes. I'd say that's reasonable.

Go over the code and comments and let me know what you think.

Here's the list container, it's Iterator and the type to extend


This is a commented example



Tibit(Posted 2006) [#19]
Thanks!

About the use of the Adjacency list. It seems it could be of very good use for a node based AI or bot system.
Makes it both easier and faster to make decitions.

I'll go trough and check the code later today and then make a "real" reply ;)


Tibit(Posted 2006) [#20]
I really liked the idea with a node based list. I find the greatest use for this in making an AI. I have still not gotten full understanding of how it works, and how I should use the list system. I think the reason I have problem to understand is that your example is quite abstract, no offence meant, I appriciate the effort very much. I tried to think of A,B,C,D,E and so on as Cities which is connected by flightpaths. Could you explain the example like that? I do not understand the use of inheritance in this, or that how I create some sort of relationship? Or can the types be totally unrelated- no inheritance? Or what if all of the types are of the same type: City but different instances of city? How did you mean that I could add a cost to each node? For example length, benifit or danger on relationships between nodes.

'With this type I will show that the list can be purely logical
'In other words you could have one type and implement a hierarchy logically
I like the sound of this. Is this by using addRelationship(a, b). Also is the relationship between instanced objects or between the base types? Can you give an practical example on that?

By having different nodes of different types, do you mean something like the node being a city, but also a base or a waypoint. All which are nodes but of different types?

Except for AI, any other good use for this?

Many thanks for sharing this, I hope I can make some great use for this. If I dynamically can create relationships between nodes and dynamically strengthen or weaken links then this has the potential to be a base of very smart AI's.


Cajun17(Posted 2006) [#21]
No problem on the sharing of this. I'm glad my hours of seemingly pointless Computer Science theory classes has payed off. And sorry about the abstract explanation. I blame formal education.

To really give you a full exaplination I think I should start at the beginning and I don't have much time right now. On Monday I'll come back with all the answers to your questions.


Tibit(Posted 2006) [#22]
Great, thanks :)