Фабрики синглтонов, хорошо или плохо?

У меня много (абстрактных) фабрик, и они обычно реализуются как одиночные.

Обычно для удобства, чтобы не проходить их через слои, которые действительно не имеют дела с использованием или знанием этих фабрик.

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

это выглядит например как

abstract class ColumnCalculationFactory { 
  private static ColumnCalculationFactory factory;

 public static void SetFactory(ColumnCalculationFactory f) {
         factory = f;
  }

  public static void Factory() {
         return factory;
  }

 public IPercentCalculation CreatePercentCalculation();
 public IAverageCalculation CreateAverageCalculation();
    ....

}

Что-то действительно пахнет этим, я просто не уверен, что - это, может быть, больше неуловимый глобальный, чем синглтон. Не похоже, что на самом деле должна быть только одна фабрика, когда-либо создававшая ColumnCalculations - хотя моим программам и не нужно больше.

Считается ли это лучшей практикой? Должен ли я лучше поместить их в какой-нибудь (полу) глобальный класс AppContext? Что-то еще (я еще не совсем готов переключиться на какой-то более крупный контейнер IoC или spring.net, кстати)?


person leeeroy    schedule 16.08.2009    source источник


Ответы (6)


Это действительно зависит от того, что вы делаете, и от объема вашего приложения. Если это довольно небольшое приложение, и оно никогда не вырастет дальше этого, то ваш текущий подход вполне может подойти. Для этих вещей не существует универсальной "лучшей" практики. Хотя я бы не рекомендовал использовать синглтоны для чего-либо, кроме листовых методов без сохранения состояния и / или односторонних вызовов (например, журналирования), отклонять его из-под контроля «только потому, что» это синглтон, не обязательно правильно.

Для чего-либо, кроме тривиального или прототипного кода, мне лично нравится явно использовать инверсию управления с внедрением конструктора, поскольку это означает, что все зависимости учтены, и вы не получите никаких «сюрпризов». Компилятор не позволит вам создать экземпляр A без B и B без C. Синглтоны немедленно похоронят эти отношения - вы можете создать экземпляр A без B и B без C. Когда произойдет вызов от A к B, вы получите нулевую ссылку исключение.

Это особенно раздражает при тестировании, так как вам приходится итеративно работать в обратном направлении через сбои во время выполнения. Когда вы тестируете код, вы используете API так же, как это сделал бы другой программист, поэтому это указывает на проблемы проектирования с этим подходом. Внедрение конструктора гарантирует, что этого никогда не произойдет - все зависимости указаны заранее. Обратной стороной внедрения конструктора является более сложная конфигурация графа объектов. Это смягчается за счет использования контейнера IoC.

Я предполагаю, что я пытаюсь сказать, что если вы дошли до того момента, когда вы рассматриваете возможность использования какого-либо объекта контекста и шаблона реестра, вы также можете взглянуть на контейнеры IoC. Усилия по развертыванию собственной версии mutt, вероятно, будут пустой тратой времени, если вы можете использовать уже установленный бесплатный продукт, такой как Autofac.

person Mark Simpson    schedule 16.08.2009
comment
Могу ли я использовать шаблон для инверсии управления, который не включает Spring и XML? Я не знаю, как использовать IC для предварительной загрузки конструкторов без Spring и, следовательно, веб-проекта. А как насчет не веб-проектов? - person chrips; 27.08.2018
comment
@Chrips IoC на самом деле просто означает, что мы не создаем прямых зависимостей между классами. Скорее, мы даем классам интерфейс желаемого объекта. Это позволяет нам легко имитировать вещи для тестирования или адаптировать код для любых дополнительных вариантов использования, которые нам нужны. - person Matthew S; 07.11.2019

Наличие нескольких синглтонов довольно типично и обычно не вызывает проблем - большое количество синглтонов приводит к некоторому раздражающему коду.

