Вы можете подумать о контейнерах (очень умных фабриках) и советах (чтобы справиться со всеми беспорядочными деталями).
Dear Mr. Container Sir,
Whenever I request from you an instance object of the interface ISomething,
please construct for me an instance of the concrete class SomethingImpl;
in addition, please see to it (however you do it) that, whenever I call a
method on this instance, it is wrapped within a complicated and messy try-
catch-finally which logs exceptions and mark calls as completed. That way,
all I have to do is write the business logic that goes into the SomethingImpl
and I don't have to worry about all the messy infrastuctural details.
Sincerely,
Mr. Agile.
Вы можете увидеть это в коде как:
//a class that knows how to take care of the messy infrastructure details
public class MyMessyInterceptor : IInterceptor {
public void Intercept(IInvocation invocation) {
//handle the messy details of continuing with the method-invocation,
//but within a try-catch-finally that includes exception handling and
//call logging.
}
}
//a function that will configure a container (very smart factory)
public IContainer CreateContainer() {
var builder = new ContainerBuilder();
//tell the container-builder about the interceptor
builder
.Register(c => new MyMessyInterceptor())
.Named("keep-my-code-clean")
;
//tell the container what to do when you ask it for a ISomething
builder
.Register<SomethingImpl>()
.As<ISomething>()
.InterceptedBy("keep-my-code-clean")
;
return builder.BuildContainer();
}
//some function out there in your code somewhere that needs to make a
//service call; there's hundreds of functions out there just like this
//in your code, and they all just got much simpler
public object GottaGoDoSomething() {
//find the container
var container = GetTheSingletonContainerObject();
//ask for an instance of ISomething - it knows to provide a
//SomethingImpl wrapped in an interceptor that takes care of all
//the logging and exception handling
var something = container.resolve<ISomething>();
//call the big method
return something.DoSomething();
//magically (not really), the exception handling and logging are
//already taken care of
}
Выход в класс перехватчика происходит только один раз. Регистрация каждого перехватчика и класса обслуживания также происходит только один раз. Настройка контейнера (очень умная фабрика), безусловно, сложна.
Однако каждое место в вашем коде, которое должно использовать объект службы и должно встраивать это использование в сложные и запутанные детали инфраструктуры, такие как обработка исключений и ведение журнала, просто стало очень чистым и очень простым. Есть только один CreateContainer
, но есть сотни GottaGoDoSomething
с, так что это очень легко за счет немного сложного.
(Примечания. В примере кода используются контейнерная среда Autofac и инфраструктура перехватчиков Castle. Мне известно, что это пример шаблона расположения службы, а не шаблона внедрения зависимости, но целью было проиллюстрировать перехватчики и регистрация их в контейнере, а не для иллюстрации внедрения-зависимости.)