2009-08-16 3 views
29

У меня много (абстрактных) заводов, и они обычно реализуются как одиночные.«Синглтон» заводы, хорошо или плохо?

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

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

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

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(); 
    .... 

} 

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

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

ответ

11

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

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

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

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

3

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

Я не вижу проблем: вы сказали, что «моим программам больше не нужно», поэтому ваше внедрение чего-либо более гибкого/абстрактного может быть в случае You Ain't Gonna Need It.

6

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

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

+0

Несмотря на то, что предоставленный публичный SetFactory не гарантирует, что кто-то не может поменять место на фабрике, я обычно предпочитаю фабрику только при запуске, основываясь на первоначальном желании или конфигурации. – leeeroy

+0

, тогда вы становитесь жертвой недостатка одиночек ... они одиночные. – coppro

4

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

Misko Hevery имеет nice article об этом в своем блоге.

+0

С вышеуказанным кодом вы можете легко издеваться над фабрикой. – nos

+0

Уверен, но знаете ли вы, какие из них вам нужно высмеять, в нетривиальном приложении, где вы не знаете наизусть зависимостей классов? Опять же, я имею в виду статью Мишко. – jqno

+0

Misko также имеет несколько отличных видеороликов на youtube на эту тему, http://www.youtube.com/watch?v=acjvKJiOvXw – Despertar

11

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

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

Одно из простых исправлений состоит в том, чтобы иметь «сеттеры» для всех ваших синглтонов для целей тестирования. Не рекомендуется.

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

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

Во всяком случае, изучите DI, вы можете быть действительно счастливы.

+0

Согласовано. Другим следствием синглтонов является «утечка» заданных значений во время модульных испытаний. Мы должны были привести к размножению JVM для каждого теста, поскольку однотонный тест или просто используемый тест был в состоянии A, а ниже по течению он должен находиться в состоянии B. – Trenton

+0

Проблема с обширным TDD заключается в том, что вы часто усложняют код и делают его более подверженным ошибкам ради использования тестовых инструментов. Я твердо верю, что если вы должны изменить архитектуру ради Mocking, вы чрезмерно используете Mocking и вместо этого должны использовать предполагаемую конфигурацию Factory/Singleton. 100% покрытие кода является безумным и вызовет больше проблем и сложности, чем оно решит. – user3225313

+0

Я согласен с TDD, но это не единственная причина использовать DI. Кроме того, код TESTABLE, как правило, будет лучше, чем непроверяемый код, независимо от фактического существования тестов. Наличие DI вместо синглтона не всегда плох и может быть действительно хорошим в случае, когда вы действительно можете его протестировать, даже если у вас нет 100% -ного охвата тестирования. –

1

Плохая форма одноточечного является тот, который реализует эквивалент вашего метода завода с:

return new CalculationFactory(); 

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

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

Смежные вопросы