Практический вопрос о синглтоне и внедрении зависимостей

StackOverflow https://stackoverflow.com/questions/246963

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

     PermissionManager.getInstance().isReadPermissionEnabled(this)

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

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

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


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

Это было полезно?

Решение

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top