День 1072. #NET6
Не Делайте Этого В .NET 6, Делайте Вот Так. Окончание
Начало
4. Не используйте костыли для обработки исключений в Blazor Server, используйте компонент ErrorBoundary
Что происходит при возникновении необработанного исключения в Blazor Server? Оно считается фатальным, поскольку «цепь остается в неопределённом состоянии, что может привести к проблемам со стабильностью или безопасностью в Blazor Server». В результате вам может потребоваться разбрасывать повсюду блоки try/catch в качестве превентивной меры или инкапсулировать логику в JavaScript, поскольку код C# после необработанного исключения не будет выполнен.
Не делайте так. Вместо этого используйте новый компонент
Если я хочу включить подробный логгинг для Kestrel, раньше нужно было использовать
В C# 10 представлены пространства имён уровня файлов. Вот обычное объявление класса в C# до версии 10:
Не Делайте Этого В .NET 6, Делайте Вот Так. Окончание
Начало
4. Не используйте костыли для обработки исключений в Blazor Server, используйте компонент ErrorBoundary
Что происходит при возникновении необработанного исключения в Blazor Server? Оно считается фатальным, поскольку «цепь остается в неопределённом состоянии, что может привести к проблемам со стабильностью или безопасностью в Blazor Server». В результате вам может потребоваться разбрасывать повсюду блоки try/catch в качестве превентивной меры или инкапсулировать логику в JavaScript, поскольку код C# после необработанного исключения не будет выполнен.
Не делайте так. Вместо этого используйте новый компонент
ErrorBoundary
. Это не глобальный обработчик исключений, но он поможет справиться с непредсказуемым поведением, особенно с компонентами, которые вы не контролируете.<div class="main">Конечно, можно не перехватывать все исключения:
<div class="content px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</div>
</div>
<tbody>5. Не используйте подробный логгинг в Server.Kestrel, используйте новые подкатегории
@foreach (var emp in Employees)
{
<ErrorBoundary @key="@emp">
<ChildContent>
<tr>
<td>@emp.Id</td>
<td>@emp.FirstName</td>
<td>@emp.LastName</td>
</tr>
</ChildContent>
<ErrorContent>
Не удалось отобразить #@emp.Id.
</ErrorContent>
</ErrorBoundary>
}
</tbody>
Если я хочу включить подробный логгинг для Kestrel, раньше нужно было использовать
Microsoft.AspNetCore.Server.Kestrel
. Это всё ещё существует, но есть и новые подкатегории, которые упрощают жизнь. В дополнение к Server.Kestrel
теперь есть Kestrel.BadRequests
, Kestrel.Connections
, Kestrel.Http2
и Kestrel.Http3
. Допустим, вы хотите регистрировать только неверные запросы:{Не делайте так. Делайте вот так:
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Server.Kestrel": "Debug"
}
}
}
{6. Не теряйтесь в скобках, используйте пространства имён уровня файлов
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Server.Kestrel.BadRequests": "Debug"
}
}
}
В C# 10 представлены пространства имён уровня файлов. Вот обычное объявление класса в C# до версии 10:
namespace SuperheroApp.ModelsТеперь можно убрать скобки в пространстве имён, а также получить все преимущества неизменяемости, использовав записи:
{
public class Superhero
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
}
namespace SuperheroApp.Models;Кстати, о скобках. В C# 10 также введены новые шаблоны свойств. В выражении
public record Superhero(string? FirstName, string? LastName);
switch
вместо var tax = sales switchможно использовать более ясный формат:
{
{ Address: { State: "AZ" } } => 1.03M,
{ Address: { State: "FL" } } => 1.07M,
{ Address: { State: "CA" } } => 1.12M
_ => 0
};
var tax = sales switchИсточник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
{
{ Address.State: "AZ" } => 1.03M,
{ Address.State: "FL" } => 1.07M,
{ Address.State: "CA" } => 1.12M
_ => 0
};
👍10
День 1073. #Testing
Исправлять ли Ошибки в Приватных Методах, Если Они Компенсируют Друг Друга?
Недавно наткнулся на интересное обсуждение. Нужно ли исправлять ошибки, которые компенсируют друг друга и в итоге приводят к правильному поведению?
Допустим, у нас есть такой код:
Как следует тестировать этот метод? Общее мнение, что вам следует тестировать только публичные методы. Приватные методы следует тестировать косвенно, через публичные методы, которые их используют.
Другими словами,
Но разве этот совет не заставляет вас игнорировать ошибки в
В нашем примере метод
Клиентов
Так что да, этот совет потенциально может привести к незамеченным ошибкам в приватных методах. Но нет, это не проблема, потому что эти ошибки никогда не проявятся. Если только приватные методы не используются где-либо ещё, и там ошибка проявляется в публичных методах. Но тогда она должна быть обнаружена тестами тех публичных методов.
Я бы даже сказал, что это вовсе не ошибки, потому что клиенты вашего приложения не могут с ними столкнуться. Как говорится, если в лесу упало дерево и никто этого не услышал, можно ли считать, что оно упало беззвучно?
Однако, если бы эти методы не были приватными, всё было бы по-другому. Теперь, когда эти методы являются частью общедоступного API, они также становятся частью наблюдаемого поведения и, следовательно, должны быть протестированы и исправлены. Но до тех пор ошибки в приватных методах - не проблема.
Конечно, если вы заметили ошибки в приватных методах
Но это «исправление» будет рефакторингом, а не исправлением ошибки: здесь вы меняете детали реализации; наблюдаемое поведение остаётся неизменным.
Источник: https://enterprisecraftsmanship.com/
Автор Оригинала: Владимир Хориков
Исправлять ли Ошибки в Приватных Методах, Если Они Компенсируют Друг Друга?
Недавно наткнулся на интересное обсуждение. Нужно ли исправлять ошибки, которые компенсируют друг друга и в итоге приводят к правильному поведению?
Допустим, у нас есть такой код:
public void PerformAction()Допустим, что и
{
Step1();
Step2();
}
private void Step1()
{ ... }
private void Step2()
{ ... }
Step1
, и Step2
содержат ошибки в своей реализации, но общее поведение метода PerformAction
является правильным, потому что по чистой случайности ошибки в двух приватных методах взаимно компенсируются.Как следует тестировать этот метод? Общее мнение, что вам следует тестировать только публичные методы. Приватные методы следует тестировать косвенно, через публичные методы, которые их используют.
Другими словами,
PerformAction
- единственный метод, который здесь следует тестировать. Step1
и Step2
вообще не должны рассматриваться.Но разве этот совет не заставляет вас игнорировать ошибки в
Step1
и Step2
, поскольку поведение PerformAction
остаётся правильным? Это отличный вопрос, и чтобы ответить на него, нам нужно ещё раз обсудить разницу между «наблюдаемым поведением» и «деталями реализации».В нашем примере метод
PerformAction
- это наблюдаемое поведение, а Step1
и Step2
- детали реализации. Поэтому единственное, что имеет значение, - это поведение PerformAction
.Клиентов
PerformAction
не волнует, как именно этот метод выполняет свою функцию. Единственное, что их волнует, - это конечный результат - его наблюдаемое поведение. Модульные тесты всегда должны имитировать клиентов тестируемого кода и, следовательно, не должны заботиться о двух приватных методах.Так что да, этот совет потенциально может привести к незамеченным ошибкам в приватных методах. Но нет, это не проблема, потому что эти ошибки никогда не проявятся. Если только приватные методы не используются где-либо ещё, и там ошибка проявляется в публичных методах. Но тогда она должна быть обнаружена тестами тех публичных методов.
Я бы даже сказал, что это вовсе не ошибки, потому что клиенты вашего приложения не могут с ними столкнуться. Как говорится, если в лесу упало дерево и никто этого не услышал, можно ли считать, что оно упало беззвучно?
Однако, если бы эти методы не были приватными, всё было бы по-другому. Теперь, когда эти методы являются частью общедоступного API, они также становятся частью наблюдаемого поведения и, следовательно, должны быть протестированы и исправлены. Но до тех пор ошибки в приватных методах - не проблема.
Конечно, если вы заметили ошибки в приватных методах
Step1
и Step2
, лучше всего исправить их независимо от того, заметны ли они, чтобы избежать путаницы для будущих читателей этих методов.Но это «исправление» будет рефакторингом, а не исправлением ошибки: здесь вы меняете детали реализации; наблюдаемое поведение остаётся неизменным.
Источник: https://enterprisecraftsmanship.com/
Автор Оригинала: Владимир Хориков
👍8
Утащу из твиттера. Какой вариант предпочитаете:
Anonymous Poll
29%
var widgets = new Widget[] { new Widget() };
19%
var widgets = new Widget[] { new() };
35%
var widgets = new[] { new Widget() };
17%
Widget[] widgets = { new() };
👍14
День 1074. #ЗаметкиНаПолях #AsyncTips
Параллельное агрегирование
Задача: требуется агрегировать результаты (суммирование значений или вычисление среднего) при завершении параллельной операции.
Решение
Для поддержки агрегирования класс
UPD: Обновил пример ParallelSum примером из документации с использованием Interlocked.Add вместо lock.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 4.
Параллельное агрегирование
Задача: требуется агрегировать результаты (суммирование значений или вычисление среднего) при завершении параллельной операции.
Решение
Для поддержки агрегирования класс
Parallel
использует концепцию локальных значений — переменных, существующих локально внутри параллельного цикла. Это означает, что тело цикла может просто обратиться к значению напрямую, без необходимости синхронизации. Когда цикл готов к агрегированию всех своих локальных результатов, он делает это с помощью делегата localFinally
. Следует отметить, что делегату localFinally
нужно синхронизировать доступ к переменной для хранения результата. Пример параллельного суммирования:int ParallelSum(IEnumerable<int> values)В Parallel LINQ реализована более понятная поддержка агрегирования, чем в классе
{
int result = 0;
Parallel.ForEach(source: values,
localInit: () => 0,
body: (item, state, localValue) =>
localValue + item,
localFinally: localValue =>
Interlocked.Add(ref result, localValue)
);
return result;
}
Parallel
:int ParallelSum(IEnumerable<int> values)На самом деле в PLINQ реализована встроенная поддержка многих распространённых операторов (например,
{
return values.AsParallel().Sum();
}
Sum
). Также предусмотрена обобщённая поддержка агрегирования с оператором Aggregate
:int ParallelSum(IEnumerable<int> values)Если вы уже используете класс
{
return values.AsParallel().Aggregate(
seed: 0,
func: (sum, item) => sum + item
);
}
Parallel
, следует использовать его поддержку агрегирования. В остальных случаях поддержка в PLINQ, как правило, более выразительна, а код получается короче.UPD: Обновил пример ParallelSum примером из документации с использованием Interlocked.Add вместо lock.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 4.
В дополнение к предыдущему посту.
Вот именно поэтому нужно читать источники в оригинале. Там может ошибиться только автор, но хотя бы нет ошибок переводчика. И хотя в целом перевод Клири более менее качественный, вот вам пример, разворачивающий смысл на 180 градусов. Сам 3 раза прочитал текст, потом код, потом опять текст. Не сходилось. Хорошо, что под рукой был оригинал.
Вот именно поэтому нужно читать источники в оригинале. Там может ошибиться только автор, но хотя бы нет ошибок переводчика. И хотя в целом перевод Клири более менее качественный, вот вам пример, разворачивающий смысл на 180 градусов. Сам 3 раза прочитал текст, потом код, потом опять текст. Не сходилось. Хорошо, что под рукой был оригинал.
👍15
День 1075. #ЧтоНовенького
В C #10 появился новый атрибут
Кстати, его использует новый метод
Если раньше нам нужно было писать что-то подобное:
В C #10 появился новый атрибут
CallerArgumentExpression
. Он позволяет записывать выражение, переданное в качестве параметра, в виде строки (см. рисунок). Как и другие атрибуты CompilerServices
, он применяется к необязательному параметру.Кстати, его использует новый метод
ArgumentNullException.ThrowIfNull(nullArg);в .NET без явной передачи имени.
Если раньше нам нужно было писать что-то подобное:
public bool DoSomething(string id, string name)Теперь новый метод позволяет писать более чистый код:
{
if (id is null)
throw new ArgumentNullException(nameof(id));
if (name is null)
throw new ArgumentNullException(nameof(id));
// ...
}
public bool DoSomething(string id, string name)Источник: https://twitter.com/Nick_Craver/status/1476929172249448459
{
ArgumentNullException.ThrowIfNull(id);
ArgumentNullException.ThrowIfNull(name);
// ...
}
👍13
День 1076. #NuGet
10 Лучших NuGet Пакетов для Повышения Продуктивности в 2022. Начало
Сегодня мы рассмотрим лучшие NuGet пакеты для повышения продуктивности разработчиков на C#.
1. RestSharp
RestSharp — самая популярная клиентская библиотека HTTP для .NET. Используя эту библиотеку, разработчики C# могут легко вызывать удалённые ресурсы по HTTP. Кроме того она заботится о сериализации тела запроса в JSON или XML и десериализации ответа.
Пакет RestSharp поддерживает:
- синхронные и асинхронные запросы,
- сериализацию и десериализацию,
- различные методы HTTP-запросов (GET, POST, PUT и DELETE),
- разнообразные способы аутентификации.
Всего загрузок: 111,1 млн.
2. Json.NET
Json.NET — это бесплатная библиотека с открытым исходным кодом для .NET, которую скачали более миллиарда раз. Её ключевые особенности включают в себя:
- Сериализацию и десериализацию любого объекта .NET в JSON и текста JSON в объект .NET.
- Преобразование между XML и JSON.
- Создание, анализ, запросы и изменение JSON, используя объекты JObject, JArray и JValue.
- Запросы к JSON с синтаксисом, подобным XPath.
- Высокую производительность.
Всего загрузок: 1,6 млрд.
3. Serilog
Логи — это записи действий, исключений, информации и предупреждений. Ведение журнала — важный фактор в разработке приложений, который помогает разработчикам легко обнаруживать и устранять проблемы. Serilog — это платформа логирования, используемая в .NET. Он записывает пользовательские свойства и данные в формате JSON.
В отличие от других инструментов ведения журналов, Serilog предоставляет структурированные журналы, которые позволяют производить поиск по записанным данным. Он также позволяет разработчикам легко записывать данные о событиях в консоль, в файлы и во все типы систем хранения.
Всего загрузок: 365,1 млн.
4. NUnit
Тестирование играет важную роль в разработке программного обеспечения для обеспечения корректности кода. NUnit — это платформа модульного тестирования с открытым исходным кодом для всех языков .NET, включая C#. С помощью NUnit вы можете легко тестировать большие приложения, разбивая их на небольшие модули. Это помогает нам находить проблемы прямо во время разработки.
Всего загрузок: 153,1 млн.
5. Insight.Database
Insight.Database — это микро-ORM для .NET, который преобразует объекты C# в записи базы данных и наоборот. Это набор методов расширения, упрощающих работу с базами данных в приложении C#.
Основные функции Insight.Database включают в себя:
- Автоматическое открытие/закрытие соединений с базой данных.
- Простое выполнение хранимых процедур.
- Поддержку нескольких наборов результатов.
Всего загрузок: 691,0 тыс.
Окончание следует…
Источник: https://www.syncfusion.com/blogs/post/10-best-c-nuget-packages-to-improve-your-productivity-in-2022.aspx/amp
10 Лучших NuGet Пакетов для Повышения Продуктивности в 2022. Начало
Сегодня мы рассмотрим лучшие NuGet пакеты для повышения продуктивности разработчиков на C#.
1. RestSharp
RestSharp — самая популярная клиентская библиотека HTTP для .NET. Используя эту библиотеку, разработчики C# могут легко вызывать удалённые ресурсы по HTTP. Кроме того она заботится о сериализации тела запроса в JSON или XML и десериализации ответа.
Пакет RestSharp поддерживает:
- синхронные и асинхронные запросы,
- сериализацию и десериализацию,
- различные методы HTTP-запросов (GET, POST, PUT и DELETE),
- разнообразные способы аутентификации.
Всего загрузок: 111,1 млн.
2. Json.NET
Json.NET — это бесплатная библиотека с открытым исходным кодом для .NET, которую скачали более миллиарда раз. Её ключевые особенности включают в себя:
- Сериализацию и десериализацию любого объекта .NET в JSON и текста JSON в объект .NET.
- Преобразование между XML и JSON.
- Создание, анализ, запросы и изменение JSON, используя объекты JObject, JArray и JValue.
- Запросы к JSON с синтаксисом, подобным XPath.
- Высокую производительность.
Всего загрузок: 1,6 млрд.
3. Serilog
Логи — это записи действий, исключений, информации и предупреждений. Ведение журнала — важный фактор в разработке приложений, который помогает разработчикам легко обнаруживать и устранять проблемы. Serilog — это платформа логирования, используемая в .NET. Он записывает пользовательские свойства и данные в формате JSON.
В отличие от других инструментов ведения журналов, Serilog предоставляет структурированные журналы, которые позволяют производить поиск по записанным данным. Он также позволяет разработчикам легко записывать данные о событиях в консоль, в файлы и во все типы систем хранения.
Всего загрузок: 365,1 млн.
4. NUnit
Тестирование играет важную роль в разработке программного обеспечения для обеспечения корректности кода. NUnit — это платформа модульного тестирования с открытым исходным кодом для всех языков .NET, включая C#. С помощью NUnit вы можете легко тестировать большие приложения, разбивая их на небольшие модули. Это помогает нам находить проблемы прямо во время разработки.
Всего загрузок: 153,1 млн.
5. Insight.Database
Insight.Database — это микро-ORM для .NET, который преобразует объекты C# в записи базы данных и наоборот. Это набор методов расширения, упрощающих работу с базами данных в приложении C#.
Основные функции Insight.Database включают в себя:
- Автоматическое открытие/закрытие соединений с базой данных.
- Простое выполнение хранимых процедур.
- Поддержку нескольких наборов результатов.
Всего загрузок: 691,0 тыс.
Окончание следует…
Источник: https://www.syncfusion.com/blogs/post/10-best-c-nuget-packages-to-improve-your-productivity-in-2022.aspx/amp
👍5
*Утащу. На этот раз у Немчинского*
Вы достигли целей, поставленных на 2021 год?
Вы достигли целей, поставленных на 2021 год?
Anonymous Poll
16%
Да
40%
Частично
14%
Нет
30%
Не пишу себе целей на год
👍2
День 1077. #NuGet
10 Лучших NuGet Пакетов для Повышения Продуктивности в 2022. Продолжение
Начало
6. FluentValidation
FluentValidation — это библиотека .NET для построения строго типизированных правил валидации. Она использует Fluent-интерфейс и лямбда-выражения для создания правил. Правила валидации помогают поддерживать чистоту кода домена, выделяя код проверки в отдельный блок.
FluentValidation имеет множество встроенных правил, таких как проверка на null, на пустую строку, максимальную и минимальную длину и т.п. Библиотека также поддерживает пользовательские валидаторы, настраиваемые сообщения валидации на основе имён свойств, предоставляет локализованные сообщения, асинхронные проверки и т.д.
Всего загрузок: 118,7 млн.
7. Noda Time
Работа с датой, временем и часовыми поясами в .NET очень болезненна. Noda Time заполняет пробел в операциях с датой и временем в .NET и тем самым упрощает жизнь. Пакет предоставляет различные API для обработки UTC, местного времени и часовых поясов. Также предоставляется функционал для простого преобразования местного времени в UTC и обратно, что позволяет избежать серьёзных ошибок, которые могут допустить разработчики, реализуя эту логику самостоятельно.
Всего загрузок: 46,2 млн.
8. FluentEmail
FluentEmail — это библиотека .NET с открытым исходным кодом, которая позволяет внедрить функции отправки электронной почты в ваше приложение .NET за 10 минут. Помимо основных параметров электронного письма, пакет позволяет вложить файл, отправлять почту асинхронно, а также использовать Razor для разработки шаблонов писем и отправлять письма с помощью SendGrid, MailGun, SMTP и т. д.
Всего загрузок: 1,8 млн.
9. Hangfire
Hangfire — это платформа с открытым исходным кодом, которая позволяет создавать, обрабатывать и управлять фоновыми заданиями. Вы можете запускать фоновые задания из основного процесса вашего приложения без необходимости создания отдельного сервиса.
Hangfire поддерживает широкий спектр фоновых задач: краткосрочные и долгосрочные, загружающие процессор или ввод-вывод, одноразовые и повторяющиеся.
Всего загрузок: 18,8 млн.
10. LazyCache
Кэширование — отличный способ повысить производительность приложения. LazyCache использует формат
LazyCache отлично подходит для кэширования вызовов базы данных, процедур построения сложных графов объектов и вызовов веб-сервисов, которые нужно временно сохранить для повышения производительности. Пакет позволяет кэшировать элементы в течение большего или меньшего времени, но по умолчанию он сохраняет кэшированные элементы до 20 минут.
Ключевые особенности LazyCache:
- Потокобезопасен и готов для использования в параллельном коде.
- Совместим с асинхронным кодом: доступна ленивая однократная оценка асинхронных делегатов с использованием метода
Всего загрузок: 7,3 млн.
Источник: https://www.syncfusion.com/blogs/post/10-best-c-nuget-packages-to-improve-your-productivity-in-2022.aspx/amp
10 Лучших NuGet Пакетов для Повышения Продуктивности в 2022. Продолжение
Начало
6. FluentValidation
FluentValidation — это библиотека .NET для построения строго типизированных правил валидации. Она использует Fluent-интерфейс и лямбда-выражения для создания правил. Правила валидации помогают поддерживать чистоту кода домена, выделяя код проверки в отдельный блок.
FluentValidation имеет множество встроенных правил, таких как проверка на null, на пустую строку, максимальную и минимальную длину и т.п. Библиотека также поддерживает пользовательские валидаторы, настраиваемые сообщения валидации на основе имён свойств, предоставляет локализованные сообщения, асинхронные проверки и т.д.
Всего загрузок: 118,7 млн.
7. Noda Time
Работа с датой, временем и часовыми поясами в .NET очень болезненна. Noda Time заполняет пробел в операциях с датой и временем в .NET и тем самым упрощает жизнь. Пакет предоставляет различные API для обработки UTC, местного времени и часовых поясов. Также предоставляется функционал для простого преобразования местного времени в UTC и обратно, что позволяет избежать серьёзных ошибок, которые могут допустить разработчики, реализуя эту логику самостоятельно.
Всего загрузок: 46,2 млн.
8. FluentEmail
FluentEmail — это библиотека .NET с открытым исходным кодом, которая позволяет внедрить функции отправки электронной почты в ваше приложение .NET за 10 минут. Помимо основных параметров электронного письма, пакет позволяет вложить файл, отправлять почту асинхронно, а также использовать Razor для разработки шаблонов писем и отправлять письма с помощью SendGrid, MailGun, SMTP и т. д.
Всего загрузок: 1,8 млн.
9. Hangfire
Hangfire — это платформа с открытым исходным кодом, которая позволяет создавать, обрабатывать и управлять фоновыми заданиями. Вы можете запускать фоновые задания из основного процесса вашего приложения без необходимости создания отдельного сервиса.
Hangfire поддерживает широкий спектр фоновых задач: краткосрочные и долгосрочные, загружающие процессор или ввод-вывод, одноразовые и повторяющиеся.
Всего загрузок: 18,8 млн.
10. LazyCache
Кэширование — отличный способ повысить производительность приложения. LazyCache использует формат
GetOrAdd
для кэширования, когда вы запрашиваете элемент из кэша, предоставляя при этом функциональность для его добавления, если элемент отсутствует.LazyCache отлично подходит для кэширования вызовов базы данных, процедур построения сложных графов объектов и вызовов веб-сервисов, которые нужно временно сохранить для повышения производительности. Пакет позволяет кэшировать элементы в течение большего или меньшего времени, но по умолчанию он сохраняет кэшированные элементы до 20 минут.
Ключевые особенности LazyCache:
- Потокобезопасен и готов для использования в параллельном коде.
- Совместим с асинхронным кодом: доступна ленивая однократная оценка асинхронных делегатов с использованием метода
GetOrAddAsync()
.Всего загрузок: 7,3 млн.
Источник: https://www.syncfusion.com/blogs/post/10-best-c-nuget-packages-to-improve-your-productivity-in-2022.aspx/amp
👍16
День 1078.
Избегайте Ненужных Программных Абстракций. Начало
Разработчики ПО любят абстракции. Абстракции — ключ к эффективной разработке. Проблема возникает, когда абстракции вводятся преждевременно, т.е. до того, как они решают реальную нетеоретическую проблему. Добавление абстракций всегда происходит за счёт роста сложности и, если с ними переборщить, начинает замедлять скорость разработки и способность понимать кодовую базу.
Все проблемы в информатике можно решить с помощью ещё одного уровня абстракции… За исключением проблемы слишком большого количества уровней абстракции.
— Батлер Лэмпсон
Мы рассмотрим, как отказ от лишних абстракций может привести к гораздо более чистой кодовой базе со значительно сниженной сложностью, а также повышенной читаемостью и удобством сопровождения. Обсуждаемые вопросы основаны на таких принципах, как «Делай Проще, Тупица» (KISS) и «Вам Этого не Понадобится» (YAGNI), что означает использование абстракции только тогда, когда это даёт значительную и реальную пользу. Давайте рассмотрим некоторые конкретные случаи преждевременных абстракций, которые часто встречаются на практике.
1. Чрезмерная детализация обязанностей
Это может быть абстракция запроса к базе данных в выделенный класс репозитория, абстрагирование HTTP-вызова в сервисный класс или какая-то исключительно внутренняя логика, перемещённая в отдельный компонент.
Обычно это делается для следования принципу единственной ответственности. Если мы выделим каждую крошечную часть логики в отдельный класс, тогда все классы будут иметь сверхчёткие обязанности, и, следовательно, только одну причину для изменения. Проблема в том, что все эти маленькие части, как правило, всё ещё тесно связаны и сильно зависят друг от друга. Если какая-либо коммуникация между частями изменится, часто это будет иметь каскадный эффект, требующий изменений во многих частях. Таким образом, у каждой части может быть только одна причина для изменения, но это бессмысленно, если одно изменение часто требует внесения изменений во многие части.
Кроме того, часто нет реальных практических преимуществ в том, чтобы классы изменялись только по одной причине. На самом деле, внесение изменений в классы, которые выполняют несколько функций, часто предоставляет разработчику гораздо больше контекста, что значительно упрощает понимание изменений и их влияния на окружающий код.
Когда же разделять обязанности? Распространённый случай — когда логику нужно использовать более, чем в одном месте. Если один и тот же HTTP-вызов или запрос к базе требуется в нескольких местах, дублирование логики снижает удобство сопровождения. Тогда будет хорошей идеей перенести код в общий и многократно используемый компонент. Главное не делать этого до того, как это потребуется. Другой допустимый случай — когда логика очень сложна и негативно влияет на читаемость окружающего кода. Если часть логики занимает 300 строк, это как раз тот случай. Но выделение всего нескольких строк в отдельный класс, скорее всего, только ухудшит читаемость и затруднит навигацию по коду. Помните, что разделение обязанностей всегда усложняет структуру кода.
Продолжение следует…
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
Избегайте Ненужных Программных Абстракций. Начало
Разработчики ПО любят абстракции. Абстракции — ключ к эффективной разработке. Проблема возникает, когда абстракции вводятся преждевременно, т.е. до того, как они решают реальную нетеоретическую проблему. Добавление абстракций всегда происходит за счёт роста сложности и, если с ними переборщить, начинает замедлять скорость разработки и способность понимать кодовую базу.
Все проблемы в информатике можно решить с помощью ещё одного уровня абстракции… За исключением проблемы слишком большого количества уровней абстракции.
— Батлер Лэмпсон
Мы рассмотрим, как отказ от лишних абстракций может привести к гораздо более чистой кодовой базе со значительно сниженной сложностью, а также повышенной читаемостью и удобством сопровождения. Обсуждаемые вопросы основаны на таких принципах, как «Делай Проще, Тупица» (KISS) и «Вам Этого не Понадобится» (YAGNI), что означает использование абстракции только тогда, когда это даёт значительную и реальную пользу. Давайте рассмотрим некоторые конкретные случаи преждевременных абстракций, которые часто встречаются на практике.
1. Чрезмерная детализация обязанностей
Это может быть абстракция запроса к базе данных в выделенный класс репозитория, абстрагирование HTTP-вызова в сервисный класс или какая-то исключительно внутренняя логика, перемещённая в отдельный компонент.
Обычно это делается для следования принципу единственной ответственности. Если мы выделим каждую крошечную часть логики в отдельный класс, тогда все классы будут иметь сверхчёткие обязанности, и, следовательно, только одну причину для изменения. Проблема в том, что все эти маленькие части, как правило, всё ещё тесно связаны и сильно зависят друг от друга. Если какая-либо коммуникация между частями изменится, часто это будет иметь каскадный эффект, требующий изменений во многих частях. Таким образом, у каждой части может быть только одна причина для изменения, но это бессмысленно, если одно изменение часто требует внесения изменений во многие части.
Кроме того, часто нет реальных практических преимуществ в том, чтобы классы изменялись только по одной причине. На самом деле, внесение изменений в классы, которые выполняют несколько функций, часто предоставляет разработчику гораздо больше контекста, что значительно упрощает понимание изменений и их влияния на окружающий код.
Когда же разделять обязанности? Распространённый случай — когда логику нужно использовать более, чем в одном месте. Если один и тот же HTTP-вызов или запрос к базе требуется в нескольких местах, дублирование логики снижает удобство сопровождения. Тогда будет хорошей идеей перенести код в общий и многократно используемый компонент. Главное не делать этого до того, как это потребуется. Другой допустимый случай — когда логика очень сложна и негативно влияет на читаемость окружающего кода. Если часть логики занимает 300 строк, это как раз тот случай. Но выделение всего нескольких строк в отдельный класс, скорее всего, только ухудшит читаемость и затруднит навигацию по коду. Помните, что разделение обязанностей всегда усложняет структуру кода.
Продолжение следует…
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
👍8
День 1079.
Избегайте Ненужных Программных Абстракций. Продолжение
Начало
2. Паттерны проектирования, используемые без реальной пользы
Внедрение различных паттернов до того, как их преимущества действительно понадобятся, — ещё одна распространённая ловушка. Паттерны отлично подходят для решения конкретных проблем и при определённых обстоятельствах могут снизить общую сложность. Тем не менее, почти все они имеют недостаток в виде дополнительной структурной сложности и снижения связности кода.
Например, декоратор часто используется для добавления дополнительных функций поверх существующего компонента. Например, компонент, делающий HTTP-запрос, к которому мы хотим добавить механизм повтора. Не изменяя исходный компонент, мы можем добавить обёртку с логикой повтора, реализующую тот же интерфейс. Далее исходный компонент можно заменить декорированным непосредственно в коде или путём внедрения зависимостей.
Недостаток этого подхода в том, что мы снова теряем согласованность. Когда позже разработчик смотрит на исходный компонент или код, использующий компонент, не сразу ясно, что произойдёт, когда код будет выполнен, поскольку сверху добавляется больше логики «за кулисами». Бывали случаи, когда логика из декоратора добавлялась непосредственно в класс, а позже обнаруживалось, что класс уже декорируется этой логикой.
Также широко используются паттерны Команда и Издатель-Подписчик. Здесь класс вместо того, чтобы обрабатывать запрос напрямую, абстрагирует его в команду для обработки в другом месте. Это обеспечивает слабосвязанное и чёткое разделение между частью кода, которая получает и интерпретирует запросы, и частью, которая знает, как обрабатывать запросы. Есть обоснованные случаи, когда это необходимо, но всегда разумно задаться вопросом, не добавили ли мы лишний слой. Такой слой, помимо прочего, затрудняет отслеживание пути выполнения программы, поскольку издатель, согласно определению, не знает, где в итоге обрабатывается команда.
То же можно сказать практически о любом паттерне. Все они имеют недостатки, поэтому используйте их только тогда, когда преимущества перевешивают недостатки.
3. Преждевременная оптимизация производительности
Часто самое эффективное решение проблемы — самое чистое и простое. Но иногда это не совсем так. В таких случаях затраты на оптимизацию должны сопоставляться с реальной практической выгодой, которую мы ожидаем получить. Это затраты времени на анализ, внедрение и поддержку оптимизации, а также потенциальное снижение читаемости кода из-за использования более сложного, но эффективного подхода. Не жертвуйте читаемостью ради ненужной эффективности и помните, что затраты времени разработчика часто намного превышают потенциальную выгоду от экономии вычислительных ресурсов за счёт микрооптимизаций.
Преждевременная оптимизация — корень всех зол
— Дональд Кнут
Оптимизация также может быть выполнена на архитектурном уровне. Одним из примеров является паттерн CQRS. Он подразумевает две отдельные модели данных: для записи и для чтения. Это позволяет оптимизировать один блок для эффективного чтения, а другой — для эффективной записи, а также масштабировать блоки по отдельности, если ваше приложение интенсивнее читает данные, чем пишет, или наоборот.
Недостатком является тот факт, что необходимо создавать и поддерживать целую отдельную модель данных, что приводит к дополнительным накладным расходам на разработку. Если требуется производительность, этот компромисс может быть просто идеальным, но даже для приложений, используемых миллионами людей, редко бывает, чтобы дополнительная эффективность чтения или записи давала какую-либо измеримую выгоду. Более разумным подходом было бы использовать единую модель и создавать оптимизированные модели чтения только для нескольких отдельных случаев, когда точно известно, что простой подход не будет работать достаточно эффективно.
Окончание следует…
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
Избегайте Ненужных Программных Абстракций. Продолжение
Начало
2. Паттерны проектирования, используемые без реальной пользы
Внедрение различных паттернов до того, как их преимущества действительно понадобятся, — ещё одна распространённая ловушка. Паттерны отлично подходят для решения конкретных проблем и при определённых обстоятельствах могут снизить общую сложность. Тем не менее, почти все они имеют недостаток в виде дополнительной структурной сложности и снижения связности кода.
Например, декоратор часто используется для добавления дополнительных функций поверх существующего компонента. Например, компонент, делающий HTTP-запрос, к которому мы хотим добавить механизм повтора. Не изменяя исходный компонент, мы можем добавить обёртку с логикой повтора, реализующую тот же интерфейс. Далее исходный компонент можно заменить декорированным непосредственно в коде или путём внедрения зависимостей.
Недостаток этого подхода в том, что мы снова теряем согласованность. Когда позже разработчик смотрит на исходный компонент или код, использующий компонент, не сразу ясно, что произойдёт, когда код будет выполнен, поскольку сверху добавляется больше логики «за кулисами». Бывали случаи, когда логика из декоратора добавлялась непосредственно в класс, а позже обнаруживалось, что класс уже декорируется этой логикой.
Также широко используются паттерны Команда и Издатель-Подписчик. Здесь класс вместо того, чтобы обрабатывать запрос напрямую, абстрагирует его в команду для обработки в другом месте. Это обеспечивает слабосвязанное и чёткое разделение между частью кода, которая получает и интерпретирует запросы, и частью, которая знает, как обрабатывать запросы. Есть обоснованные случаи, когда это необходимо, но всегда разумно задаться вопросом, не добавили ли мы лишний слой. Такой слой, помимо прочего, затрудняет отслеживание пути выполнения программы, поскольку издатель, согласно определению, не знает, где в итоге обрабатывается команда.
То же можно сказать практически о любом паттерне. Все они имеют недостатки, поэтому используйте их только тогда, когда преимущества перевешивают недостатки.
3. Преждевременная оптимизация производительности
Часто самое эффективное решение проблемы — самое чистое и простое. Но иногда это не совсем так. В таких случаях затраты на оптимизацию должны сопоставляться с реальной практической выгодой, которую мы ожидаем получить. Это затраты времени на анализ, внедрение и поддержку оптимизации, а также потенциальное снижение читаемости кода из-за использования более сложного, но эффективного подхода. Не жертвуйте читаемостью ради ненужной эффективности и помните, что затраты времени разработчика часто намного превышают потенциальную выгоду от экономии вычислительных ресурсов за счёт микрооптимизаций.
Преждевременная оптимизация — корень всех зол
— Дональд Кнут
Оптимизация также может быть выполнена на архитектурном уровне. Одним из примеров является паттерн CQRS. Он подразумевает две отдельные модели данных: для записи и для чтения. Это позволяет оптимизировать один блок для эффективного чтения, а другой — для эффективной записи, а также масштабировать блоки по отдельности, если ваше приложение интенсивнее читает данные, чем пишет, или наоборот.
Недостатком является тот факт, что необходимо создавать и поддерживать целую отдельную модель данных, что приводит к дополнительным накладным расходам на разработку. Если требуется производительность, этот компромисс может быть просто идеальным, но даже для приложений, используемых миллионами людей, редко бывает, чтобы дополнительная эффективность чтения или записи давала какую-либо измеримую выгоду. Более разумным подходом было бы использовать единую модель и создавать оптимизированные модели чтения только для нескольких отдельных случаев, когда точно известно, что простой подход не будет работать достаточно эффективно.
Окончание следует…
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
👍1
День 1080.
Давненько не писал ничего от себя, а тут как раз и повод подвернулся. Поэтому напишу, что называется, по горячим следам, а окончание «Избегайте Ненужных Программных Абстракций» выйдет завтра.
Мы все люди и все мы совершаем ошибки. Ошибки программистов могут быть не такими критичными, как ошибки врачей или архитекторов. От наших ошибок не умирают люди и не рушатся здания (обычно, по крайней мере). Ошибки программистов чаще всего просто стоят компаниям определённой суммы денег.
Однако есть ещё один вид очень обидных ошибок. Когда выбираешь не тот путь решения. В результате тратишь кучу времени и сил, добиваешься результата, чувствуешь заряд бодрости и удовлетворения от работы, а потом оказывается, что все усилия были напрасны.
Как многие из подписчиков знают, я фулстек. И иногда вожусь с UI, что в принципе мне даже нравится. Знаю, что многие дотнетчики бэкендеры люто ненавидят JS и CSS, но разговор пойдёт об этом, так что потерпите. Обещаю, никакой жести не будет.
Нужно нам было создать систему оценок. И задумали мы для выставления оценки вместо использовавшегося раньше обычного скучного выпадающего списка, сделать красивый ползунок. Тащишь его слева направо, оценка меняется от 0 до 5. Никаких новомодных фреймворков мы не используем, у нас старый добрый набор HTML+CSS+jQuery. Собственно, и ползунок взяли из jQuery UI. Проблема обнаружилась на мобильных устройствах. Очевидное для каждого действие тыкнуть в ползунок и протащить его пальцем до нужной оценки отказывалось работать. То есть мышкой его можно было таскать, а вот пальцем (или в эмуляторе) – нет.
В итоге к концу для, после настройки основного функционала, погуглив проблему, нашли небольшой плагин для jQuery, который, насколько я понял, и служил костылём для предоставления функционала «Touch & Drag/Drop» в jQuery UI. На следующий день я открыл страницу проекта на GitHub (который, кстати, давно был заброшен), выполнил все нужные действия из 3х простых шагов, два из которых – скачать файл с кодом и подключить его на странице. И – вуаля! – получаю ошибку JS “
Заметил, что плагин основан на нескольких пакетах из сборки jQuery UI. Поискал
Промучившись с этим около часа, стал искать другие варианты. Нашёл. Есть же специально обученный HTML элемент
В общем, на всю эту настройку, проверку во всех доступных браузерах, включая IE (не спрашивайте!), переписывание уже готового кода разметки и JS под использование нового элемента и т.п. ушла бОльшая часть дня. Но к приходу коллеги вечером, всё было готово, и выглядело даже почти так же, как изначально одобренный вариант UI. Я молодец! Я решил проблему!
Коллега удивился, что плагин не заработал. Но ещё больше удивился я (хотя, «удивился» тут слишком мягкое слово), когда коллега написал, что у него плагин заработал сразу, и он откатит мои изменения. После пары минут шока я решил разобраться, в чём же дело. И довольно скоро выяснил, что эту самую функцию
Конечно, в глазах коллеги, я, наверное, выглядел полным идиотом, которому нельзя доверить даже элементарную операцию. Поэтому я, как мог, попытался объяснить, как же это меня так переклинило, и почему я потратил целый день впустую. Он вроде даже понял (по переписке трудно судить). Но если уж под какой случай и подходит определение эпик фейл, то это был он.
Давненько не писал ничего от себя, а тут как раз и повод подвернулся. Поэтому напишу, что называется, по горячим следам, а окончание «Избегайте Ненужных Программных Абстракций» выйдет завтра.
Мы все люди и все мы совершаем ошибки. Ошибки программистов могут быть не такими критичными, как ошибки врачей или архитекторов. От наших ошибок не умирают люди и не рушатся здания (обычно, по крайней мере). Ошибки программистов чаще всего просто стоят компаниям определённой суммы денег.
Однако есть ещё один вид очень обидных ошибок. Когда выбираешь не тот путь решения. В результате тратишь кучу времени и сил, добиваешься результата, чувствуешь заряд бодрости и удовлетворения от работы, а потом оказывается, что все усилия были напрасны.
Как многие из подписчиков знают, я фулстек. И иногда вожусь с UI, что в принципе мне даже нравится. Знаю, что многие дотнетчики бэкендеры люто ненавидят JS и CSS, но разговор пойдёт об этом, так что потерпите. Обещаю, никакой жести не будет.
Нужно нам было создать систему оценок. И задумали мы для выставления оценки вместо использовавшегося раньше обычного скучного выпадающего списка, сделать красивый ползунок. Тащишь его слева направо, оценка меняется от 0 до 5. Никаких новомодных фреймворков мы не используем, у нас старый добрый набор HTML+CSS+jQuery. Собственно, и ползунок взяли из jQuery UI. Проблема обнаружилась на мобильных устройствах. Очевидное для каждого действие тыкнуть в ползунок и протащить его пальцем до нужной оценки отказывалось работать. То есть мышкой его можно было таскать, а вот пальцем (или в эмуляторе) – нет.
В итоге к концу для, после настройки основного функционала, погуглив проблему, нашли небольшой плагин для jQuery, который, насколько я понял, и служил костылём для предоставления функционала «Touch & Drag/Drop» в jQuery UI. На следующий день я открыл страницу проекта на GitHub (который, кстати, давно был заброшен), выполнил все нужные действия из 3х простых шагов, два из которых – скачать файл с кодом и подключить его на странице. И – вуаля! – получаю ошибку JS “
draggable() is not a function
”.Заметил, что плагин основан на нескольких пакетах из сборки jQuery UI. Поискал
draggable
в нашей кастомной сборке, и её там не оказалось. Поэтому скачал новую сборку, где всё должно было быть. И ползунок заработал! Правда сломался остальной код JS, причём сразу в нескольких неожиданных местах.Промучившись с этим около часа, стал искать другие варианты. Нашёл. Есть же специально обученный HTML элемент
<input type=”range” />
Проблема в том, что он в разных браузерах выглядит совершенно по-разному. Но и это не беда. Всего какой-то сотней строчек CSS можно было заставить элемент выглядеть так, как тебе надо.В общем, на всю эту настройку, проверку во всех доступных браузерах, включая IE (не спрашивайте!), переписывание уже готового кода разметки и JS под использование нового элемента и т.п. ушла бОльшая часть дня. Но к приходу коллеги вечером, всё было готово, и выглядело даже почти так же, как изначально одобренный вариант UI. Я молодец! Я решил проблему!
Коллега удивился, что плагин не заработал. Но ещё больше удивился я (хотя, «удивился» тут слишком мягкое слово), когда коллега написал, что у него плагин заработал сразу, и он откатит мои изменения. После пары минут шока я решил разобраться, в чём же дело. И довольно скоро выяснил, что эту самую функцию
draggable
вообще не надо было вызывать! В шаге 3 на странице документации был пример кода, использующий эту функцию. Но элементы из jQuery UI работали и без неё. Только подключения плагина на страницу было достаточно! Причём всё прекрасно работало сразу и везде, включая эмулятор и реальный телефон. Дел было на 2 минуты в ленивом темпе!Конечно, в глазах коллеги, я, наверное, выглядел полным идиотом, которому нельзя доверить даже элементарную операцию. Поэтому я, как мог, попытался объяснить, как же это меня так переклинило, и почему я потратил целый день впустую. Он вроде даже понял (по переписке трудно судить). Но если уж под какой случай и подходит определение эпик фейл, то это был он.
👍12
День 1081.
Избегайте Ненужных Программных Абстракций. Окончание
Начало
Продолжение
4. Повсеместное внедрение слабой связанности
В слабо связанном коде каждая часть максимально независима от других частей. Поэтому изменения в одной части оказывают минимальное влияние на другие части и упрощают замену части кода. Хорошими примерами являются внешние библиотеки или модули, которые используются несколькими различными проектами.
Типичным способом достижения слабой связанности является следование принципам инверсии зависимостей и «Открыт-закрыт». На практике это часто делается путем абстрагирования классов за интерфейсами и зависимости других классов от этих интерфейсов, а не от конкретных реализаций.
Проблема возникает, когда интерпретация этих принципов приводит к повсеместному введению слабой связанности, даже между отдельными классами в рамках одного изолированного функционала. Изолированный функционал не требует слабой связанности и интерфейсов между элементами внутри него. Слабая связанность не бесплатна. Интерфейсы делают код менее связным и трудным для навигации, поскольку вы не знаете, какой конкретный код будет выполняться. Сначала нужно проверить, какие реализации интерфейса существуют, а затем, какая из них фактически используется во время выполнения. Кроме того, интерфейс — это ещё один файл, который необходимо добавить в проект и поддерживать в актуальном состоянии.
Интерфейсы решают множество проблем. Но их нужно вводить, когда они необходимы для решения реальной практической задачи, а не просто для достижения ненужной слабой связанности. Как правило, это происходит, когда вам нужно подменить реализацию, либо при создании внешних библиотек, используемых другими людьми, чтобы не давать доступа к коду самой библиотеки. Если же вы просто используете интерфейсы для создания имитаций при тестировании, можно имитировать конкретные классы, чтобы избежать лишнего кода.
5. Нагромождение мелких файлов
Можно пойти дальше и переместить некоторые классы в один файл. Если классы тесно связаны, взаимозависимы и часто меняются вместе, это может улучшить как удобство сопровождения, так и обеспечить легкий доступ к контексту при внесении изменений. Тем не менее, будьте готовы разделить классы, если файл значительно увеличится в размере.
Здесь важно, чтобы код, который изменяется вместе, располагался как можно ближе друг к другу. Чаще изменение затрагивает несколько классов, потому что они относятся к одному и тому же функционалу, а не потому, что они относятся к определённому типу.
Итого
Важно рассматривать рефакторинг как естественную часть внесения изменений. Если какая-то часть логики вдруг понадобится более чем в одном месте, самое время абстрагировать её в отдельный повторно используемый компонент. Если понадобится подменять реализации, самое время добавить интерфейс. Избегание преждевременных абстракций не означает, что абстракции никогда не будут вводиться, просто они вводятся по ходу, когда возникнет реальная необходимость.
Ключевым моментом является изменение вашего мышления. Каждый раз, когда вы думаете о том, чтобы ввести ещё одну абстракцию, спросите себя и коллег, действительно ли она обеспечит ту ценность, которую вы ищете, или её можно не вводить без каких-либо проблем. Всегда должны быть конкретные практические преимущества, когда добавляется новая сложность.
Теперь взгляните на свои проекты. У вас есть преждевременные абстракции, которые вы могли бы удалить? Хорошая кодовая база позволяет очень быстро и легко вносить простые изменения и осуществлять рефакторинг. Проверьте свои недавние пулл-реквесты и сравните размер изменений с тем, что достигнуто с их помощью.
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
Избегайте Ненужных Программных Абстракций. Окончание
Начало
Продолжение
4. Повсеместное внедрение слабой связанности
В слабо связанном коде каждая часть максимально независима от других частей. Поэтому изменения в одной части оказывают минимальное влияние на другие части и упрощают замену части кода. Хорошими примерами являются внешние библиотеки или модули, которые используются несколькими различными проектами.
Типичным способом достижения слабой связанности является следование принципам инверсии зависимостей и «Открыт-закрыт». На практике это часто делается путем абстрагирования классов за интерфейсами и зависимости других классов от этих интерфейсов, а не от конкретных реализаций.
Проблема возникает, когда интерпретация этих принципов приводит к повсеместному введению слабой связанности, даже между отдельными классами в рамках одного изолированного функционала. Изолированный функционал не требует слабой связанности и интерфейсов между элементами внутри него. Слабая связанность не бесплатна. Интерфейсы делают код менее связным и трудным для навигации, поскольку вы не знаете, какой конкретный код будет выполняться. Сначала нужно проверить, какие реализации интерфейса существуют, а затем, какая из них фактически используется во время выполнения. Кроме того, интерфейс — это ещё один файл, который необходимо добавить в проект и поддерживать в актуальном состоянии.
Интерфейсы решают множество проблем. Но их нужно вводить, когда они необходимы для решения реальной практической задачи, а не просто для достижения ненужной слабой связанности. Как правило, это происходит, когда вам нужно подменить реализацию, либо при создании внешних библиотек, используемых другими людьми, чтобы не давать доступа к коду самой библиотеки. Если же вы просто используете интерфейсы для создания имитаций при тестировании, можно имитировать конкретные классы, чтобы избежать лишнего кода.
5. Нагромождение мелких файлов
Можно пойти дальше и переместить некоторые классы в один файл. Если классы тесно связаны, взаимозависимы и часто меняются вместе, это может улучшить как удобство сопровождения, так и обеспечить легкий доступ к контексту при внесении изменений. Тем не менее, будьте готовы разделить классы, если файл значительно увеличится в размере.
Здесь важно, чтобы код, который изменяется вместе, располагался как можно ближе друг к другу. Чаще изменение затрагивает несколько классов, потому что они относятся к одному и тому же функционалу, а не потому, что они относятся к определённому типу.
Итого
Важно рассматривать рефакторинг как естественную часть внесения изменений. Если какая-то часть логики вдруг понадобится более чем в одном месте, самое время абстрагировать её в отдельный повторно используемый компонент. Если понадобится подменять реализации, самое время добавить интерфейс. Избегание преждевременных абстракций не означает, что абстракции никогда не будут вводиться, просто они вводятся по ходу, когда возникнет реальная необходимость.
Ключевым моментом является изменение вашего мышления. Каждый раз, когда вы думаете о том, чтобы ввести ещё одну абстракцию, спросите себя и коллег, действительно ли она обеспечит ту ценность, которую вы ищете, или её можно не вводить без каких-либо проблем. Всегда должны быть конкретные практические преимущества, когда добавляется новая сложность.
Теперь взгляните на свои проекты. У вас есть преждевременные абстракции, которые вы могли бы удалить? Хорошая кодовая база позволяет очень быстро и легко вносить простые изменения и осуществлять рефакторинг. Проверьте свои недавние пулл-реквесты и сравните размер изменений с тем, что достигнуто с их помощью.
Источник: https://www.daveabrock.com/2021/12/08/do-this-not-that-the-net-6-edition/
👍8
День 1082.
Сегодня порекомендую вам вебинар от Конрада Кокосы, который прошёл в понедельник. На канале я уже упоминал тест по работе с памятью в .NET (.NET Memory Quiz), a также коротко разбирал ответы:
- 1-4
- 5-8
- 9-12
- 13-16
- 17-20
- 21-24
- 25-27
- 28-32
В стриме Конрад подробно разбирает 3 наиболее интересных (с его точки зрения) вопроса из теста:
26. Очень хороший разбор на примере того, как использовать Benchmark.NET и профилировщик памяти (в данном случае – JetBrains dotMemory), чтобы определить, сколько памяти выделяется и на что.
25. Разбор того, как работает интернирование строк в .NET (не путать с интерполяцией).
28. Короткий (относительно других вопросов) разбор метода
https://youtu.be/U74m6eVYSGs
Сегодня порекомендую вам вебинар от Конрада Кокосы, который прошёл в понедельник. На канале я уже упоминал тест по работе с памятью в .NET (.NET Memory Quiz), a также коротко разбирал ответы:
- 1-4
- 5-8
- 9-12
- 13-16
- 17-20
- 21-24
- 25-27
- 28-32
В стриме Конрад подробно разбирает 3 наиболее интересных (с его точки зрения) вопроса из теста:
26. Очень хороший разбор на примере того, как использовать Benchmark.NET и профилировщик памяти (в данном случае – JetBrains dotMemory), чтобы определить, сколько памяти выделяется и на что.
25. Разбор того, как работает интернирование строк в .NET (не путать с интерполяцией).
28. Короткий (относительно других вопросов) разбор метода
Dispose
.https://youtu.be/U74m6eVYSGs
👍2
Как вы относитесь к отображению лигатур (≠, ≤, ≥, →) в коде?
Anonymous Poll
26%
С ними код понятнее
40%
Затрудняют чтение/редактирование кода
33%
Без разницы
👍1
День 1083. #ЧтоНовенького
Специфичные для платформы API
Новый .NET является кроссплатформенным. Вы можете запустить своё приложение на разных платформах. Однако использование специфичных для конкретных платформ API означает, что с ними нужно быть осторожным. В .NET появились новые API и анализатор совместимости платформ для решения проблем кроссплатформенности. .NET 5 представил новые статические методы в классе
-
Полный список находится здесь
Кроме того, представлены новые атрибуты:
-
-
См. картинку ниже.
Считается, что API без этих атрибутов работают на всех платформах. Анализатор совместимости с платформой включён в список анализаторов качества кода Roslyn. Начиная с .NET 5, он входит в пакет SDK для .NET и включен по умолчанию для .NET 5 и более поздних версий.
Подробнее о том, как анализатор определяет зависимость от платформы
В .NET 6 представлена концепция включения платформ, когда одна платформа может быть подмножеством другой платформы. Новые настраиваемые атрибуты для зависимых от платформы API:
-
Источник: https://twitter.com/okyrylchuk/status/1481011910195752960
Специфичные для платформы API
Новый .NET является кроссплатформенным. Вы можете запустить своё приложение на разных платформах. Однако использование специфичных для конкретных платформ API означает, что с ними нужно быть осторожным. В .NET появились новые API и анализатор совместимости платформ для решения проблем кроссплатформенности. .NET 5 представил новые статические методы в классе
OperatingSystem
:-
IsAndroid
- IsBrowser
- IsFreeBSD
- IsIOS
- IsLinux
- IsWindows
- и другие.Полный список находится здесь
Кроме того, представлены новые атрибуты:
-
[SupportedOSPlatform]
для обозначения, что API специфично для какой-то платформы.-
[UnsupportedOSPlatform]
для обозначения, что API не подходит для какой-то платформы.См. картинку ниже.
Считается, что API без этих атрибутов работают на всех платформах. Анализатор совместимости с платформой включён в список анализаторов качества кода Roslyn. Начиная с .NET 5, он входит в пакет SDK для .NET и включен по умолчанию для .NET 5 и более поздних версий.
Подробнее о том, как анализатор определяет зависимость от платформы
В .NET 6 представлена концепция включения платформ, когда одна платформа может быть подмножеством другой платформы. Новые настраиваемые атрибуты для зависимых от платформы API:
-
[SupportedOSPlatformGuard]
- [UnsupportedOSPlatformGuard]
Например, платформа iOS является подмножеством платформы MacCatalyst. Поэтому для [SupportedOSPlatformGuard("MacCatalyst")]
метод OperatingSystem.IsIOS()
, помеченный этим атрибутом проверит и iOS, и MacCatalyst.Источник: https://twitter.com/okyrylchuk/status/1481011910195752960
👍3
День 1084. #Курсы
Сегодня порекомендую вам пару курсов от Microsoft Learn Live.
1. Создание микросервисов в .NET и ASP.NET (Create microservices with .NET and ASP.NET).
Курс посвящён созданию независимо развертываемых, масштабируемых и отказоустойчивых сервисов на платформе .NET.
Особенность его в том, что по каждой теме курса еженедельно, по вторникам, проходит стрим в прямом эфире с экспертами. Стрим по первой теме «Введение в микросервисы на .NET» уже прошёл 11го января, но его можно посмотреть в записи на ютубе.
Предстоящие стримы:
- 18.01.2022. 12:00–13:30 Мск. Создание и развёртывание облачного микросервиса в ASP.NET Core.
- 25.01.2022. Реализация отказоустойчивости.
- 01.02.2022. Инструментарий для облачного микросервиса.
- 08.02.2022. Реализация флагов функций в микросервисном приложении.
- 15.02.2022. Использование хранилищ данных в микросервисном приложении.
- 22.02.2022. Понимание API-шлюзов в микросервисном приложении.
- 01.03.2022. Развёртывание микросервисного приложения с помощью GitHub Actions.
Помимо стримов, для каждой темы доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.
2. Обучение на сертификацию по Azure Cosmos DB (Azure Cosmos DB Certification Study Hall).
Узнайте, как разрабатывать, внедрять и отслеживать облачные приложения для хранения данных и управления ими, и получите сертификат Azure Cosmos DB Developer Specialty.
Этот курс проходит по средам и также начался 12 января. Первый модуль «Основы Azure Cosmos DB SQL API» можно посмотреть тут.
Предстоящие модули:
- 19.01.2022. 23:00–01:30 Мск. Планирование и реализация Azure Cosmos DB SQL API.
- 26.01.2022. Перемещение данных из и в базу.
- 02.02.2022. Использование SDK.
- 09.02.2022. Конфигурация SDK.
- 16.02.2022. Выполнение операций в базе данных.
- 23.02.2022. Выполнение кросс-документных транзакций.
- 02.03.2022. Обработка больших объемов данных.
- 09.03.2022. Запросы к БД.
- 16.03.2022. Создание сложных запросов.
- 23.03.2022. Определение индексов.
- 30.03.2022. Настройка индексов.
- 06.04.2022. Обработка сообщений об изменениях в SDK.
- 13.04.2022. Обработка событий в Azure Functions.
- 20.04.2022. Поиск по БД с помощью Azure Cognitive Search.
Как и для предыдущего курса, доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.
Сегодня порекомендую вам пару курсов от Microsoft Learn Live.
1. Создание микросервисов в .NET и ASP.NET (Create microservices with .NET and ASP.NET).
Курс посвящён созданию независимо развертываемых, масштабируемых и отказоустойчивых сервисов на платформе .NET.
Особенность его в том, что по каждой теме курса еженедельно, по вторникам, проходит стрим в прямом эфире с экспертами. Стрим по первой теме «Введение в микросервисы на .NET» уже прошёл 11го января, но его можно посмотреть в записи на ютубе.
Предстоящие стримы:
- 18.01.2022. 12:00–13:30 Мск. Создание и развёртывание облачного микросервиса в ASP.NET Core.
- 25.01.2022. Реализация отказоустойчивости.
- 01.02.2022. Инструментарий для облачного микросервиса.
- 08.02.2022. Реализация флагов функций в микросервисном приложении.
- 15.02.2022. Использование хранилищ данных в микросервисном приложении.
- 22.02.2022. Понимание API-шлюзов в микросервисном приложении.
- 01.03.2022. Развёртывание микросервисного приложения с помощью GitHub Actions.
Помимо стримов, для каждой темы доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.
2. Обучение на сертификацию по Azure Cosmos DB (Azure Cosmos DB Certification Study Hall).
Узнайте, как разрабатывать, внедрять и отслеживать облачные приложения для хранения данных и управления ими, и получите сертификат Azure Cosmos DB Developer Specialty.
Этот курс проходит по средам и также начался 12 января. Первый модуль «Основы Azure Cosmos DB SQL API» можно посмотреть тут.
Предстоящие модули:
- 19.01.2022. 23:00–01:30 Мск. Планирование и реализация Azure Cosmos DB SQL API.
- 26.01.2022. Перемещение данных из и в базу.
- 02.02.2022. Использование SDK.
- 09.02.2022. Конфигурация SDK.
- 16.02.2022. Выполнение операций в базе данных.
- 23.02.2022. Выполнение кросс-документных транзакций.
- 02.03.2022. Обработка больших объемов данных.
- 09.03.2022. Запросы к БД.
- 16.03.2022. Создание сложных запросов.
- 23.03.2022. Определение индексов.
- 30.03.2022. Настройка индексов.
- 06.04.2022. Обработка сообщений об изменениях в SDK.
- 13.04.2022. Обработка событий в Azure Functions.
- 20.04.2022. Поиск по БД с помощью Azure Cognitive Search.
Как и для предыдущего курса, доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.
👍5
День 1085. #ЗаметкиНаПолях
Использовать ли Одинаковые Идентификаторы в Разных Микросервисах
Существует распространённый и довольно интересный вопрос о том, как управлять идентификаторами в архитектуре микросервисов. Этот вопрос лучше всего описать на примере, поэтому, допустим, у нас есть два ограниченных контекста,
В обоих этих микросервисах есть одна и та же сущность клиента (
Отсюда вопрос: «Должен ли сервис поддержки использовать тот же ID, что и сервис продаж, или нужно назначать собственный ID своей версии клиента?»
Если вы используете UUID(GUID), вы можете легко назначить раздельные
Нет. Используйте один и тот же идентификатор во всей вашей системе, если этот идентификатор создаётся одним из ваших микросервисов.
Когда вы смотрите на идентификаторы с этой точки зрения, становится ясно, что не имеет смысла назначать раздельные идентификаторы клиентов в нижестоящих микросервисах, так же как не имеет смысла назначать разные имена или адреса электронной почты. Пока только один микросервис контролирует время существования определённого объекта (то есть может создавать и удалять его), у вас всегда будет только один идентификатор в вашей системе, даже если эта система состоит из нескольких микросервисов.
А что, если идентификатор поступает из внешней системы?
Предположим, клиенты могут зарегистрироваться в вашей системе через свои социальные профили, такие как Facebook. В этом случае Facebook предоставит вашей системе собственный идентификатор.
Стоит ли использовать этот идентификатор Facebook как есть?
Нет, вам нужно создать собственный идентификатор клиента и сохранить идентификатор Facebook как отдельное (необязательное) поле. Разница здесь в том, что у вас нет контроля над идентификаторами, которые предоставляет вам Facebook. Facebook потенциально может изменить эти идентификаторы и испортить вашу систему.
Контракт между вашими микросервисами намного прочнее, чем между вашей системой и Facebook. Вы можете (и должны) поддерживать обратную совместимость между микросервисами в вашей системе. Нет никакой гарантии такой совместимости между вашей системой и Facebook.
Источник: https://enterprisecraftsmanship.com/
Автор оригинала: Владимир Хориков
Использовать ли Одинаковые Идентификаторы в Разных Микросервисах
Существует распространённый и довольно интересный вопрос о том, как управлять идентификаторами в архитектуре микросервисов. Этот вопрос лучше всего описать на примере, поэтому, допустим, у нас есть два ограниченных контекста,
Sales
и Support
, которые работают в своих соответствующих микросервисах (см. картинку ниже).В обоих этих микросервисах есть одна и та же сущность клиента (
Customer
). Предположим также, что сервис продаж является вышестоящим по отношению к сервису поддержки. То есть, когда клиент создаётся в продажах, он передаётся в поддержку, которая создаёт собственное представление этого клиента.Отсюда вопрос: «Должен ли сервис поддержки использовать тот же ID, что и сервис продаж, или нужно назначать собственный ID своей версии клиента?»
Если вы используете UUID(GUID), вы можете легко назначить раздельные
ID
клиента в обоих сервисах, и идентификаторы в двух ограниченных контекстах не будут пересекаться. Но хорошо ли это?Нет. Используйте один и тот же идентификатор во всей вашей системе, если этот идентификатор создаётся одним из ваших микросервисов.
ID
— это одно из свойств, представляющих клиента. Идентификатор контролируется микросервисом, который создаёт экземпляры клиента. В нашем примере это сервис продаж. Новые клиенты создаются, когда ваша компания что-то им продаёт. Поэтому Sales
— главный микросервис для идентификации всех клиентов. Только сервис Sales
может назначать ID
. Нижестоящие сервисы должны реплицировать его в свои базы данных, аналогично тому, как они реплицируют свойства Name
или Email
.Когда вы смотрите на идентификаторы с этой точки зрения, становится ясно, что не имеет смысла назначать раздельные идентификаторы клиентов в нижестоящих микросервисах, так же как не имеет смысла назначать разные имена или адреса электронной почты. Пока только один микросервис контролирует время существования определённого объекта (то есть может создавать и удалять его), у вас всегда будет только один идентификатор в вашей системе, даже если эта система состоит из нескольких микросервисов.
А что, если идентификатор поступает из внешней системы?
Предположим, клиенты могут зарегистрироваться в вашей системе через свои социальные профили, такие как Facebook. В этом случае Facebook предоставит вашей системе собственный идентификатор.
Стоит ли использовать этот идентификатор Facebook как есть?
Нет, вам нужно создать собственный идентификатор клиента и сохранить идентификатор Facebook как отдельное (необязательное) поле. Разница здесь в том, что у вас нет контроля над идентификаторами, которые предоставляет вам Facebook. Facebook потенциально может изменить эти идентификаторы и испортить вашу систему.
Контракт между вашими микросервисами намного прочнее, чем между вашей системой и Facebook. Вы можете (и должны) поддерживать обратную совместимость между микросервисами в вашей системе. Нет никакой гарантии такой совместимости между вашей системой и Facebook.
Источник: https://enterprisecraftsmanship.com/
Автор оригинала: Владимир Хориков
👍9👎1
Возник вопрос в переводе термина Entity Framework Core (EF Core) - это ...
*см. первый комментарий
*см. первый комментарий
Anonymous Poll
79%
фреймворк (он)
21%
библиотека (она)
👍4