Мы только что прошли через ситуацию, когда нам нужно было протестировать наши сильно загруженные синглтон-классы. Проблема в том, что когда вы тестируете класс b и он получает класс c (одноэлементный), у вас нет возможности имитировать класс c (по крайней мере, EasyMock не позволит нам заменить статический фабричный метод одноэлементного класса.

Одно простое решение - установить "Сеттеры" для всех ваших синглтонов в целях тестирования. Не рекомендуется.

Еще мы попробовали создать один класс, в котором хранятся все синглтоны, - реестр. Это очень похоже на внедрение зависимостей, которое вы почти наверняка должны использовать.

Помимо тестирования, я давно узнал, что когда никогда не будет больше одного экземпляра данного объекта; в следующем ревизии они часто хотят два, что делает синглтоны НАМНОГО лучше, чем статические классы - по крайней мере, вы можете добавить параметр к получателю синглтона и вернуть второй без особого рефакторинга (который снова просто делает то, что делает DI ).

В любом случае, посмотрите на DI, вы можете быть действительно счастливы.

person Bill K    schedule 16.08.2009
comment
Согласованный. Еще одно последствие синглтонов - утечка заданных значений во время модульных тестов. Нам приходилось создавать разветвление JVM для каждого теста, так как одиночный объект при тестировании или просто используемый тестом находился в состоянии A, а ниже по течению он должен был быть в состоянии B. - person Trenton; 23.08.2009
comment
Проблема с обширным TDD заключается в том, что вы часто усложняете код и делаете его более подверженным ошибкам ради использования инструментов тестирования. Я твердо верю, что если вы должны изменить архитектуру ради Mocking, вы чрезмерно используете Mocking и вместо этого должны использовать предполагаемую конфигурацию Factory / Singleton. 100% покрытие кода безумно и вызовет больше проблем и сложностей, чем решит. - person Steve Cook; 22.07.2016
comment
Я согласен с TDD, но это не единственная причина использовать DI. Кроме того, тестируемый код обычно лучше, чем непроверяемый, независимо от фактического существования тестов. Наличие DI вместо синглтона никогда не бывает плохим и может быть действительно хорошим в случае, когда вы действительно можете захотеть его протестировать, даже если у вас нет 100% тестового покрытия. - person Bill K; 22.07.2016

Нет, потому что то, что вы здесь делаете, создает глобальное состояние. С глобальным состоянием связаны всевозможные проблемы, главная из которых заключается в том, что одна функция затем незаметно зависит от поведения других функций. Если функция вызывает другую функцию, которая забывает сохранить и восстановить factory до своего завершения, то у вас есть проблема, потому что вы даже не можете вернуть старое значение, если не сохранили его где-нибудь. И для этого вам нужно добавить код (судя по вашему коду, я предполагаю, что вы используете язык с finally, что оставляет еще больше места для ошибок). Более того, если вы получите код, который должен быстро переключаться между двумя фабриками для двух подобъектов, вам придется писать вызов метода в каждой точке - вы не можете сохранять состояние в каждом подобъекте (ну, вы можете, но тогда вы побеждаете цель глобального состояния [что, по общему признанию, не так уж и много]).

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

person coppro    schedule 16.08.2009
comment
хотя, учитывая открытый общедоступный SetFactory, нет никаких гарантий, что кто-то не сможет избавиться от большего беспорядка с фабрикой, я обычно, кажется, устанавливаю фабрику только при запуске, на основе первоначального решения или конфигурации. - person leeeroy; 16.08.2009
comment
тогда вы становитесь жертвой недостатка синглтонов ... они синглтоны. - person coppro; 16.08.2009

Считается ли это лучшей практикой?

Я не вижу проблем: вы сказали, что «моим программам больше не нужно», поэтому ваша реализация чего-то более гибкого / абстрактного может быть случаем Вам это не понадобится.

person ChrisW    schedule 16.08.2009

Я бы не советовал этого делать, потому что из-за этого очень сложно писать достойные модульные тесты для кода, который вызывает эти фабрики.

У Миско Хевери есть хорошая статья об этом на его блог.

person jqno    schedule 16.08.2009
comment
Конечно, но знаете ли вы, какие из них вам нужно высмеивать, в нетривиальном приложении, где вы не знаете наизусть зависимости классов? Я снова ссылаюсь на статью Миско. - person jqno; 16.08.2009
comment
У Misko также есть несколько отличных видео на YouTube по этой теме: youtube.com/watch?v=acjvKJiOvXw < / а> - person Despertar; 22.08.2013
comment
Спасибо! Статья Миско великолепна, и комментарии стоит прочитать! - person Adam Calvet Bohl; 13.01.2017

Плохая форма синглтона - это тот, который реализует эквивалент вашего метода Factory с помощью:

return new CalculationFactory ();

Гораздо сложнее заглушить, издеваться или завернуть.

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

person soru    schedule 16.08.2009