Safely call unpredictable instances

BlitzMax Forums/BlitzMax Programming/Safely call unpredictable instances

Armitage 1982(Posted 2009) [#1]
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


N(Posted 2009) [#2]
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...


Armitage 1982(Posted 2009) [#3]
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.


plash(Posted 2009) [#4]
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.


Nate the Great(Posted 2009) [#5]
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.


TommyH(Posted 2009) [#6]
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


Kurator(Posted 2009) [#7]
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


Armitage 1982(Posted 2009) [#8]
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 !


TommyH(Posted 2009) [#9]
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 ;-)