Теоретически, чтобы не беспокоиться о наличии статического экземпляра IoC, вы должны следовать правилу Fight Club - т.е. не говорить о бойцовском клубе - то есть не упоминать контейнер IoC.
Это означает, что ваши компоненты не должны знать о контейнере IoC. Его следует использовать только на самом верхнем уровне при регистрации компонентов. Если класс должен что-то разрешить, он действительно должен быть введен как зависимость.
Тривиальный случай достаточно прост. Если PaymentService
зависит от IAccount
, последний должен быть введен IoC:
interface IAccount {
Deposit(int amount);
}
interface CreditCardAccount : IAccount {
void Deposit(int amount) {/*implementation*/}
int CheckBalance() {/*implementation*/}
}
class PaymentService {
IAccount account;
public PaymentService (IAccount account) {
this.account = account;
}
public void ProcessPayment() {
account.Deposit(5);
}
}
//Registration looks something like this
container.RegisterType<IAccount, CreditCardAccount>();
container.RegisterType<PaymentService>();
Не совсем тривиальный случай, когда вы хотите ввести несколько регистраций. Это особенно относится к случаям, когда вы выполняете любые виды Converntion Over Configuration и создаете объект по имени.
В нашем примере оплаты, скажем, вы хотите перечислить все счета и проверить их остатки:
class PaymentService {
IEnumerable<IAccount> accounts;
public PaymentService (IEnumerable<IAccount> accounts) {
this.accounts = accounts;
}
public void ProcessPayment() {
foreach(var account in accounts) {
account.Chackbalance();
}
}
}
Unity имеет возможность зарегистрировать несколько интерфейсов для сопоставления классов (они должны иметь разные имена). Однако он не внедряет их автоматически в классы, которые принимают коллекции этих зарегистрированных интерфейсов. Таким образом, приведенный выше пример будет выдавать исключение при разрешении во время выполнения.
Если вам все равно, что эти объекты живут вечно, вы можете зарегистрировать PaymentService
в более статичном режиме:
container.RegisterType<PaymentService>(new InjectionConstructor(container.ResolveAll<IAccount>()));
Приведенный выше код зарегистрирует PaymentService
и будет использовать коллекцию IAccount
экземпляров, которая разрешена во время регистрации.
В качестве альтернативы , вы можете передать экземпляр самого контейнера как зависимость и позволить PaymentService
выполнить разрешение учетных записей. Это не совсем соответствует правилу Бойцовского клуба, но немного менее вонючий, чем статический Service Locator.
class PaymentService {
IEnumerable<IAccount> accounts;
public PaymentService (IUnityContainer container) {
this.accounts = container.ResolveAll<IAccount>();
}
public void ProcessPayment() {
foreach(var account in accounts) {
account.Chackbalance();
}
}
}
//Registration is pretty clean in this case
container.RegisterType<IAccount, CreditCardAccount>();
container.RegisterType<PaymentService>();
container.RegisterInstance<IUnityContainer>(container);