Freeze
/Inject
/Register
в AutoFixtureДавным-давно, в первых версиях библиотеки балом правил
Register
. Два других метода ещё не существовали.Определён он был следующим образом:
public static void Register<T>(this IFixture fixture, T item)
Однако, была и перегрузка:public static void Register<T>(this IFixture fixture, Func<T> creator)
Какое-то время казалось, что это нормально, впрочем, вскоре пользователи стали путаться. Например, можно было написать следующий код:fixture.Register(() => universe.LightUp());
Поскольку делегат может получаться путём приведения ссылкой на метод, то валидной была и следующая запись:fixture.Register(universe.LightUp);
Что если universe.LightUp
это обращение к свойству, а не к методу? Тогда была бы выбрана первая перегрузка.Но это крайне неочевидно.
Поэтому первая перегрузка превратилась в
Inject<T>(this IFixture fixture, T item)
.У метода
Freeze
несколько другая история.В прошлом оказалось, что достаточно часто встречается следующее использование AutoFixture в императивном стиле:
var foo = fixture.Create<Foo>();
fixture.Inject(foo);
Тогда, автор библиотеки решил, что это так называемая концепция заморозки.В итоге два вызова превратились в один метод
Freeze
.#полезное #tips
Какую самую главную ошибку совершают новички в ООП?
Начав изучать ООП, многие знакомятся с его тремя столпами: инкапсуляция, наследование и полиморфизм. Но удаётся ли правильно понять все из них?
Если выполнить поиск определения инкапсуляции в Google, то можно получить примерно следующее:
«Инкапсуляция в программировании — это принцип, согласно которому внутреннее устройство сущностей нужно объединять в специальной «оболочке» и скрывать от вмешательств извне.»
А теперь вспомним, что самыми популярными объектно-ориентированными языками программирования являются Java и C#, где модификаторы доступа (
На самом же деле, корректнее привести следующее определение:
«Инкапсуляция – это объединение в рамках одной структуры функций и данных, с которыми эти функции работают.»
То есть, в случае объектно-ориентированного программирования, создав некоторый объект, мы связали его данные с определёнными методами на всё время его жизни.
А в случае, например, обычных функций мы просто описали некий алгоритм, который описывает последовательность действий никак не связанных с некоторым состоянием.
Вот простой пример, чтобы понять разницу:
Начав изучать ООП, многие знакомятся с его тремя столпами: инкапсуляция, наследование и полиморфизм. Но удаётся ли правильно понять все из них?
Если выполнить поиск определения инкапсуляции в Google, то можно получить примерно следующее:
«Инкапсуляция в программировании — это принцип, согласно которому внутреннее устройство сущностей нужно объединять в специальной «оболочке» и скрывать от вмешательств извне.»
А теперь вспомним, что самыми популярными объектно-ориентированными языками программирования являются Java и C#, где модификаторы доступа (
private
, public
, protected
, etc.) это одни из самых используемых ключевых слов. И получается, что начинающие программисты путают сокрытие с инкапсуляцией.На самом же деле, корректнее привести следующее определение:
«Инкапсуляция – это объединение в рамках одной структуры функций и данных, с которыми эти функции работают.»
То есть, в случае объектно-ориентированного программирования, создав некоторый объект, мы связали его данные с определёнными методами на всё время его жизни.
А в случае, например, обычных функций мы просто описали некий алгоритм, который описывает последовательность действий никак не связанных с некоторым состоянием.
Вот простой пример, чтобы понять разницу:
class Doubler
{
private readonly int value;
public Doubler(int value) => this.value = value;
public int Double() => this.value * 2;
}
//…
public int Double(int value) => value * 2;
#полезное #tips❤16👍2
Многоликая регистрация в стандартном контейнере
Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:
Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры
Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:
public interface IBar {}Наша задача зарегистировать экземпляр
public interface IFoo {}
public class FooBar : IFoo, IBar {}
FooBar
таким образом, чтобы при внедрении зависимостей IBar
и IFoo
использовался один и тот же объект.Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры
FooBar
:services.AddSingleton<IFoo, FooBar>();Но проблема вполне решаема, достаточно лишь зарегистрировать вначале экземпляр
services.AddSingleton<IBar, FooBar>();
FooBar
и затем ссылаться на него:services.AddSingleton<FooBar>();#полезное #tips
services.AddSingleton<IFoo>(x => x.GetRequiredService<FooBar>());
services.AddSingleton<IBar>(x => x.GetRequiredService<FooBar>());
👍31
Autofac. Именованные сервисы
Контейнер Autofac предоставляет возможность внедрять конкретные зависимости, явно указывая некоторый ключ, который соотносится с желаемой зависимостью.
Например, у нас есть сервис
Чтобы указать, что мы хотим внедрить конкретную реализацию
По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.
Пример:
Контейнер Autofac предоставляет возможность внедрять конкретные зависимости, явно указывая некоторый ключ, который соотносится с желаемой зависимостью.
Например, у нас есть сервис
IDisplay
, отображающий какие-то произведения искусства IArtwork
.Чтобы указать, что мы хотим внедрить конкретную реализацию
MyPainting
, можно использовать атрибут KeyFilterAttribute
.По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.
Пример:
public class ArtDisplay : IDisplay#полезное #tips
{
public ArtDisplay([KeyFilter("MyPainting")] IArtwork art) { ... }
}
// ...
var builder = new ContainerBuilder();
builder.RegisterType<MyPainting>().Keyed<IArtwork>("MyPainting");
builder.RegisterType<ArtDisplay>().As<IDisplay>().WithAttributeFiltering();
// ...
var container = builder.Build();
❤4👍2🤔2❤🔥1
Знали ли вы...
Что в .NET 7 при разработке API больше не требуется явно указывать атрибут
Теперь биндинг параметров у действий в контроллерах также отслеживает то, что приходит из DI контейнера.
Соответственно, такой код спокойно отработает без ошибок:
#полезное #tips
Что в .NET 7 при разработке API больше не требуется явно указывать атрибут
[FromServices]
для зависимостей, указанных в параметре метода?Теперь биндинг параметров у действий в контроллерах также отслеживает то, что приходит из DI контейнера.
Соответственно, такой код спокойно отработает без ошибок:
Services.AddScoped<SomeCustomType>();Ну а в случае, когда такое неявное поведение не нравится или не требуется, его можно отключить следующим образом:
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
public ActionResult Get(SomeCustomType service) => Ok();
}
Services.Configure<ApiBehaviorOptions>(options =>Поделитесь в комментариях темами, которые хотелось бы разобрать на канале.
{
options.DisableImplicitFromServicesParameters = true;
})
#полезное #tips
👍13😨2
Как изменить таймаут для конкретного запроса в
Одной из лучших практик по работе с
Однако, возникают ситуации, когда для разных запросов требуется разное поведение клиента. Например, разные таймауты.
Проблема в том, что
Но я бы не писал этот пост, если бы не существовало решения проблемы. А решение довольно простое:
1️⃣ Пользовательский таймаут меньше того, что установлен в
#полезное #tips
HttpClient
?Одной из лучших практик по работе с
HttpClient
в C# считается переиспользование одного экземпляра клиента для множества запросов. Как минимум во избежание port exhaustion.Однако, возникают ситуации, когда для разных запросов требуется разное поведение клиента. Например, разные таймауты.
Проблема в том, что
HttpClient.Timeout
устанавливается единожды, во время создания клиента. И несмотря на наличие public set
'тера, это значение не может быть изменено впоследствии. Любые попытки пресекаются выбрасыванием InvalidOperationException
.Но я бы не писал этот пост, если бы не существовало решения проблемы. А решение довольно простое:
TimeSpan timeout = GetMyTimeout();Такое решение можно не только использовать "в лоб", но и обернуть в пайплайн из
using (var tokenSource = new CancellationTokenSource(timeout))
{
var response = await httpClient.GetAsync(uri, tokenSource.Token);
HandleResponse(response);
}
DelegatingHandler
'ов. Для того чтобы оно работало, потребуется убедиться в двух вещах:1️⃣ Пользовательский таймаут меньше того, что установлен в
HttpClient.Timeout
2️⃣ Пользовательский таймаут валиден. Проще говоря, время ожидания больше 0 секунд.#полезное #tips
❤9👍4
marker interface
Для чего их вообще используют?
Допустим, в коде используется некий объект, который реализует указанный интерфейс. Тогда, появляется возможность проверить реализуется ли он и скорректировать на основе этого обработку объекта.
Также, маркерный интерфейс может быть необходимым злом при отсутствии поддержки в языке discriminated union types.
К сожалению, в объектно-ориентированных языках вроде C# объявить тип, который будет чем-то конкретным из указанного набора, невозможно.
Поэтому, приходится прибегать к таким уловкам.
Почему маркерных интерфейсов стоит избегать?
Главная проблема такого подхода - нарушение инкапсуляции.
Объект сам по себе теперь обладает неявным контролем над возможностями внешнего использования. Более того, он знает окружение, в котором будет использоваться.
Применение маркерного интерфейса подразумевает, что где-то будет находится проверка на этот маркер. Это противоречит идее инкапсуляции, потому что у объекта появляется знание о реализации той части системы, которая находится совершенно вне зоны его «полномочий».
В общем, обычный интерфейс говорит окружающему миру о том как он может быть использован, пустой, маркерный, о том, как должен быть использован.
#полезное #tips
Для чего их вообще используют?
Допустим, в коде используется некий объект, который реализует указанный интерфейс. Тогда, появляется возможность проверить реализуется ли он и скорректировать на основе этого обработку объекта.
Также, маркерный интерфейс может быть необходимым злом при отсутствии поддержки в языке discriminated union types.
К сожалению, в объектно-ориентированных языках вроде C# объявить тип, который будет чем-то конкретным из указанного набора, невозможно.
Поэтому, приходится прибегать к таким уловкам.
Почему маркерных интерфейсов стоит избегать?
Главная проблема такого подхода - нарушение инкапсуляции.
Объект сам по себе теперь обладает неявным контролем над возможностями внешнего использования. Более того, он знает окружение, в котором будет использоваться.
Применение маркерного интерфейса подразумевает, что где-то будет находится проверка на этот маркер. Это противоречит идее инкапсуляции, потому что у объекта появляется знание о реализации той части системы, которая находится совершенно вне зоны его «полномочий».
В общем, обычный интерфейс говорит окружающему миру о том как он может быть использован, пустой, маркерный, о том, как должен быть использован.
#полезное #tips
👍4
На пальцах про AAA
Как вы могли понять речь пойдёт не о батарейках или играх. Сегодняшний пост про юнит-тестирование.
Согласитесь, в таком сложном мире как программирование сложно ориентироваться, когда всё лежит где попало, не на своих местах.
Гораздо проще, когда всё структурировано, лежит, так сказать, по полочкам.
Однажды, умные разработчики подумали, и поняли, что и тесты можно структурировать и разложить на конкретные и понятные этапы.
Arrange
В этой секции находится код, ответственный за настройку теста. Создание объектов, подготовка данных, настройка моков и так далее.
Act
Затем идёт действие. То есть, непосредственно вызов тестируемого функционала.
Assert
Финальный этап - проверка. Проверяется всё что требуется проверить. Какие получились данные, состояние объектов, вызвалось ли то что нужно, была ли ошибка. В общем, есть где развернуться.
Вот и получается, что паттерн Arrange-Act-Assert за счёт своей простоты и эффективности в отношении организации и написания тестов стал де-факто стандартом индустрии.
#полезное #tips
Как вы могли понять речь пойдёт не о батарейках или играх. Сегодняшний пост про юнит-тестирование.
Согласитесь, в таком сложном мире как программирование сложно ориентироваться, когда всё лежит где попало, не на своих местах.
Гораздо проще, когда всё структурировано, лежит, так сказать, по полочкам.
Однажды, умные разработчики подумали, и поняли, что и тесты можно структурировать и разложить на конкретные и понятные этапы.
Arrange
В этой секции находится код, ответственный за настройку теста. Создание объектов, подготовка данных, настройка моков и так далее.
Act
Затем идёт действие. То есть, непосредственно вызов тестируемого функционала.
Assert
Финальный этап - проверка. Проверяется всё что требуется проверить. Какие получились данные, состояние объектов, вызвалось ли то что нужно, была ли ошибка. В общем, есть где развернуться.
Вот и получается, что паттерн Arrange-Act-Assert за счёт своей простоты и эффективности в отношении организации и написания тестов стал де-факто стандартом индустрии.
#полезное #tips
👍9
Do I need to run tests before
На текущем проекте мы используем Kafka. Так вышло, что я - MacBook enjoyer и пишу код на m1 машине.
Соответственно, интеграционные тесты, задействующие Kafka тупо не запускаются.
И какое-то время назад у меня в голове возник вопрос: «а должно ли это вообще меня волновать?»
Ладно, Kafka. Но в большом коммерческом проекте есть ещё много других вещей, которые нужно было бы поднимать на своей машине, просто чтобы запустить приложение:
▪️Эмулятор внешних систем (mock интеграций);
▪️Базы данных;
▪️Кэш;
▪️Gateway микросервисов;
И многое другое…
Зачем мне засорять компьютер, когда уже есть облако с окружением, где крутятся пайплайны, триггернутые коммитом? CI/CD - это автоматизация всей вот этой рутины. И я воспользуюсь этим технологическим достижением, чтобы упростить себе жизнь.
Смысл прогонять тесты на машине, если репорт будет читаться из пайплайна в гитлабе?
#полезное #tips
push
?На текущем проекте мы используем Kafka. Так вышло, что я - MacBook enjoyer и пишу код на m1 машине.
Соответственно, интеграционные тесты, задействующие Kafka тупо не запускаются.
И какое-то время назад у меня в голове возник вопрос: «а должно ли это вообще меня волновать?»
Ладно, Kafka. Но в большом коммерческом проекте есть ещё много других вещей, которые нужно было бы поднимать на своей машине, просто чтобы запустить приложение:
▪️Эмулятор внешних систем (mock интеграций);
▪️Базы данных;
▪️Кэш;
▪️Gateway микросервисов;
И многое другое…
Зачем мне засорять компьютер, когда уже есть облако с окружением, где крутятся пайплайны, триггернутые коммитом? CI/CD - это автоматизация всей вот этой рутины. И я воспользуюсь этим технологическим достижением, чтобы упростить себе жизнь.
Смысл прогонять тесты на машине, если репорт будет читаться из пайплайна в гитлабе?
#полезное #tips
👍4❤1🤣1