Brandon
Brandon

Reputation: 945

Using the Singleton Pattern

I can think of three different places to retrieve the instance of my singleton, I am wondering if any one way is better than the others and for what reason.

Friend Class ConcreteProductDeafultBarChart
    Implements IAbstractBarChart

    Private Way1 As DatabaseSingleton = DatabaseSingleton.Instance
    Private Way2 As DatabaseSingleton

    Public Sub New()
        Way2 = DatabaseSingleton.Instance
    End Sub

    'This function gets called else where often
    Public Function GetBarChartData() As System.Data.DataTable Implements IAbstractBarChart.GetBarChartData
        Dim Way3 As DatabaseSingleton = DatabaseSingleton.Instance

        'Do something with my singleton instance
    End Function
End Class

If you can give your reasons behind your claim too, it would be appreciated.

Upvotes: 0

Views: 1088

Answers (2)

Will Marcouiller
Will Marcouiller

Reputation: 24132

When to use the Singleton Pattern?

One shall use the Singleton Pattern when only an instance of an object is required throughout the application. For instance, when an object costs much to instantiate, you want to pass the same instance along.

How to share the same instance across the whole system?

Dependency Injection answers it all. In your particular scenario, your ConcreteProductDefaultBarChart class depends on an instance of the Database class. So why not make the dependency obvious by injecting into its constructor like so?

Friend Class ConcreteProductDefaultBarChart 
    Implements IAbstractBarChart

    Public Sub New(Database database)
        If database Is Nothing Then Throw New ArgumentNullException("database")
        Me.database = database
    End Sub

    Private ReadOnly database As Database
End Class

This way, as already stated by Mark Seemann:

One of the wonderful benefits of Constructor Injection is that it makes violations of the Single Responsibility Principle glaringly obvious.

And you may change your instance lifecycle whenever you want so that you get the instance you really intend to. For instance, in a testing context, you might as well want to inject a fake database or a mocked database setup to give predictable results so that you may test your code against given result criterion, and see if it behaves as wanted.

How to define a Singleton?

A Singleton, as you already know, always return the same instance once your object is instantiated. An example of a manual Singleton definition might be:

Public NotInheritable Class Database 
    Private Sub New()
    End Sub

    Public ReadOnly Property Instance() As Database
        Get
            If db Is Nothing Then db= New Database()
            Return db
        End Get
    End Property

    Private db As Database
End class

So now not only your class is responsible to provide you with an instance, but it is also responsible for its lifecycle, which is more the application responsibility. A class, as far as I'm concerned, shall never be responsible for its lifecycle. So let's delegate this responsiblity to a Factory which is responsible to create instances of objects.

Public Class DatabaseFactory
    Public Sub New()
    End Sub

    Public Function Create(ByVal connectionString As String) As Database
        If String.IsNullOrWhiteSpace(connectionString) Then 
            Throw New ArgumentNullException("connectionString")
        End If

        If database Is Nothing Then database = New Database(connectionString)

        Return database
    End function

    Private Shared database As Database
End Class

This way, you may inject your DatabaseFactory class into your ConcreteProductDefaultBarChart constructor, and handle your Database instance lifecycle through your factory which responsility belongs to make sure it provides you with the instance you intended to use each time you need it.

In the end, yes it increases your code complexity. In exchange, you get an easier to maintain code base with easy-to-troubleshoot and very easy-to-test application using unit tests, integration tests, functional tests and acceptance tests. And each time a change is required, you only need a few minutes to bring the changes and see whether they are breaking changes or not by running the tests against your code, then there you go for a Red-Green-Refactor session, et voilà!

You may as well use some DI tools out there for your instances lifecycle, then your container is responsible to handle your instances for you, so no need to worry how to implement the Singleton pattern anymore, just tell your tool that you want this class handled as a Singleton, and you're done!

Upvotes: 1

M. Lanza
M. Lanza

Reputation: 6790

Don't get attached to implementing the singleton via the new constructor. Consider the EventArgs.Empty that Microsoft provides as a standard way to pass insignificant event arguments. Doing it this way makes the use of the singleton explicit. Forcing the use of the new constructor just conceals what is actually happening.

When you use new something new should actually be created since that's what the method name implies.

Class MyClass
  Public Shared ReadOnly MySingleton = New MyClass()
End Class

Upvotes: 1

Related Questions