Problem:
VbClassic Classes don't support static methods, or a mechanism equivalent to overriding new(), so singletons can't be implemented in the way you would in C++.
- Quoting from the MicroSoft VisualBasicDesignPatterns? book...
- "Implementing a Singleton design pattern in VisualBasic is not possible with the current language features." (And then it goes on to show how to do it anyway. ;-)
MicrosoftDotNet appear to have Singleton built in to the framework, and an article on MSDN is located at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/singletondespatt.asp
Context:
You are in a context where you would normally use a singleton but the implementation is in VisualBasic
Forces:
- Resorting to global variables has all the problems of correctness and maintainability that singletons were designed to prevent
- Rewriting the code in something other than VisualBasic is elegant and conducive to sanity yet all too often infeasible due to outside constraints
- Implementation details that depend on the programmer rather than language features need to be made transparent yet avoid overload.
Solutions:
- Make a "private" implementation class, and use a FactoryMethod on a general "singleton factory" class to create it.
- AbstractFactory class controls creation of implementation class, in ActiveX DLL for enforcement. (from the MicroSoft VisualBasicDesignPatterns? book)
- HandleBody? -- multiple "handle" instances all point to a single "body" instance; the singleton.
- Procedural ClassFactory function (not an object), with ActiveX protection of singleton class
Related Patterns:
Singleton, FactoryMethod, IntentionSuggestingName?, QualifiedClassName?
Contributors: Stefan Kapusniak, JeffGrigg
But singletons can be implemented in VbClassic. It's just a different technique but the principle it's the same. All it really takes is a GlobalMultiuse? class in the same ActiveX DLL as your Singleton class and a static module. I've described it below. :)
I have found that you can fake a lot of things using the different instancing properties of classes and standard modules in in VbClassic
-- AlfredoChavez
Problem:
Ensure a class only has one instance, and provide a global point of access to it.
Context:
You are in a context where you would normally use a singleton but the implementation is in VisualBasic
Implementation:
I don't know if someone came up with this one before me, but I recently had a programming problem in a project that quite fits the context and forces described for the singleton pattern in many online catalogs. So here's my take on it:
- If your target class isn't already in an ActiveX DLL project, create one and move your class into it.
- Make your singleton-to-be class PublicNotCreatable
- Add a new standard module to your ActiveX DLL and declare a new variable. i.e. if I'm dealing with a CSesion class, then I'd use:
Public g_oSesion As CSesion
- Create a new class (which I personally call "CAccesor", unless I have previously created a "CCreator" one, in which case I'll "recycle" the previous one) and make it GlobalMultiuse?
- Add a new public Property Get procedure, which will grant public access to your singleton class. Using the example above, I'd use something like:
Public Property Get Sesion() As CSesion
If g_oSesion is Nothing Then
Set g_oSesion = New CSesion
' add any initialization code here,
' or better yet, IntroduceCreationObject here
End If
Set Sesion = g_oSesion
End Property
- Finally, make sure you destroy your Singleton properly. A new Class_Terminate event handler will do nicely here:
Private Sub Class_Terminate()
Set g_oSesion = Nothing
End Sub
--
AlfredoChavez
Private implementation class, with a FactoryMethod:
- Create the classes which should be singletons as standard classes named in the form privateSomeClass
- Create a module called Singletons and create a GlobalAccessor? for each singleton thusly:
public property get publicSomeClass() as privateSomeClass
static oInstance as privateSomeClass
if oInstance is nothing then
set oInstance = new privateSomeClass
end if
set publicClassName = oInstance
end property
- Heavily document that classes named in the form privateSomeClass, should only be accessed through their corresponding publicSomeClass interface.
- Call methods and set properties of publicSomeClass in your code in the usual way:
publicSomeClass.someMethod
publicSomeClass.someProperty = "someString"
Set someReference = publicSomeClass.someObjectProperty
Additional thought: Make this an ActiveX DLL, and you can set the Instancing property to PublicNotCreatable, preventing DLL users from violating your creation rules. Consider setting the ThreadingModel to SingleThread? too.
Contributors: StefanKapusniak?, JeffGrigg
AbstractFactory class controls creation of implementation class, in ActiveX DLL for enforcement:
Like the above, except that it...
- Uses a different TargetFactory? (AbstractFactory) class for each singleton class -- with a CreateInstance? function.
- Uses ActiveX Instancing property of PublicNotCreatable to prevent cleints from creating singleton class instances themselves.
- Sets the ThreadingModel to SingleThread? to avoid one-instance-per-apartment.
HandleBody? approach:
The problem with this approach is it still relies on programmer's discipline not to instantiate the actual "private" class. I turn it around, and basically use the handle/body approach.
- Implement the actual class as module (MySingletonBody?), with all data members private to the module. Provide access functions for all "public" properties.
- Create a class MySingleton?, and delegate all methods and properties to those in the body module.
- If garbage collection is required, perform reference counting in the Initialize and terminate events of the class. The body can release its resources on the count going to zero, and re-acquire them when the count becomes non-zero.
The result has many advantages
- Programmers can not create more than one instance as all the data is effectively static to the body module.
- Garbage collection is still possible
- You can get the "instance" reference by simply creating a instance of the handle class.
- It is relatively simple to move from a singleton to an ordinary class at a later date.
- The Implements feature is now available to allow you to have polymorphic singletons.
- If you define this in an OLE server, you can export the handle class and get a PC-wide singleton (useful for sharing COM ports between apps).
The only thing I can not find an easy way to do is to allow the classes to raise events. The only solution I have is for the body to maintain a collection of all created handles and call a"private" function in each instance telling them to raise their event.
--KeithDerrick
Procedural ClassFactory function (not an object), with ActiveX protection of singleton class:
Here's how you do it. Slightly long winded, perhaps, but it works.
Create a new ActiveX DLL. Create in this your Singleton class with all its methods etc. Make sure you mark it 'Public not creatable'.
Add a .BAS module with one function:
Public Function getSingleton() as NameOfSingletonClass?
Static Singleton as NameOfSingletonClass?
If Singleton is Nothing then
set Singleton= New NameOfSingletonClass?
' add any initialisation calls you want
endif
Set getSingleton = Singleton
Exit Function
End Function
Create a new class; ensure it is marked MultiUse?. Give it one method:
public property Get SingletonInstance?() as NameOfSingletonClass?
Set SingletonInstance? = getSingleton
end property
Add a reference to this AxDLL in your VB project and there's your singleton. What's so hard about that?
Took me < 5 mins to type into Wiki; add in compiles etc and there is about 10 mins overhead.
-- SimonSmith
CategoryVbClassic