Polymorphism help...

BlitzMax Forums/BlitzMax Beginners Area/Polymorphism help...

.rIKmAN.(Posted 2011) [#1]
Hey all,

I am trying to play with OOP, Inheritance, Polymorphism etc, creating small samples to play with, but I have hit a brick wall.

I am creating a "TFruit" type, and extending 'TApple' and 'TBanana' from this with a unique type field each.
When I create the 'TApple' and 'TOrange', I add them to the global 'TFruitList'.
I then try to iterate through 'TFruitList' to list the data from ALL fruits added to this list.

Here is my test code...
Strict

Global TFruitList:TList = CreateList()
Type TFruit
	Field colour:String
EndType

Type TApple Extends TFruit
	Field core:Int	
EndType

Type TBanana Extends TFruit
	Field bend:Int
End Type

Local a:TApple = New TApple
a.colour = "Red"
a.core = 1
ListAddLast(TFruitList,a)

Local b:TBanana = New TBanana
b.colour = "Yellow"
b.bend = 15
ListAddLast(TFruitList,b)

For Local c:TFruit = EachIn TFruitList
	Print c.colour
	'Print c.bend
Next


Now, if I uncomment the "Print c.bend" line, I get an error, because 'c.TFruit' does not contain the 'bend' field variable - this is only in the 'TBanana' type.
If I use 'c.TApple' or 'c.TBanana' in the 'EachIn' loop it only returns those types, and I want a full list of all TFruits.

I can't even give the TFruit an ID and test if it is a TApple or TBanana using this in the loop as it give me an error...

For Local c:TFruit = EachIn TFruitList	
	If c.id = 1 
		Print c.colour
	EndIf
	If c.id = 2 
		Print c.bend
	EndIf
Next


This seems so simple but I have been racking my brains for too long now, can someone please enlighten me?

Thanks in advance!

Last edited 2011

Last edited 2011


SLotman(Posted 2011) [#2]
What you need is object casting:

For Local c:TFruit = EachIn TFruitList	
	Print c.colour
	if (TBanana(c)) then Print TBanana(c).bend
Next



Jesse(Posted 2011) [#3]
the idea about objects is that objects are supposed to be encapsulated. everything relating to the object is supposed to be resolved by itself. if you let each object take care of everything related to it, you won't have to worry about casting. there are instances in which you will need to do it but for the most part it should take care of itself:

Strict

Global TFruitList:TList = CreateList()
Type TFruit
	Field colour:String
	
	Method display()
		Print colour
	End Method
EndType

Type TApple Extends TFruit
	Field core:Int
	
	Method display()
		Print "apple"
		Super.display()
		Print "core "+core
		Print "---------"
	End Method
EndType

Type TBanana Extends TFruit
	Field bend:Int
	
	Method display()
		Print "banana"
		Super.display()
		Print "bend "+bend
		Print "----------"
	End Method
End Type

Local a:TApple = New TApple
a.colour = "Red"
a.core = 1
ListAddLast(TFruitList,a)

Local b:TBanana = New TBanana
b.colour = "Yellow"
b.bend = 15
ListAddLast(TFruitList,b)

For Local c:TFruit = EachIn TFruitList
	
	c.display()
Next


Last edited 2011


.rIKmAN.(Posted 2011) [#4]
Thanks for the replies guys, appreciated.

Both your codes work - although I am not really a fan of OOP (I guess cos I'm new) and it all feels a bit alien atm, working myself into knots as I'm unsure of a lot of things, and have old habits and thinking.

I mean I understand types, classes, lists etc, but it's the methods inside a type/class and how they all relate to each other - as I said I end up tangled in knots with errors everywhere.

SLotman's code is more up my street as I understand it straight off and can get on with coding the game, rather than getting tangled up in OOP syntax and principles.

Is there any difference is the speed of the code provided above, is one way or the other better speed/cpu/ overhead-wise?

Last edited 2011


Jesse(Posted 2011) [#5]

Is there any difference is the speed of the code provided above, is one way or the other better speed/cpu/ overhead-wise?


OOP has its advantages and disadvantages, such as processing all the calculation with its corresponding variables is faster than doing the calculations outside of the object. to call a method just to return the value of a single variable is worst than doing several calculations outside of the object's method.
also this code inside a method :
x = 30+dir*speed

is faster than this code outside the object:
car.x = 30+car.dir*car.speed


in most cases the difference in speed is so small that it's not worth mentioning.

Last edited 2011


.rIKmAN.(Posted 2011) [#6]
Awesome, thanks Jesse, much appreciated! :)


Czar Flavius(Posted 2011) [#7]
I have extensively used OOP in my giant game and not noticed any performance issues from it. Performance problems are usually from drawing too many graphics or updating too many particle effects for me.

But Jesse, why do you say the variables are faster inside the method? There is still an implicit dereference of the Self reference.

rikman, the reason why this code doesn't work
For Local c:TFruit = EachIn TFruitList
	Print c.colour
	'Print c.bend
Next


is because the for loop doesn't know what to do if it gets a c which isn't a banana. If c is an apple, there is no bend field so what does it do? You need to think about what it is you want to happen.

The idea of Jesse's code is that each fruit gets a display method. Each type can change the method to do something different. So banana's method prints its bend and apple's method prints its core. The for loop can now work with c.display(), as every fruit has a display method and it knows what to do for any type of fruit c might be.

The Super.display() is a more advanced OOP feature. It means to call the display of the parent object, in this case TFruit's display method.

This convert's TApples method from this
Method display()
		Print "apple"
		Super.display()
		Print "core "+core
		Print "---------"
	End Method


To this

Method display()
		Print "apple"
		Print colour
		Print "core "+core
		Print "---------"
	End Method


So if you add something more to TFruit, which will be automatically shared by everything extended from it, you can add the code to print out the new fields in TFruit's display, and the other types which call Super.display() will display them too. Otherwise, you'd need to go around updating all the displays to add the new common fields to print. I hope this explains it for you.


Jesse(Posted 2011) [#8]

But Jesse, why do you say the variables are faster inside the method? There is still an implicit dereference of the Self reference.


I did some test years ago and consistently game me better results. It still do:
Strict
Type Tcar
	Field x:Float
	Field y:Float
	Field speed:Float = 10.0
	Field dir:Float = 90.0



	Method update()
		Local m:Double  = MilliSecs()
		For Local n:Int = 0 To 50000000
			x =  30 + dir*speed
		Next
		Print MilliSecs()-m

	End Method

End Type

Local car:Tcar = New Tcar

car.update()
Local m:Double = MilliSecs()
For Local n:Int = 0 To 50000000
	car.x = 30 + car.dir*car.speed
Next
Print MilliSecs() - m

in my laptop I get a continuous difference off 19 milliseconds faster when in the method than outside for the 50 million iterations. as I said it's not a significant number but it is true.

laptop description below.

[edit]
this is funny, I guess I never did thorough tests. it seems that debug mode is faster for in method calculations while release mode is faster from outside the method calculation for up to 12 milliseconds difference.

Last edited 2011


Czar Flavius(Posted 2011) [#9]
At a guess, in debug mode it does checks every time you deference an object to see if it exists, so it's slower. Inside the method, Self must exist so it doesn't need to check again.

Interesting test! On my computer I get for debug mode:
Met 2129
Out 1911

Release mode
Met 63
Out 62

Not only is the method slower in both cases, the speed is RIDICULOUSLY greater in release mode. I don't know what's going there. I ran your code exactly as you presented it!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Please post your results


Czar Flavius(Posted 2011) [#10]
There is something very strange here. fupdate takes 10 times longer than any of the other methods.



Edit: I just realised, it has an extra 0! Doh! I'll leave it so you can amuse yourself with my stupidity :D

Last edited 2011


Kryzon(Posted 2011) [#11]
Quoting Mark Sibly: "That's gotta be one of the flakiest speed tests I've ever seen" :D


col(Posted 2011) [#12]
@Kryzon

:D:D:D:D


Jesse(Posted 2011) [#13]
humiliating?
sorry, we are not all perfect.


.rIKmAN.(Posted 2011) [#14]
Thanks for the replies guys, not 'quite' got my head around OOP yet, having all the methods inside the type, and some scope issues are still throwing me a little, but I know it makes more sense - just need to keep practicing.

Gonna make a few smaller test games (snake, pong etc) sticking to OOP principles and hopefully after a few of them I'll have it down and be able to implement it in my proper project.


col(Posted 2011) [#15]
Hey Jesse.
No one's perfect and I most certainly am NOT perfect at anything in life, let alone this field!!

I wasn't laughing at you, I was laughing at the quote. I would never have posted it myself and I just found it funny.

Hope I didn't offend you!!


Jesse(Posted 2011) [#16]
I guess I miss interpreted your comment. Personally I think it's misleading.

It did offend me slightly at first but now that you cleared it, Its no problem then.