Singleton : Fields vs Globals
BlitzMax Forums/BlitzMax Programming/Singleton : Fields vs Globals
| ||
I have a singleton(-ish) type. What is the difference between using fields and globals within the type? |
| ||
Well, you can access globals from other parts of the program without referencing to (or knowing about) any instance of the type. If you have a singleton type, you can always declare the type instance itself as global, so I guess it really makes no difference. Performance-wise, I'm under the impression that only true program globals are significantly faster to use and access than local variables, and globals inside types aren't. So there's really no performance gain in using globals within type. |
| ||
Other than being able to access the global before the singleton is instantiated I don't know. *EDIT* I suppose a possible reason to use globals rather than fields could be that a module that uses the same singleton class can access them without knowing the name of the instance (but it's not really that useful). |
| ||
a module that uses the same singleton class can access them without knowing the name of the instance (but it's not really that useful). That's why I was using globals to begin with rather than use a Get. I've switched back to using fields as that *feels* more correct. |
| ||
Just playing around, this seems to function just fine.Type TSingleton Global singleton:TSingleton Field value:Int Function Create:TSingleton() If singleton = Null Then singleton = New TSingleton Return singleton End Function End Type Local a:TSingleton = TSingleton.Create() a.value = 100 Local b:TSingleton = TSingleton.Create() Print b.value |
| ||
You could access your singleton from anywhere without having an instance of the object if using globals and functions within the type, so I think it depends on the structure of your program, because having one instance of an object and accessing its fields would be similar to accessing globals of the type, except you don't need to have an instance for one. |
| ||
Wow pert, That's pretty nasty code lol. Doesn't do at all what the user would think if they didn't look at the Tsingleton class code directly. [edit: Actually Pert has a good idea, so I retract the preceeding statement!] If you are creating a singleton twice, your code is wrong, so instead of "return singleton" I'd have something like a Runtimeerror stating you're using the class wrong. Way I see it, once a class is completed, you shouldn't need to go back to that code. a Summary comment will (should...) show in the IDE as you're typing and this should be all you need to use the class. Sadly, going off on a tangent, Blide is the only IDE I know of that makes use of Summary, and it only works in Windows. :-( here's how I see it: A global is persistent throughout all instances of a class, Which makes it useful to store something like a list of those instances in a Global. TPlayer.GetPlayerlist() for example returns the Tplayers global List. 'Example Singleton. This is one of two ways, and you can use a Field just as I have used a Global, 'but you would need To keep a reference of what was created. 'The advantage of using the Global is that the code can simply be Tsingleton.GetInstance() 'instead of having a global instance elsewhere in the code, it is kept tidily in the Type interface. Type Tsingleton Global Instance:Tsingleton 'Summary: Constructor- Create the Tsingleton singleton. Function Create:Tsingleton() 'You could also overload the New function... If instance=Null Then instance=New Tsingleton Else RuntimeError "TSingleton is a Singleton. You may only create one instance!" EndIf End Function Function GetSingleton:Tsingleton() Return Instance End Function End Type 'Example use of Global in a none Singleton: Type Tplayer Global PlayerList:TList Field Name:String 'Summary: Constructor- Create a player Function Create:Tplayer(name:String) 'You could also overload the New function... Local player:Tplayer=New Tplayer If Playerlist=Null Then playerlist=New TList playerlist.addlast(Player) player.name=name Return Player End Function 'Summary: Retrieve a list of players. Function GetPlayerList:TList() Return Playerlist End Function 'Destructor Method Delete() Playerlist.remove(Self) End Method 'Summary: Get this current players name. Method Getname:String() Return Self.Name End Method End Type 'Correct use of Singleton with this code: 'Create the Tsingleton instance. Tsingleton.Create() 'Call a second time? (should error and tell the coder he's cocked up! 'Tsingleton.Create() 'Correct use of Tplayer: Global Player1:Tplayer Global Player2:Tplayer Player1=Tplayer.Create("Damien") Player2=Tplayer.Create("Tracy") 'Code that doesn't know what players have been created: Local Playerlist:TList=Tplayer.GetPlayerList() For Local Player:Tplayer=EachIn Playerlist Print "Look! I can see "+player.getname()+"!" Next |
| ||
If it is a single instance type, there's no need to have any instance. yo can declare all as global and use functions. Even declare the type as abstract. That's the closest you'll get to create a shared object on BlitzMax |
| ||
It's kind of offtopic but that's the way I learnd how to use a singleton:Type TSingleton ... End Type Global FMySingleton: TSingleton Function MySingleton: TSingleton If FMySingleton = Null Then FMySingleton = New TSingleton End If Return FMySingleton End Function 'Somewhere in your code MySingleton.SomeValue = 1 MySingleton.DoSomething() You do not have to care about if your object was created or not. Also you always have just one instance and it will only created if you really need it. |
| ||
@Cygnus: That's my interpretation of how a singleton is supposed to function. i.e. a pattern that returns one instance and ensures you can't instantiate another. A runtime error would be a bad thing since what happens if you have a singleton used in two different modules within the same application? Incidentally, your one requires that you already have the instance of the singleton when calling getSingleton since it's a method you're using not a function. i.e. you'd have to do: local a:TSingleton = TSingleton.Create(); Local b:TSingleton = a.getInstance(); Which is a tad pointless since you already have instance a. The only difference between the code I wrote and the intention of your one appears to be that I used the create function to return the instance rather than encapsulating the return in a separate function. Of course the create function could easily be renamed to getInstance and it would be exactly the same code but for some reason you'd have no problem with it. The reason I use create is because all of my types use a create function. |
| ||
Oops! That SHOULD indeed be a function! ;-) Ta for spotting that! Also, you're right regarding using a singleton with many modules, it's not something I've had to do, and it's a very valid point. I also use Create. The above code (modified now to remove my bug!) is an example of the way we've built our Flow demo and it helps with fast development and also, if you decide to change the underlying engine, the interface doesn't have to change as long as you provide get/set methods. :-) I DID have a rethink after Ratchet posted the code above and you are also right with Create/GetInstance(). They might as well all be the same code, it would make no difference. By the way ratchet, you should probably put your functions in the type! In short, you've persuaded me that your way of using the Singleton is in fact valid and has more uses than my own code. :-) Saying that, the Tplayer stuff in my code is also pretty handy for others ;-) |
| ||
In short, you've persuaded me that your way of using the Singleton is in fact valid and has more uses than my own code. :-) Good, I was beginning to doubt myself for a moment there :) Saying that, the Tplayer stuff in my code is also pretty handy for others ;-) It does yes, that's actually very similar to the way I manage things like that (static list inside the type, etc.) |
| ||
I still prefer how I do it, I can just see how yours works. I would have a problem with your code should the Singleton be for example the setup class of a 3D engine, where its parameters are set up at the start. If modules are used, and the instance doesn't exist, then there's a problem still, so it would be better to error out on Getinstance() when there is no instance, and to error out on a multiple create(). For example: If someone tried to make two instances of our Tgame class, it should error out because it doesn't make sense that it happened twice. You cant put the create() code in Getinstance() because it needs setting up with parameters. Now, if you handle it by creating the instance and setting it up via other functions, fair enough. if all the create method is doing is creating a reference, then off you go, but somewhere, the parameters will need setting before some other code craps out without a meaningful message. :-) |