Casting objects - Polymorphism
BlitzMax Forums/BlitzMax Beginners Area/Casting objects - Polymorphism
| ||
Hello, let's take this code: SuperStrict Type TAlien Field health:Int = 1 ' Method PrintHealth() End Method End Type Type TAlienSmall Extends TALien Field health:Int = 2 ' Method PrintHealth() Print "Method PH in TAlienSmall" End Method End Type Type TAlienBig Extends TAlien Field health:Int = 3 Method PrintHealth() Print "Method PH in TAlienBig" End Method End Type ' Main Local alienlist:TList = New TList Local as:TalienSmall = New TAlienSmall Local ab:TAlienBig = New TAlienBig alienlist.AddLast(as) alienlist.AddLast(ab) For Local alien:TAlien = EachIn alienlist Local castedalien:TAlien = TAlien(alien) ' casting type of object ' castedalien.PrintHealth Print castedalien.health Next With the line "Local castedalien:TAlien = TAlien(alien) " I can call the right methods of the objects, but how do I get access to the fields of the objects that are not Baseobjects? I can use a method like "ReceiveHealth:Int()" which will return the correct value(2,3) but is there a smarter way? |
| ||
Don't have a 'Field Health' in your baseclass. This might help. |
| ||
Thanks. This works too with 'Field Health' in the baseclass defined. But thats what I want to avoid; defining a method for every field I want to access. If the object has a lot of fields I can define methods like ReceiveHealth(), ReceiveDamage() and they will return the right value. But that can be a lot of methods. |
| ||
Using those Methods (usually called Setter and Getter) are used in many OO-Languages. It gives you the opporunity to Check any of the values you want to set. It gives you the opporunity to return values independent of your base implementation of the class. One of the Basic priniples in OO ist to keep the inner values a secret, and let in only change by the methods you want it to. unfortunately this is not really implemented in bmax, c# and java use modiefiers so you can simply change the permission of every single class member |
| ||
Using those Methods (usually called Setter and Getter) are used in many OO-Languages. Now I know why every object in WxMax has such a bunch of methods.. One of the Basic priniples in OO ist to keep the inner values a secret, and let in only change by the methods you want it to. Thanks for the explanation, I will do it this way to keep my code encapsulated. |
| ||
I disagree... a little. the point of encapsulation is to hide parts of the interface that are likely to change or that other shouldn't be touching. that does [edit]not[/edit]translate into "don't directly access variables" if we had Private and Public options for variables this wouldn't be an issue. now on the other hand... always using setters and getters does give you many advantages and keeps your interface consistent but since BMax doesn't have Properties it can be quite cumbersome. The author of the type or class should be free to implement a module in any way which fulfills it's requirements. Functions do add overhead and as mostly game programmers here you might need the tightest loops. there is no problem with directly accessing the field as long as you stay consistent in your design. to answer you first question... I can call the right methods of the objects, but how do I get access to the fields of the objects that are not Baseobjects? only by casting them to the correct type. which you did not do in your example. your for loop casted the returned objects from alienlist to TAlien. your next line did the exact same thing for castedalien. you need to know what and why you are casting... to access those fields you need to directly cast to TAlienSmall or TAlienBig. but that's not what you want here.... my first question to you would be; why did you think you need a health field in each type? doing that means you are overloading the field that's already in the base. you can do this BUT there is generally no reason to and it is most often a product of bad design and should be avoided. all your aliens have health right? that means it should be in your base ONLY. here is what I would do. (without reflection) SuperStrict Type TAlien Field health:Int = 1 Field name:String = "Alien" ' Method PrintHealth() Print "Health in "+name+" = "+health End Method End Type Type TAlienSmall Extends TALien Method New() health = 2 name = "AlienSmall" End Method End Type Type TAlienBig Extends TAlien Method New() health = 3 name = "AlienBig" End Method End Type ' Main Local alienlist:TList = New TList Local as:TalienSmall = New TAlienSmall Local ab:TAlienBig = New TAlienBig alienlist.AddLast(as) alienlist.AddLast(ab) For Local alien:TAlien = EachIn alienlist ' alien.PrintHealth Print alien.health Next and as you continue to code you will probably find it's easier and more convenient to use Create(blah:blah,blah:blah) *functions* in addition to New() *methods*. Also, encapsulating your lists, especially your base list (but I say all of them) will gain you many advantages. I can show you that for this example if you would like. |
| ||
Correct dmaz :) Sometimes I am too blind. Of course health should only be in the base type. Sorry for the misinformation |
| ||
all your aliens have health right? that means it should be in your base ONLY. My TAlien's baseclass was a class TUnit. And a TUnit has no health. So I defined health in every class and used the creator new(). That caused trouble. The way I did it: With health defined only in TAlien my code works fine. ... to access those fields you need to directly cast to TAlienSmall or TAlienBig. but that's not what you want here.... Can I have an example? I'm sure I will need it in the future. Also, encapsulating your lists, especially your base list (but I say all of them) will gain you many advantages. I can show you that for this example if you would like. Would be appreciated. |
| ||
Why do you need to extend all these different kinds of alien? What's wrong with one alien type, with a health field (and strength/image/etc fields) but with their own particular values. Then just have a create alien function with the stats of the desired alien as parameters. You don't need a new type for every type of unit in the game! Just one type can represent them all. |
| ||
These are two valid casts based on your class hierachy:Local BigAlien:TAlienBig = TAlienBig(aVariableofTUnitType) Local BigAlien:TAlienBig = TAlienBig(aVariableofTAlienType) |
| ||
Why do you need to extend all these different kinds of alien? Because they all can do very different things. Follow a path or a complex set of orders, trace the playership, start like a rocket in 'scramble', fly sinuscurves.. This would be a very big update method if I only use one alien. I could combine some of them, but I use this project to grok OOP. |
| ||
You are still putting health in the wrong place. (well, not wrong, but ill advised place) IF Every derrived object is going to need access to a specific field name. ie Health, then it shuold be in the base class. With health defined only in TAlien my code works fine We know My TAlien's baseclass was a class TUnit. And a TUnit has no health Well. Are you sure? If you are sure that SOME units will not have health then fair enough put health in TalienHowever, TAlien Small has TWO health fields ATalienSmall.Health ATailienSmall.Super.Health And its the ATalienSmall.health set to 99 SO it should ONLY be in Talien Edit. I should have read the whole thing before I posted. Oh well |
| ||
Edit. I should have read the whole thing before I posted. Oh well My posting was a bit misleading. Well. Are you sure? If you are sure that SOME units will not have health then fair enough put health in Talien TUnit is the baseclass for ALL graphical objects. I read it here in the forums to derive all of them from one baseclass. And not all all of them have health. |
| ||
Here is something interesting you could do, have whole bunch of seperate movement types containing the methods for that type of movement, all extended from a base movement type. Then each game item has a field that momvent type. Then you just do alien.movement.update() It sounds complicated but might simplify things. You can just use one alien type but, esentially, plug-n-play with completely different movement and even extend this to attack methods as well. |
| ||
I have something similar to this. A Type TOrderUnit in which two lists of meshcommands (TOrders)like move, translate, rotate, scale, delay etc. are executed over time. Two of them so I can rotate and move the same time. I have kept it meshspecific, so it can be used in later projects. There is even a simple Editor to create this lists and test them :-) @dmaz: What about this?: Also, encapsulating your lists, especially your base list (but I say all of them) will gain you many advantages. I can show you that for this example if you would like. |
| ||
just put the list as a global in the type... but you also want to add create functions and remove methods. you want remove methods for sure because we -very- much want to store the TLink so we can quickly remove the item from the list when dead. here I'll add the remove methods... in the example above I only put a remove method in the base type but you should get in the habit of having a remove method in each and every type for any cleanup that -might- need to be done. in the extend types the remove method should clean up anything that it needs to and then call "super.Remove" to run the base remove method. [EDIT] I added all the remove methods just to be clear... you don't need them in this example but it's just a better habit to get into. |
| ||
The global list in TAlien is only created once when the first time an TAlien object or an instance of it is created. Right? If I would use just an "list:tlist=new tlist" a new list is created each time a new TAlien is build!? Another point: I have no clue why the constructor New() in TAlien, which adds the object to the list, is executed by creating: "New TAliensmall". Is the Super Type Object Creator called additional to the one in the TAliensmall instance ? |
| ||
Is the Super Type Object Creator called additional to the one in the TAliensmall instance ? All New() methods are called automatically in the hierarchy. Just like constructors in Java. |
| ||
"global"s in a type are not associated with an instance but with the type itself. think of them being initialized at program start. just like functions you can access before any instances are created. TAlien.list.ClearList is valid and so would be TAlienBig.list.ClearList. like Brucey said, New() is special and all of them will be called in order with out using super. If I would use just an "list:tlist=new tlist" I'm not sure what you are asking here? a new list is created each time a new TAlien is build!? |
| ||
just like functions you can access before any instances are created. Oh yes, I see: Type TAlien Global list:TList = New TList End Type Print TAlien.list.Count()works without creating an object. I'm not sure what you are asking here? Doesn't matter. The question is already answered in your first sentence. |
| ||
just put the list as a global in the type... @dmaz Just finished spending half a day tracking down a memory leak. Just for one small little object I had no internal list and remove method; instead I got a cyclic reference. The way you showed to encapsulate the lists really works perfect and is a great help for cleanup. Thank you! |
| ||
you're welcome... I'm happy to help. |