Можно ли использовать синглтон с конструктором не по умолчанию в С#?

Я реализую структуру уведомлений для одного из моих проектов. Поскольку я хочу, чтобы он был очень общим, пользователь может использовать несколько транспортных уровней, так что ему действительно не нужно заботиться об использовании одного метода доставки (скажем, WCF) или другого (например, для ActiveMQ). Интерфейс, к которому пользователь имеет доступ, конечно, не зависит от метода доставки (WCF или ActiveMQ). Тем не менее, два класса (потребитель и производитель) реализуют синглтоны, поэтому они фактически используют конструкторы по умолчанию (то есть без параметров). Моя проблема в том, что я хотел бы иметь один параметр, метод доставки, который пользователь хочет использовать. Но насколько я знаю, singleton использует только конструкторы по умолчанию? что нормально, так как нет смысла использовать синглтоны с параметрами. Итак, каковы мои варианты здесь? не создавать синглтон? создать метод для установки метода доставки?

Большое спасибо за Вашу помощь,

Себастьян


person srodriguez    schedule 20.05.2009    source источник


Ответы (4)


Конечно, у вас могут быть параметры с синглтонами, за исключением того, что вместо передачи параметра в конструктор вы передаете его в метод getInstance(). Конечно, ваш переопределенный конструктор должен быть закрытым для настоящей реализации синглтона. Мой пример написан на Java, но применим и для C#.

Пример:

Singleton s = Singleton.getInstance(42);

В коде синглтона:

private Singleton() {

}

private Singleton(int num) {
   //set num
}

public getInstance(int num) {
  //singleton code (calls the private non-default constructor if an object 
  //does not already exist) 
}
person AlbertoPL    schedule 20.05.2009
comment
Что произойдет или что это означает, если позже во время жизни программы кто-то вызовет Singleton.getInstance(43);? - person ChrisW; 20.05.2009
comment
Это действительно будет зависеть от деталей реализации. Если объект можно создать только один раз с одним числом, а затем никогда не изменять, то метод вернет исходный созданный объект. Если бы 43 был вообще другим объектом, класс Singleton в моем примере мог бы хранить массив всех созданных Singleton и проверять массив. Хотя в любом случае это выглядит странно. - person AlbertoPL; 20.05.2009
comment
Это будет больше похоже на Factory, а не на Singleton. - person Ikke; 20.05.2009
comment
Конечно, и я должен отметить, что это не лучшее использование синглтона. Singleton следует использовать только для объектов без состояния, из которых вам понадобится только один. - person AlbertoPL; 20.05.2009
comment
Почему вы говорите, что синглтон должен быть без гражданства? Разве синглтон не является просто глобальной переменной (с сохранением состояния)? Синглтон можно использовать, например, для хранения списка всех текущих экземпляров чего-либо. - person ChrisW; 20.05.2009
comment
Помимо Синглтона, связанный с ним паттерн — это «приспособленец», состояние которого неизменно. - person ChrisW; 20.05.2009
comment
Существует много споров о правильном использовании Singleton. На самом деле, некоторые утверждают, что шаблон Singleton на самом деле является анти-шаблоном. Вы правы, Singleton может быть глобальным объектом/классом с состоянием, но это состояние никогда не должно меняться. Трудно оправдать шаблон Singleton для класса/объекта, состояние которого будет меняться. - person AlbertoPL; 20.05.2009

Существуют некоторые фреймворки внедрения зависимостей, такие как Spring.Net, которые могут вам помочь. Вы можете эффективно передать параметр в файле конфигурации для вашего конструктора синглетонов.

Ссылка на пример Spring Framework

Могу ли я предположить, что если у вас есть два разных поведения, требуемых от вашего синглтона, вы можете захотеть создать подкласс. Таким образом, вы получаете желаемое поведение, вызывая синглтон нужного поведения класса.

person Spence    schedule 20.05.2009

Вы можете легко сделать это с помощью фреймворка внедрения зависимостей. У меня есть аналогичная конструкция в моем текущем проекте с использованием MEF. Все, что требуется, — это использовать параметры внедрения конструктора и добавить эту сборку и сборку запрошенной зависимости в каталог, и она правильно подключит ее.

Другой вариант - иметь некоторую форму функции инициализации, которая принимает ваш вариант и создает экземпляр singleton. Вместо того, чтобы создавать его при первом доступе, вы можете создать его во время вызова инициализации. Недостатком здесь является то, что вам нужно обязательно инициализировать свой синглтон, прежде чем использовать его (обычно при запуске программы, используя файл конфигурации).

Похожий, но менее подверженный ошибкам вариант, состоит в том, чтобы просто выполнить ленивую инициализацию синглтона и задать для него параметр «по умолчанию». Разрешите вызывающему объекту установить статическое свойство, чтобы изменить построенный параметр, поэтому, если он установлен до построения синглтона, вы получите другое значение по умолчанию. Это может сбивать с толку, поскольку, опять же, вам нужно убедиться, что вы установили свойство перед доступом к синглтону, иначе вы получите неожиданное поведение.

person Reed Copsey    schedule 20.05.2009

Я знаю, что уже поздно отвечать на исходный вопрос, но у меня только что была эта проблема, и вот как я ее решил. Возможно, это не идеально, но, похоже, работает. Я создал метод Init, который необходимо вызвать перед попыткой использовать экземпляр singleton.

public void Init(/*parameters*/)
{
        if (_isInitialized)
        {
            throw new InvalidOperationException("Component is already initialized!");
        }
        //do your work here
}

Любой другой доступ к экземпляру singleton (свойства get, set, вызовы методов) вызовет исключение недопустимой операции, сообщающее, что объект не был инициализирован.

Я думаю, что это делает то, что мне нужно, менее запутанно, чем GetInstance(params), потому что нет риска неправильно понять, что делает метод. Недостатком является то, что он не будет вызывать ошибки времени компиляции, но первый запуск без выполненной инициализации вызовет исключение, поэтому этого должно быть достаточно.

person Mihai Drebot    schedule 02.02.2012