"Синглтон" заводы, хорошо или плохо?
У меня много (абстрактных) фабрик, и они обычно реализуются как одиночные.
Обычно для удобства не пропускать их через слои, которые действительно не имеют бизнеса с использованием или знанием этих заводов.
В большинстве случаев мне нужно только принять решение при запуске, в котором реализация factory остальная часть программы кода, возможно, через некоторую конфигурацию
он выглядит, например. как
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();
....
}
Состязаясь, обоняйтесь об этом, я просто не уверен, что - возможно, это более разобщенный глобальный, чем синглтон. Не похоже, что действительно действительно должен быть только один factory когда-либо создающий ColumnCalculations - хотя моим программам больше не нужно.
Считается ли это лучше всего? Должен ли я использовать их в каком-нибудь (полу) глобальном классе AppContext? Что-то еще (я не совсем готов переключиться на какой-то более крупный контейнер IoC, или spring.net еще совсем btw)?
Ответы
Ответ 1
Это действительно зависит от того, что вы делаете, и от объема вашего приложения. Если это просто небольшое приложение, и он никогда не будет расти за его пределами, тогда ваш нынешний подход может быть в порядке. Для этих вещей нет универсальной "лучшей" практики. Хотя я бы не рекомендовал использовать синглтоны для чего-либо другого, кроме методов без имени и/или односторонних вызовов (например, для ведения журнала), отбросив его из-под контроля "только потому, что" это односторонний подход не всегда подходит "./p >
Для чего-то другого, кроме тривиального или прототипного кода, мне лично нравится явно использовать инверсию управления с инсталляцией конструктора, так как это означает, что все зависимости учитываются, и вы не получаете никаких "сюрпризов". Компилятор не позволит вам создать экземпляр A без B и B без C. Синглтоны немедленно похоронят эти отношения - вы можете создать экземпляр A без B и B без C. Когда происходит вызов от A до B, вы получите нулевую ссылку исключение.
Это особенно раздражает при тестировании, поскольку вам приходится итеративно работать в обратном направлении через ошибки выполнения. Когда вы проверяете код, вы используете API так же, как это делает кодер-коллега, поэтому он указывает на проблемы с дизайном при таком подходе. Инъекция конструктора гарантирует, что этого никогда не произойдет - все зависимости указаны заранее. Недостатком с инсталляцией конструктора является то, что конфигурация графического объекта сложнее. Это смягчается с помощью контейнера IoC.
Я предполагаю, что я пытаюсь сказать, что если вы доберетесь до того, что собираетесь использовать какой-то объект контекста и шаблон реестра, вы также можете взглянуть на контейнеры IoC. Попытка скопировать вашу собственную версию mutt, вероятно, пустая трата времени, когда вы можете использовать установленный бесплатный продукт, такой как Autofac.
Ответ 2
Наличие нескольких синглтонов довольно типично и обычно не проблематично - многие одиночные пары приводят к некоторому раздражающему коду.
Мы только что пережили ситуацию, когда нам пришлось тестировать наши тяжелые однозарядные классы. Проблема в том, что когда вы тестируете класс b, и он получает класс c (singleton), вы не можете издеваться над классом c (по крайней мере EasyMock не позволит нам заменить статический метод factory одноэлементного класс.
Одно простое решение - иметь "сеттеры" для всех ваших синглтонов для целей тестирования. Не рекомендуется.
Еще одна вещь, которую мы пробовали, заключалась в том, чтобы иметь единственный класс, в котором содержались все синглеты - реестр. Выполнение этого довольно близко к инъекции зависимостей, что вы почти наверняка должны использовать.
Помимо тестирования, я давно узнал, что когда никогда не будет больше одного экземпляра данного объекта; в следующем rev они часто хотят два, что делает синглтоны МНОГО лучше, чем статические классы - по крайней мере, вы можете добавить параметр в getter одноэлементного и вернуть второй без слишком большого количества рефакторинга (что опять-таки делает то, что делает DI).
Во всяком случае, посмотрите на DI, вы можете быть действительно счастливы.
Ответ 3
Нет, потому что вы здесь создаете глобальное состояние. Есть всевозможные проблемы с глобальным состоянием - главным из них, что одна функция зависит от довольно невидимого способа поведения других функций. Если функция вызывает другую функцию, которая забывает хранить и восстанавливать factory
до ее завершения, возникает проблема, потому что вы даже не можете вернуть старое значение, если вы его где-то не сохранили. И вы должны добавить код, чтобы сделать это (судя по вашему коду, я бы предположил, что вы находитесь на языке с finally
, что оставляет еще больше места для ошибок). Что еще, если вы закончите с кодом, который нужно быстро переключаться между двумя фабриками для двух подобъектов, вам нужно написать вызов метода в каждой точке - вы не можете сохранить состояние в каждом подобъекте (ну, вы можете, но затем вы побеждаете цель глобального состояния [что, по общему признанию, не так много)).
Вероятно, наиболее удобно хранить тип factory как член и передавать его конструктору новых объектов, которые в нем нуждаются (или создавать новый по мере необходимости и т.д.). Это также дает вам лучший контроль - вы можете гарантировать, что все объекты, построенные объектом A, прошли через тот же factory, или вы можете предложить методы для свопинга фабрик.
Ответ 4
Я бы посоветовал это, потому что очень сложно писать достойные модульные тесты для кода, который вызывает эти заводы.
Misko Hevery имеет хорошую статью об этом в своем блоге.
Ответ 5
Является ли это лучшей практикой?
Я не вижу проблемы: вы сказали, что "моим программам больше не нужно", поэтому реализация каких-либо более гибких/абстрактных может быть примером You Это не понадобится.
Ответ 6
Плохая форма singleton - это тот, который реализует эквивалент вашего метода Factory с помощью:
return new CalculationFactory ();
Это гораздо труднее заглушить, издеваться или обернуть.
Версия, которая возвращает объект, созданный в другом месте, намного лучше, хотя, как и большинство других, может быть использована неправильно или чрезмерно.