Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Продолжение
Начало
Устойчивый декоратор
Задача в этом примере - сделать зависимость IService более устойчивой. Класс MyApi станет более устойчивым транзитивно, получив более устойчивую зависимость. Поэтому первым шагом рефакторинга является введение устойчивого декоратора:
public class ResilientService(
ResiliencePipeline pipeline,
IService inner) : IService
{
public QueryResult RetrieveMultiple(
QueryByAttribute query)
{
return pipeline.Execute(()
=> inner.RetrieveMultiple(query));
}
}
Как и все декораторы, этот класс содержит экземпляр IService, а также сам реализует этот интерфейс. Вместо внедрения ResiliencePipelineProvider<string> только для вызова GetPipeline для него, он просто получает ResiliencePipeline и сохраняет объект для использования в методе RetrieveMultiple. Обратите внимание, что тут используется первичный конструктор.
Упрощение MyApi
Теперь, нам не нужен код Polly в MyApi:
public class MyApi
{
private IService _svc;
public MyApi(IService svc)
{
_svc = svc;
}
public List<string> GetSomething(
QueryByAttribute query)
{
var result = svc.RetrieveMultiple(query);
return result.Entities
.Cast<string>().ToList();
}
}
Да, в MyApi почти ничего не осталось, но напомню, что у нас упрощённый пример, и GetSomething может быть гораздо длиннее и требовать тестирования. А в нынешнем виде тестирование тривиально:
[Theory]
[InlineData("foo", "bar", "baz")]
public void GetSomething(params string[] expected)
{
var svc = new Mock<IService>();
svc
.Setup(s => s.RetrieveMultiple(
new QueryByAttribute()))
.Returns(new QueryResult(expected));
var sut = new MyApi(svc.Object);
var actual = sut.GetSomething(
new QueryByAttribute());
Assert.Equal(expected, actual);
}
Однако важнее то, что теперь нам не только удалось исключить сторонние зависимости из кода приложения, но и упростить его. Создать устойчивый объект MyApi можно в корне композиции:
var svc = new ResilientService(pipeline, inner);
var myApi = new MyApi(svc);
Так мы отделяем код приложения от сторонних зависимостей. Мы определяем ResilientService в корне композиции и оставляем зависимость от Polly там.
Окончание следует…
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/