Игры в singleton.
Классический (GOF) синглтон на LS не реализуем во-первых, из-за невозможности создать приватный конструктор класса и, во-вторых, из-за "локального" контекста выполнения(отсутствия переменных, глобальных на уровне приложения). Проблемы глобальной видимость здесь трогать не буду, а вот за проблему приватного конструктора есть некоторые мысли...
В свое время было найден workaround:
Ссылка скрыта от гостей
.
Автор предлагает использовать в качестве синглтона приватный экземпляр приватного же класса, возвращая ссылку на него как на variant:
Код:
Private universe As TUniverse
Private Class TUniverse
Sub New()
Call BigBang()
End Sub
Private Function BigBang()
' Применяем(?) Большой Взрыв, для создания новой вселенной
End Function
End Class
Public Function getUniverse As Variant
If universe Is Nothing Then
Set universe = New TUniverse()
End If
Set getUniverse = universe
End Function
Идея, в общем, интересная, НО:
Предложенная реализация паттерна отвратительна тем, что приходится обращаться с объектом как с variant-ом - т.е. все очепятки клиента вылезут только на этапе выполнения. Кроме того невозможно создать функцию, принимающую объект указанного типа как параметр. Данный минус решения (IMHO) перевешивают все возможные плюсы. Забиваем.
Предлагаю на суд общественности альтернативное решение.
Раз нельзя сделать закрытый конструктор, сделаем так, чтоб его невозможно было вызвать. Небольшая доработка напильником:
Код:
Private universe As TUniverse
Private Class TFakeClass
Sub New()
End Sub
End Class
Public Class TUniverse
Sub New( i_fake As TFakeClass)
Call BigBang()
End Sub
Private Function BigBang()
' Применяем Большой Взрыв, для создания новой вселенной
End Function
End Class
Public Function getUniverse As TUniverse
If universe Is Nothing Then
Set universe = New TUniverse( Nothing )
End If
Set getUniverse = universe
End Function
И все прелести отлова ошибок на этапе компиляции снова доступны.
Т.к. конструктор требует параметром приватный класс - в клиентском коде создать новый экземпляр объекта не получится...
Единственная конструкция, которую пропустит компилятор - Dim clientSideUniverse As New TUniverse( Nothing ). Однако создать объект все равно не выйдет( ошибка времени выполнения ). При попытке унаследоваться от Вселенной с переопределением конструктора
Код:
Sub New() , TUniverse( Nothing )
End Sub
- также ляжет на этапе выполнения. Т.е. реально имеем паблик класс, экземпляр которого невозможно инстанцировать в клиентском коде. Эврика?
З.Ы. Идея на самом деле родилась для совершенно другой задачи. Фактически требовалось передавать в функцию значение из фиксированного набора( enumeration ). Городить case и проверять входной параметр внутри функции - не выход, т.к. компилятор все равно позволяет ошибиться в клиентском коде.
При помощи данной техники мы можем объявить нужное количество экземпляров класса и пользоваться ими как элементами перечисления: INVOICE_STATUS_BLOCKED , INVOICE_STATUS_APPROVED и т.д.( будучи уверенным, что вызвать функцию с неверным параметром клиенту не удастся )