Designing Classes - The Singleton Design Pattern

Need to be sure that you have just one instance of an object? Use this.

UML class diagram for Singleton software design pattern.
Trashtoy/Public Domain

Technical articles, including those at About Visual Basic, usually discuss the detailed syntax of VB.NET, not the overall philosophy of why you might want to do things in a certain way. Design Patterns, on the other hand, are all about the why and how of programming, not the syntax. This article, one of a series about design patterns in VB.NET, discusses what might be the most popular design pattern, the Singleton pattern.

The general topic of design is one that usually turns to "design patterns". If you think syntax is hard, you have another page to turn. There are dozens of design patterns and a lot of books get written about them. In fact, you could do a lot worse than the (somewhat dated now) book Visual Basic Design Patterns.

Just to be clear, "Design Patterns" are actual code prototypes. A book like Fred Brooks' classic The Design of Design is about the overall management of the process.

"Design patterns" are not standards. Even the names vary among design gurus and variations also exist in how they're implemented. But there is quite a bit of broad agreement about the overall structure of design patterns and learning about them beats reinventing the wheel every time.

The Singleton Pattern

The main idea behind the Singleton design pattern is to make sure that your code always works with the same instance of an object.

If you need more on classes and instantiation, try this introduction: Modules, Structures, and Classes.

In uncharacteristically clear language, Microsoft defines the problem solved by the Singleton design pattern like this:

How do you make an instance of an object globally available and guarantee that only one instance of the class is created?

To get the point across, here's an example of non-singleton object creation. Actually, this is just garden-variety creation of an object. The class is just a shell:


Public Class NormalClass
    ' This class does nothing
End Class

The code that instantiates two instances confirms that they are separate instances:


Dim NormalObject1 As New NormalClass
Dim NormalObject2 As New NormalClass
Console.WriteLine(
    "NormalObject1==NormalObject2: " +
    (NormalObject1 Is NormalObject2).ToString())

This results in ...


NormalObject1==NormalObject2: False

... in the Output window.

What if you need to make sure that, no matter what, your code always returns the same object? For example, it might be a device interface where there is only one device. Or it might be a new account object. You wouldn't want the same account number returned as a property in two different objects!

The key technique used by the Singleton design pattern is to control the instantiation (the New subroutine in the class). Instantiation is covered in this About Visual Basic article: Coding New Instances of Objects.

Once a single instance of the class is instantiated, another reference to the class should return the same instance.

The typical code that does this trick looks like this:


Public Class SingletonClass
    Private Shared singleInstance As SingletonClass
    Private Sub New()
        ' In this case, the Sub does nothing
    End Sub
    Public Shared ReadOnly Property Instance() _
        As SingletonClass
        Get
            If singleInstance Is Nothing Then
                singleInstance = New SingletonClass
            End If
            Return singleInstance
        End Get
    End Property
End Class

Things to notice:

-> The singleInstance variable is a shared instance of the class itself. ("Shared" is covered in this article: Shared Variables in VB.NET.)

-> New doesn't have to do anything. It just has to be here. This prevents a New subroutine from being created by the compiler.

-> The Instance property is of the type of the class itself.

The first time it's referenced, one copy of the class is created and returned. After that, the same copy is returned.

Running the same code to test the pattern yields a different result:


Dim SingletonObject1 As SingletonClass =
    SingletonClass.Instance
Dim SingletonObject2 As SingletonClass =
    SingletonClass.Instance
Console.WriteLine(
    "SingletonObject1==SingletonObject2: " +
    (SingletonObject1 Is SingletonObject2).ToString())

The Output window now displays:


SingletonObject1==SingletonObject2: True

On the next page, we show a real example that you can use to balance the load among a set of servers.

That was fun! But if I ended the article here, it wouldn't be of much use to many of you. You want to know how to actually use the Singleton design pattern.

Here's a more realistic example. Suppose you work in a shop where there are five print servers attached to a network. You want to be able to send print to a random server and balance the load among all five so each one gets a more or less equal share of the load.

(Yes, I know there is a lot of software that does exactly this. Building it into your own code may be a better option sometimes.)

What you need to do is create one class that contains a list of all of the servers and will randomly assign a print job to one of the same list no matter how often it's called. A Singleton class!

Here's a class that does it. I've bolded the class name, theServers, to emphasize that the class instantiates itself internally. I've also used an internal class that is used to instantiate the individual servers and contains a name field and an address field.


Class theServers
    Private Shared ReadOnly sInstance As New theServers()
    ' Type-safe generic list of servers
    Private sList As List(Of Server)
    Private rNum As New Random()
    ' Note: constructor is 'private'
    Private Sub New()
        ' Load list of available servers
        sList = New List(Of Server)() From {
            New Server() With {
                .sName = "Server 1",
                .sAddress = "Address 1"
            },
            New Server() With {
                .sName = "Server 2",
                .sAddress = "Address 2"
            },
            New Server() With {
                .sName = "Server 3",
                .sAddress = "Address 3"
            },
            New Server() With {
                .sName = "Server 4",
                .sAddress = "Address 4"
            },
            New Server() With {
                .sName = "Server 5",
                .sAddress = "Address 5"
            }
        }
    End Sub
    Public Shared Function GetaServer() As theServers
        Return sInstance
    End Function
    Public ReadOnly Property NextServer() As Server
        Get
            Dim r As Integer = rNum.Next(sList.Count)
            Return sList(r)
        End Get
    End Property
End Class
Class Server
    Public Property sName() As String
    Public Property sAddress() As String
End Class

You might not immediately recognize that this code does the same thing as the definition example above, but it does! In this case, the generic list of servers is initialized in the private New constructor. To prove it, I have coded a Windows Forms application with four "users" (four buttons) that calls the class through the common access point, GetaServer.

Each button uses code like this:


Private Sub GetaServer1_Click(
    sender As System.Object, e As System.EventArgs
    ) Handles GetaServer1.Click
    Dim s As theServers = theServers.GetaServer()
    If s Is sInit Then Console.WriteLine("Same instance")
    lblServerAssigned1.Text = s.NextServer.sName
End Sub

The first time the Singleton is called, I assign it to a global variable like this:


Friend sInit As theServers = theServers.GetaServer()

I only do this so I can check to make sure that each server object "Is" the same as the one assigned in the Dim statement. Otherwise, it wouldn't be necessary.

The end result looks like this:

--------
Click Here to display the illustration
--------