Safely call unpredictable instances
BlitzMax Forums/BlitzMax Programming/Safely call unpredictable instances
| ||
Hi Sometimes I need to refer on unpredictable instances of Field and Global from Singletons. It happen that those Field or Global need to get instanced further in my program logic (think about a tweening type that nullify himself after a certain time). The question is : How can I safely access those Field or Global from every methods or functions without testing their existences first ? Why would I do that ? Because when interacting with multiple objects I often need to get feedback and I would like to be able to do something like that : if tween.getStatus() = TWEEN_STOPPED then ... rather than if tween if tween.getStatus() = TWEEN_STOPPED then ... endif Is there any design Pattern to realise such a thing ? I know you could always instantiate your Field or Global directly inside the Type. But what if this instance get nullify then ? Here is an example of what I mean (bad code but that's just an example) ' TVector is a simple example Type Type TVector Field X:Float Method SetX(X:Float) Self.X = X End Method Method GetX:Float() Return X End Method End Type ' VectorManipulation is a Singleton Type Type vectorManipulation Global vector:TVector ' I could initialize here or anywhere else but that's not the point Global instance:vectorManipulation Method New() If instance Throw "Cannot create multiple instances of Singleton Type" EndMethod Function Create:vectorManipulation() Return Self.GetInstance() End Function Function GetInstance:vectorManipulation() If Not instance Return New vectorManipulation Else Return instance EndIf EndFunction Function Init() vector = New TVector vector.SetX(10.0) End Function Function printVector_SAFE() If vector Then Print vector.GetX() ' [ ? Is there a way to safely try vector.GetX() without testing vector existance ? ] End Function Function printVector_UNSAFE() Print vector.GetX() End Function EndType ' [EDIT] EXAMPLE for non Singleton (because this could also happen with normal type). ' I would call vectorManipulation directly in other cases... Dixit Nilium Local VM:vectorManipulation = New vectorManipulation ' 1) Let say I did't init my Singleton anywhere VM.printVector_SAFE() ' 2) The other case now : I init my Singleton and so I can use Unsafe version of the function VM.Init() VM.printVector_UNSAFE() So basically it's just a question on how correctly use your object from the "outside". Thanks |
| ||
Couldn't you just make sure something is initialized before using it? I don't really see why this is an issue for you, especially in the case of a singleton. E.g., in the constructor, call Init() or something, or just initialize the global when the program starts. Edit: Also, why are you creating an instance of vectorManipulation if the methods you're calling are static? There are so many things about your post that are just downright confusing... |
| ||
Tanks for the answer Nilium As I say in this post, this is just an example that do Dumb things. To clarify things you know. When your code become larger and larger this kind of situation could appear and it's not only restricted to Singleton (I should have mention that but it was to get as close as possible of the given example). Sorry if it's confusing since I'm here to try to find solution to a bad code design. Objects live and die, particularly in games ! That's why, as you suggest, I can "make sure a right initialization occur" but I can't trust that every inheritance (or if my type as several other type inside) will last forever. That's particularly true if you are using external C++ engine (like Box2d that visually displace your object but require a postponed Object/body destructor) or using threads that can occur at any given time and could break this initialization (I thing). I know it's hard to understand but if you focus on the question rather than the (bad) given example there is probably an answer for it. |
| ||
That's why, as you suggest, I can "make sure a right initialization occur" but I can't trust that every inheritance (or if my type as several other type inside) will last forever. Sure you can. Stick everything in a list/map. |
| ||
I dont know what you are saying but I don't see whats wrong with checking to see if it exists before checking to see if its equal to something... in fact that should speed up your code slightly depending on how many dead objects you have. |
| ||
It's pretty simple indeed: make your fields or globals available by methods or functions and use only those methods instead of accessing the fields or globals directly. Your accessor (or getter) methods can easily assure that the fields or globals are properly set. You already did that in your vectorManipulation example (printVector_SAFE()). This way of initializing fields/globals only when they are needed is a common OO principle and is called lazy initialisation. Unfortunately BlitzMax doesn't offer private fields and doesn't forbid access to them but force you to use methods so it's the programmer's task to only use the accessor methods and not the fields directly. Hope that helps, Tommy |
| ||
There is no way out, either you use the lazy instanciation way and have to check - or you have to be sure that your design does not allow null members in objects. btw. as noel said, your "singleton" type is creating multiple instances in get instance, which is a little bit suboptimal for a singleton |
| ||
Thanks for your advises :) Accessors is something I progressively learn to use in my program and you right TommyH testing my field inside the getter method shall reduce many of these "external other test". I will try to systematically use them even if there is no handy setter/getter replacement for BlitzMax (that would entirely fix the problem). Although Accessor won't give me solution when object are null in certain cases. Let's take this example : 'This is what I do for the moment 'GetPosition is a box2d C++ Method and obviously can't be modified in order to verify the existence of body ... if ball.body ball.body.GetPosition() endif ... ' But using Accessors won't help me in this case... Field body:b2Body = New b2Body Method GetBody:b2Body() If body Then Return body end Method ... ball.body = Null ball.GetBody().GetPosition() 'Will crash since GetBody will return null ... But I think there is nothing else to do as far as I can't modify the C++ Accessor... It's also good to know about the term of Lazy Initialisation. I'm sure this will help ! |
| ||
Armitage1982, in your last example all access to the body field should be wrapped and hidden inside the Ball class. So the case you mention will never happen or can be considered a design issue or a programmer's bug. Your Ball class code always has to make sure that you can only access the body's position if you already have an existing body. Users of the Ball class should never be able to acess the body's position directly but that should be wrapped again with a getter method like this: Type TBall field body:b2Body field x:float field y:float ... Method getBody:b2Body() if body = Null body = New b2Body body.setPosition(x,y) endif return body end method Method getPosition:TPos() ' will never crash because getBody() guarantees an existing body! self.getBody().getPosition() end method ... end type No one forbids to code smart getter methods ;-) |