Using TV3D Classes as Singletons

It has been highlighted in the Beta Discussion forum by several users that the TV3D “global” objects, those who theoretically can’t have more than one instance, could be used without sharing the only instance in the whole application. This is the singleton design pattern, in which you can’t really construct a class instance explictely, but only fetch the current instance; if this instance is null, the class creates it internally and returns that instance.

Though instead of the private constructor & instance accessor technique, TV3D lets us use the constructor on the classes, but abstracts the actual inner instance that’s used in order to give us the right one.

It should be pointed out that singletons are considered an anti-pattern and should only be used if you are absolutely 100% certain that you only need a single instance of an object. In 99.9% of cases there is no reason to require a single instance of an object. Use of the singleton pattern is a controversial subject amongst developers and should only be used with the greatest of care as they can introduce issues, particularly with multi-threading. See this link for more information. If you feel the need to use global access to your objects, then you really should reconsider your design choices. If you don’t want to do that then consider that the singleton pattern is most often used as a global object reference and that if you really absolutely need a global, then use a global. At least then you won’t be stuck with only one instance.

Now that the doom and gloom is out of the way, feel free to continue at your own risk.

The singleton classes

Here’s the list of classes that can be used as singletons, confirmed on December 16th 2006 by Sylvain (lead dev. of TV3D) :

  • TVAtmosphere
  • TVCameraFactory
  • TVGlobals
  • TVInputEngine
  • TVInternalObjects
  • TVLightEngine
  • TVMathLibrary
  • TVPhysics
  • TVScene
  • TVScreen2DImmediate
  • TVScreen2DText
  • TVTextureFactory
  • TVMaterialFactory
  • TVGameControllers

What about TVEngine?

TV3D does a ReleaseAll operation in the destructor of CTVEngine, so you can’t have more than one instance of it. It makes the application crash on random code, on seemingly random occassions...

The main idea

Instanciate All TV3D Singleton Objects Wherever You Need Them.

Instead of sharing the instances in a shared/static/global class/module somewhere in your code, just create them (instanciate them!) in every code file you need to, or every package/namespace.

Code comparison

Here’s the procedure in several languages. I encourage everyone to add the language they used to implement it.

In Visual Basic 2005

Note : All Modules could be Shared Classes instead (and then all Sub New and other methods should be Shared). Also I do realize that all “TV3DSingletons." prefixes are optional, but if you use Shared Classes you’re obliged to use them. Anyway it’s clearer about where the object comes from...

Manage the instances ourselves (the usual way)

Module TV3DSingletons
  Private mScene As TVScene
  Private mGlobals As TVGlobals
  ' Etc.
 
  Sub New()
    mScene = New TVScene()
    mGlobals = New TVGlobals()
    ' Etc.
  End Sub
 
  ReadOnly Property Scene() As TVScene
    Get
      Return mScene
    End Get
  End Property
  ReadOnly Property Globals() As TVGlobals
    Get
      Return mGlobals 
    End Get
  End Property
  ' Etc.
End Module
 
Module World
  Sub Render()
    TV3DSingletons.Scene.RenderAll(True)
  End Sub
End Module
 
Module GUI
  Private Mesh As TVMesh
 
  Sub Initialize()
    Mesh = TV3DSingletons.Scene.CreateBillboard(TV3DSingletons.Globals.GetTex("Foobar"), 0, 0, 0, 10, 10)
  End Sub
End Module

Let TV3D do it for us (the better way)

Module World
  Private Scene As TVScene
 
  Sub New()
    Scene = New TVScene()
  End Sub
 
  Sub Render()
    Scene.RenderAll(True)
  End Sub
End Module
 
Module Gui
  Private Globals As TVGlobals
  Private Scene As TVScene
 
  Private Mesh As TVMesh
 
  Sub New()
    Globals = New TVGlobals()
    Scene = New TVScene()
  End Sub
 
  Sub Initialize()
    mMesh = Scene.CreateBillboard(Globals.GetTex("Foobar"), 0, 0, 0, 10, 10)
  End Sub
End Module

In C# 2.0

Manage the instances ourselves (the usual way)

static class TV3DSingletons
{
  static TVScene scene;
  static TVGlobals globals;
  // Etc.
 
  static TV3DSingletons()
  {
    scene = new TVScene();
    globals = new TVGlobals();
    // Etc.
  }
 
  static TVScene Scene
  {
    get { return scene; }
  }
  static TVGlobals Globals
  {
    get { return globals; }
  }
  // Etc.
}
 
static class World
{
  static void Render() 
  {
    TV3DSingletons.Scene.RenderAll(true);
  }
}
 
static class Gui
{
  static TVMesh mesh;
 
  static void Initialize()
  {
    mesh = TV3DSingletons.Scene.CreateBillboard(TV3DSingletons.Globals.GetTex("Foobar"), 0, 0, 0, 10, 10);
  }
}

Let TV3D do it for us (the better way)

static class World
{
  static TVScene scene;
 
  static World()
  {
    scene = new TVScene();
  }
 
  static void Render() 
  {
    scene.RenderAll(true);
  }
}
 
static class Gui
{
  static TVScene scene;
  static TVGlobals globals;
 
  static TVMesh mesh;
 
  static World()
  {
    scene = new TVScene();
    globals = new TVGlobals();
  }
 
  static void Initialize()
  {
    mesh = scene.CreateBillboard(globals.GetTex("Foobar"), 0, 0, 0, 10, 10);
  }
}

Why let TV3D handle it?

Some might wonder what’s so good about this pattern. Here’s a list of arguments in its favour :

  • Only instanciate and make available the objects which you use, in their relevant context

Why let every codefile see TVGameControllers if they don’t use it?

  • Less Code + Same Functionality = Better

’nuff said.

  • Makes completely independant code files

Since you don’t need to know the exterior world to use TV3D singletons, that allows completely independant code, modules, plug-ins or DLLs! If you plan on releasing the source or making a big project, it’s clearly an advantage for OOP consistency.

 
tutorialsarticlesandexamples/using_tv3d_objects_as_singletons.txt · Last modified: 2013/11/22 13:32