Практический вопрос о внедрении Singleton & Dependency Injection - PullRequest
15 голосов
/ 29 октября 2008

Скажем, у меня есть класс PermissionManager, который должен существовать только один раз для моей системы и в основном выполняет функцию управления различными разрешениями для различных действий в моем приложении. Теперь у меня есть некоторый класс в моем приложении, который должен иметь возможность проверить определенное разрешение в одном из его методов. Конструктор этого класса в настоящее время общедоступен, т.е. используется пользователями API.

Еще пару недель назад я просто попросил бы мой класс вызвать где-нибудь следующий псевдокод:

     PermissionManager.getInstance().isReadPermissionEnabled(this)

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

Так должен ли я на самом деле требовать, чтобы пользователи API передавали экземпляр PermissionManager в конструктор класса? Даже если я хочу, чтобы для моего приложения существовал только один экземпляр PermissionManager?

Или я все делаю неправильно, и у меня должен быть где-то не общедоступный конструктор и фабрика, которая передает мне экземпляр PermissionManager?


Дополнительная информация Обратите внимание, что когда я говорю "Внедрение зависимостей", я говорю о DI Pattern ... Я не использую какую-либо инфраструктуру DI, такую ​​как Guice или Spring. (... еще)

Ответы [ 5 ]

3 голосов
/ 29 октября 2008

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

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

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

Одно из компромиссных решений, которое я видел, - дать вашему классу свойство типа PermissionManager. Если свойство было установлено (скажем, в модульном тесте), вы используете этот экземпляр, в противном случае вы используете синглтон. Что-то вроде:

PermissionManager mManager = null;
public PermissionManager Permissions
{
  if (mManager == null)
  {
    return mManager;
  }
  return PermissionManager.getInstance();
}

Конечно, строго говоря, ваш PermissionManager должен реализовывать некоторый интерфейс IPermissionManager, и это , на который должен ссылаться ваш другой класс, чтобы фиктивная реализация могла быть легче заменена во время тестирования.

2 голосов
/ 29 октября 2008

Если вы подписываетесь на способ внедрения зависимостей, все классы, которым нужен ваш PermissionManager, должны внедрять его как экземпляр объекта. Механизм, который управляет его реализацией (для обеспечения одноэлементной природы), работает на более высоком уровне. Если вы используете инфраструктуру внедрения зависимостей, такую ​​как Guice, она может выполнять принудительную работу. Если вы выполняете разводку объектов вручную, внедрение зависимостей предпочитает группировать код, который выполняет инстанцирование (работа с новым оператором) вдали от вашей бизнес-логики.

В любом случае, классический синглтон "Capital-S" обычно рассматривается как анти-паттерн в контексте внедрения зависимостей.

Эти посты были полезны для меня в прошлом:

2 голосов
/ 29 октября 2008

Вы действительно можете начать с введения PermissionManager. Это сделает ваш класс более тестируемым.

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

Разделение ваших классов делает ваши классы более гибкими, но это также может затруднить их использование. Это зависит от ситуации, что вам нужно. Если у вас есть только один PermissionManager и у вас нет проблем с тестированием классов, которые его используют, то нет причин использовать DI. Если вы хотите, чтобы люди могли добавлять свои собственные реализации PermissionManager, то DI - это путь.

1 голос
/ 29 октября 2008

Так должен ли я на самом деле требовать, чтобы пользователи API передавали экземпляр PermissionManager в конструктор класса? Даже если я хочу, чтобы для моего приложения существовал только один экземпляр PermissionManager?

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

0 голосов
/ 29 октября 2008

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

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

...