Це насправді просто зробити, коли зрозумієш, що DI - це закономірності та принципи , а не технології.
Щоб розробити API в агностично-контейнерному режимі DI, дотримуйтесь цих загальних принципів:
Програма на інтерфейс, а не на реалізацію
Цей принцип насправді є цитатою (хоча з пам’яті) від « Шаблони дизайну» , але це завжди має бути вашою реальною метою . DI - це лише засіб для досягнення цієї мети .
Застосовуйте Голлівудський принцип
Голлівудський принцип в термінах DI говорить: Не дзвоніть контейнеру DI, він зателефонує вам .
Ніколи не вимагайте безпосередньо залежності, телефонуючи контейнер з вашого коду. Попросіть це неявно, використовуючи конструктор ін'єкцій .
Використовуйте інжекцію конструктора
Коли вам потрібна залежність, запитайте про неї статично через конструктор:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
Зверніть увагу, як клас обслуговування гарантує інваріантам. Щойно створений екземпляр, залежність буде гарантовано доступною через поєднання Застереження та readonly
ключового слова.
Використовуйте абстрактний завод, якщо вам потрібен короткочасний об’єкт
Залежності, що вводяться за допомогою конструкторської інжекції, мають тенденцію до довговічності, але іноді потрібен короткотривалий об'єкт або побудувати залежність на основі значення, відомого лише під час виконання.
Дивіться це для отримання додаткової інформації.
Складайте лише в останній відповідальний момент
Оберігайте об’єкти, розв'язані до самого кінця. Зазвичай, ви можете зачекати та передати все, що знаходиться у точці входу програми. Це називається складом кореня .
Детальніше тут:
Спростіть за допомогою фасаду
Якщо ви вважаєте, що отриманий API стає занадто складним для початківців користувачів, ви завжди можете надати кілька класів фасадів, які інкапсулюють загальні комбінації залежностей.
Щоб забезпечити гнучкий фасад з високим ступенем розкриття, ви можете розглянути можливість створення плавних будівельників. Щось на зразок цього:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
Це дозволить користувачеві створити Foo за замовчуванням, написавши
var foo = new MyFacade().CreateFoo();
Однак було б дуже зрозуміло, що можна поставити власну залежність, і ви могли б написати
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Якщо ви уявляєте, що клас MyFacade включає в себе багато різних залежностей, я сподіваюся, що зрозуміло, яким чином він би забезпечив правильні параметри за замовчуванням, роблячи відкриття розширюваності.
FWIW, довго після написання цієї відповіді, я розширив поняття, розміщені в цій статті, і написав довший допис у блозі про бібліотеки , сприятливі для DI , та супутню публікацію про рамки, зручні для DI .