День двести сорок восьмой. #BestPractices
Разработка для Расширяемости
Базовые классы для реализации абстракций
Строго говоря, класс становится базовым, когда другой класс наследует от него. Однако здесь подразумеваем, что базовый класс - это класс, предназначенный главным образом для обеспечения обобщённой абстракции или для повторного использования некоторой стандартной функциональности другими классами через наследование. Базовые классы обычно находятся в середине иерархий наследования, между абстракцией в корне иерархии и несколькими пользовательскими реализациями внизу.
Они служат помощниками для реализации абстракций. Например, одной из абстракций .Net Framework для упорядоченных коллекций элементов является интерфейс
Базовые классы обычно не подходят для использования в качестве абстракций, потому что они, как правило, содержат слишком много реализованной функциональности. Например, базовый класс
Как уже говорилось ранее, базовые классы могут оказать неоценимую помощь пользователям, которым необходимо реализовывать абстракции, но в то же время они могут стать серьезной проблемой. Они увеличивают глубину иерархии наследования и таким образом усложняют структуру. Поэтому базовые классы следует использовать только в том случае, если они обеспечивают значительную ценность для пользователей фреймворка. Их следует избегать, если они обеспечивают ценность только для разработчиков фреймворка, и в этом случае настоятельно рекомендуется делегировать внутреннюю реализацию вместо наследования от базового класса.
⚠️ РАССМОТРИТЕ возможность сделать базовые классы абстрактными, даже если они не содержат абстрактных членов. Это ясно сообщает пользователям, что класс предназначен исключительно для наследования.
⚠️ РАССМОТРИТЕ возможность размещения базовых классов в пространстве имен отдельном от основных вариантов использования. По определению базовые классы предназначены для сценариев расширяемости, что является продвинутым уровнем использования фреймворка, и поэтому не интересно большинству пользователей.
❌ ИЗБЕГАЙТЕ именования базовых классов с суффиксом «Base», если этот класс предназначен для использования в общедоступных API.
Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
Разработка для Расширяемости
Базовые классы для реализации абстракций
Строго говоря, класс становится базовым, когда другой класс наследует от него. Однако здесь подразумеваем, что базовый класс - это класс, предназначенный главным образом для обеспечения обобщённой абстракции или для повторного использования некоторой стандартной функциональности другими классами через наследование. Базовые классы обычно находятся в середине иерархий наследования, между абстракцией в корне иерархии и несколькими пользовательскими реализациями внизу.
Они служат помощниками для реализации абстракций. Например, одной из абстракций .Net Framework для упорядоченных коллекций элементов является интерфейс
IList<T>
. Реализация IList<T>
не тривиальна, и поэтому фреймворк предоставляет несколько базовых классов, таких как Collection<T>
и KeyedCollection<TKey, TItem>
, которые служат помощниками для реализации пользовательских коллекций.Базовые классы обычно не подходят для использования в качестве абстракций, потому что они, как правило, содержат слишком много реализованной функциональности. Например, базовый класс
Collection<T>
содержит множество реализаций, например, необобщённого интерфейса IList
(для лучшей интеграции с необобщёнными коллекциями), а также функционал управления коллекцией элементов, хранящихся в памяти в одном из полей класса.Как уже говорилось ранее, базовые классы могут оказать неоценимую помощь пользователям, которым необходимо реализовывать абстракции, но в то же время они могут стать серьезной проблемой. Они увеличивают глубину иерархии наследования и таким образом усложняют структуру. Поэтому базовые классы следует использовать только в том случае, если они обеспечивают значительную ценность для пользователей фреймворка. Их следует избегать, если они обеспечивают ценность только для разработчиков фреймворка, и в этом случае настоятельно рекомендуется делегировать внутреннюю реализацию вместо наследования от базового класса.
⚠️ РАССМОТРИТЕ возможность сделать базовые классы абстрактными, даже если они не содержат абстрактных членов. Это ясно сообщает пользователям, что класс предназначен исключительно для наследования.
⚠️ РАССМОТРИТЕ возможность размещения базовых классов в пространстве имен отдельном от основных вариантов использования. По определению базовые классы предназначены для сценариев расширяемости, что является продвинутым уровнем использования фреймворка, и поэтому не интересно большинству пользователей.
❌ ИЗБЕГАЙТЕ именования базовых классов с суффиксом «Base», если этот класс предназначен для использования в общедоступных API.
Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести сорок девятый. #BestPractices
Разработка для Расширяемости
Запечатывание
Одной из особенностей объектно-ориентированных фреймворков является то, что пользователи могут расширять и изменять их способами, не предусмотренными разработчиками фреймворка. Это и сила, и опасность расширяемого дизайна. Поэтому, когда вы разрабатываете свой фреймворк, очень важно тщательно продумать возможности расширения, где это необходимо, и ограничить возможности расширения, где оно опасно.
Мощный механизм, препятствующий расширению – это запечатывание (sealing). Вы можете запечатать класс или отдельные его члены. Запечатывание класса препятствует наследованию от класса. Запечатывание члена предотвращает переопределение пользователями отдельного члена.
❌ ИЗБЕГАЙТЕ запечатывания класса, не имея на то веских причин. Запечатывание класса только по причине того, что вы не можете придумать сценарии его расширяемости, - это плохая идея. Пользователи фреймворков любят наследовать от классов по разным неочевидным причинам, например, добавляя удобные члены, такие как пользовательские конструкторы, новые методы или перегрузки методов. Например,
Классы по умолчанию незапечатаны в большинстве языков программирования, и это также рекомендованное поведение по умолчанию для большинства классов фреймворков. Расширяемость, предоставляемая незапечатанными типами, высоко ценится пользователями фреймворка и она довольно недорогая в обслуживании из-за относительно низких затрат на тестирование.
Хорошие причины для запечатывания класса:
- Класс является статическим классом (см. https://t.me/NetDeveloperDiary/241)
- Класс хранит чувствительный к безопасности код в унаследованных защищенных членах.
- Класс наследует много виртуальных членов, и стоимость запечатывания которых по одиночке превышает преимущества от оставления класса незапечатанным.
- Класс является атрибутом, который требуется очень быстро находить во время выполнения. Запечатанные атрибуты имеют немного более высокую производительность, чем незапечатанные.
❌ ИЗБЕГАЙТЕ объявления защищённых или виртуальных членов в запечатанных типах. По определению от запечатанных типов нельзя унаследовать. Это означает, что защищённые члены запечатанных типов не могут быть вызваны, а виртуальные методы запечатанных типов не могут быть переопределены.
⚠️ РАССМОТРИТЕ возможность запечатать члены класса, которые вы переопределяете. Проблемы, которые могут возникнуть в результате введения виртуальных членов (см. https://t.me/NetDeveloperDiary/286), также применимы к переопределениям, хотя и в несколько меньшей степени. Запечатывание переопределения защищает вас от этих проблем, начиная с этой точки в иерархии наследования.
Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
Разработка для Расширяемости
Запечатывание
Одной из особенностей объектно-ориентированных фреймворков является то, что пользователи могут расширять и изменять их способами, не предусмотренными разработчиками фреймворка. Это и сила, и опасность расширяемого дизайна. Поэтому, когда вы разрабатываете свой фреймворк, очень важно тщательно продумать возможности расширения, где это необходимо, и ограничить возможности расширения, где оно опасно.
Мощный механизм, препятствующий расширению – это запечатывание (sealing). Вы можете запечатать класс или отдельные его члены. Запечатывание класса препятствует наследованию от класса. Запечатывание члена предотвращает переопределение пользователями отдельного члена.
❌ ИЗБЕГАЙТЕ запечатывания класса, не имея на то веских причин. Запечатывание класса только по причине того, что вы не можете придумать сценарии его расширяемости, - это плохая идея. Пользователи фреймворков любят наследовать от классов по разным неочевидным причинам, например, добавляя удобные члены, такие как пользовательские конструкторы, новые методы или перегрузки методов. Например,
System.Messaging.MessageQueue
является незапечатанным и, таким образом, позволяет пользователям создавать настраиваемые очереди, имеющие путь по умолчанию, или добавлять настраиваемые методы, которые упрощают API в определенных сценариях.Классы по умолчанию незапечатаны в большинстве языков программирования, и это также рекомендованное поведение по умолчанию для большинства классов фреймворков. Расширяемость, предоставляемая незапечатанными типами, высоко ценится пользователями фреймворка и она довольно недорогая в обслуживании из-за относительно низких затрат на тестирование.
Хорошие причины для запечатывания класса:
- Класс является статическим классом (см. https://t.me/NetDeveloperDiary/241)
- Класс хранит чувствительный к безопасности код в унаследованных защищенных членах.
- Класс наследует много виртуальных членов, и стоимость запечатывания которых по одиночке превышает преимущества от оставления класса незапечатанным.
- Класс является атрибутом, который требуется очень быстро находить во время выполнения. Запечатанные атрибуты имеют немного более высокую производительность, чем незапечатанные.
❌ ИЗБЕГАЙТЕ объявления защищённых или виртуальных членов в запечатанных типах. По определению от запечатанных типов нельзя унаследовать. Это означает, что защищённые члены запечатанных типов не могут быть вызваны, а виртуальные методы запечатанных типов не могут быть переопределены.
⚠️ РАССМОТРИТЕ возможность запечатать члены класса, которые вы переопределяете. Проблемы, которые могут возникнуть в результате введения виртуальных членов (см. https://t.me/NetDeveloperDiary/286), также применимы к переопределениям, хотя и в несколько меньшей степени. Запечатывание переопределения защищает вас от этих проблем, начиная с этой точки в иерархии наследования.
Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести пятидесятый. #ЗаметкиНаПолях
Использование методов расширения в инициализаторах коллекций
Для использования инициализатора коллекции необходимы два условия:
- Тип должен реализовывать
- Должен быть подходящий метод
Варианты использования:
1. Создание сигнатур метода Add общего назначения. Например, инициализация коллекцией:
Использование методов расширения в инициализаторах коллекций
Для использования инициализатора коллекции необходимы два условия:
- Тип должен реализовывать
IEnumerable
. Это раздражающее ограничение. Иногда приходится реализовывать IEnumerable
исключительно для того, чтобы использовать тип в инициализаторах коллекций.- Должен быть подходящий метод
Add
для каждого элемента в инициализаторе коллекции. Предполагается, что любые элементы, которые не находятся в фигурных скобках, соответствуют вызовам метода Add
с одним аргументом. Когда требуется несколько аргументов, они должны быть в фигурных скобках. Например:List<string> strings = new List<string> { 10, "hello", { 20, 3 } };Эквивалентно:
List<string> strings = new List<string>();Далее для определения, что значит каждый вызов, применяется стандартное разрешение перегрузки методов. С обычным
strings.Add(10);
strings.Add("hello");
strings.Add(20, 3);
List<T>
этот код не скомпилируется. Но можно добавить метод расширения:public static void Add(Этот метод добавляет значение первого аргумента столько раз, сколько указано во втором аргументе. Таким образом список примет вид:
this List<string> list, int value, int count = 1)
{
list.AddRange(Enumerable.Repeat(value.ToString(), count));
}
"10", "hello", "20", "20", "20"
.Варианты использования:
1. Создание сигнатур метода Add общего назначения. Например, инициализация коллекцией:
public static void Add<T>(this List<T> list, IEnumerable<T> collection)Применение (контакты человека инициализируются коллекцией людей из Москвы):
{
list.AddRange(collection);
}
Person jon = new Person2. Создание специализированных сигнатур метода Add. Например, добавление в словарь объектов (людей) с ключом в виде одного из свойств (имя):
{
Name = "Jon",
Contacts = { allContacts.Where(c => c.Town == "Moscow") }
};
public static void Add(Применение:
this Dictionary<string, Person> dictionary, Person person)
{
dictionary.Add(person.Name, person);
}
var dictionary = new Dictionary<string, Person>Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 10.
{
{ new Person { Name = "Jon", … } },
{ new Person { Name = "Holly", … } }
};
День двести пятьдесят первый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
1. Действуйте с осторожностью
Что бы вы ни предпринимали, действуйте с осторожностью и учитывайте последствия.
- Анон
Неважно, насколько приятно выглядит график разработки в начале итерации, вы не сможете избежать работы под давлением от случая к случаю. Если вам приходится выбирать между «сделать правильно» и «сделать быстро», часто бывает соблазнительно «сделать быстро», подразумевая, что вы вернётесь и исправите это позже. Когда вы обещаете это себе, своей команде или своему клиенту, вы действительно намерены это исправить. Но слишком часто следующая итерация приносит новые проблемы, и вы сосредотачиваетесь на них. Этот вид отложенной работы известен как технический долг, и это ваш враг. В частности, Мартин Фаулер называет это преднамеренным техническим долгом, и его не следует путать с непреднамеренным.
Технический долг подобен займу: вы получаете выгоду от него в краткосрочной перспективе, но должны платить по нему проценты, пока он не будет полностью погашен. Костыли в коде затрудняют добавление функций или рефакторинг. Они являются питательной средой для ошибок и недостаточно полных тестов. Чем дольше вы их оставляете, тем хуже становится. К тому времени, когда вы приступите к выполнению первоначального исправления, может появиться целый набор не совсем правильных вариантов дизайна, наслоенных поверх исходной проблемы, что сделает код намного сложнее для рефакторинга и исправления. На самом деле, зачастую вы возвращаетесь к исправлению изначальной проблемы только тогда, когда всё становится настолько плохо, что вам не остаётся ничего другого. А к этому моменту это уже настолько трудно сделать, что вы в реальности не можете позволить себе потратить столько времени или пойти на такой риск.
Есть моменты, когда вы должны взять на себя технические долги, чтобы уложиться в срок или реализовать небольшой фрагмент функциональности. Постарайтесь не оказываться в таком положении, но, если ситуация этого требует, действуйте. Но (и это значительное «но») вы должны отслеживать технические долги и быстро их погашать, или дела быстро пойдут вниз. Как только вы примете решение пойти на компромисс, создайте задачу в своей системе отслеживания ошибок, чтобы не забыть это исправить.
Если вы планируете погашение долга на следующей итерации, стоимость будет минимальной. Оставляя долг неоплаченным, вы попадаете на проценты, и за ними нужно следить, чтобы знать цену исправления. Это позволит надлежащим образом расставлять приоритеты задач. Выбор того, как рассчитывать и отслеживать стоимость исправления технического долга, будет зависеть от конкретного проекта, но отслеживать её необходимо.
Погашайте технический долг как можно быстрее.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
97 Вещей, Которые Должен Знать Каждый Программист
1. Действуйте с осторожностью
Что бы вы ни предпринимали, действуйте с осторожностью и учитывайте последствия.
- Анон
Неважно, насколько приятно выглядит график разработки в начале итерации, вы не сможете избежать работы под давлением от случая к случаю. Если вам приходится выбирать между «сделать правильно» и «сделать быстро», часто бывает соблазнительно «сделать быстро», подразумевая, что вы вернётесь и исправите это позже. Когда вы обещаете это себе, своей команде или своему клиенту, вы действительно намерены это исправить. Но слишком часто следующая итерация приносит новые проблемы, и вы сосредотачиваетесь на них. Этот вид отложенной работы известен как технический долг, и это ваш враг. В частности, Мартин Фаулер называет это преднамеренным техническим долгом, и его не следует путать с непреднамеренным.
Технический долг подобен займу: вы получаете выгоду от него в краткосрочной перспективе, но должны платить по нему проценты, пока он не будет полностью погашен. Костыли в коде затрудняют добавление функций или рефакторинг. Они являются питательной средой для ошибок и недостаточно полных тестов. Чем дольше вы их оставляете, тем хуже становится. К тому времени, когда вы приступите к выполнению первоначального исправления, может появиться целый набор не совсем правильных вариантов дизайна, наслоенных поверх исходной проблемы, что сделает код намного сложнее для рефакторинга и исправления. На самом деле, зачастую вы возвращаетесь к исправлению изначальной проблемы только тогда, когда всё становится настолько плохо, что вам не остаётся ничего другого. А к этому моменту это уже настолько трудно сделать, что вы в реальности не можете позволить себе потратить столько времени или пойти на такой риск.
Есть моменты, когда вы должны взять на себя технические долги, чтобы уложиться в срок или реализовать небольшой фрагмент функциональности. Постарайтесь не оказываться в таком положении, но, если ситуация этого требует, действуйте. Но (и это значительное «но») вы должны отслеживать технические долги и быстро их погашать, или дела быстро пойдут вниз. Как только вы примете решение пойти на компромисс, создайте задачу в своей системе отслеживания ошибок, чтобы не забыть это исправить.
Если вы планируете погашение долга на следующей итерации, стоимость будет минимальной. Оставляя долг неоплаченным, вы попадаете на проценты, и за ними нужно следить, чтобы знать цену исправления. Это позволит надлежащим образом расставлять приоритеты задач. Выбор того, как рассчитывать и отслеживать стоимость исправления технического долга, будет зависеть от конкретного проекта, но отслеживать её необходимо.
Погашайте технический долг как можно быстрее.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
День двести пятьдесят второй. #юмор
Ещё немного тонкого английского юмора)))
Ещё немного тонкого английского юмора)))
День двести пятьдесят третий. #ЗаметкиНаПолях
Фильтры исключений
Представьте, что вы выполняете веб-операцию и знаете, что сервер, к которому вы подключаетесь, иногда недоступен. Если вам не удаётся подключиться к нему, то у вас есть запасной вариант, однако любой другой вид сбоя должен привести к обычному выбросу исключения. До C #6 нужно было поймать исключение и выбросить его повторно, если статус отличался от нужного:
1. Перехват одного исключения несколько раз
В прошлом всегда было ошибкой указывать один и тот же тип исключения в нескольких блоках
2. Повторные попытки
Облачные вычисления становятся все более распространенными, и мы, как правило, всё больше узнаём об операциях, которые могут привести к сбою, и о задумываемся о том, как обрабатывать сбои в нашем коде. При удаленных операциях - например, вызовах удалённых веб-служб и баз данных - иногда возникают временные сбои, и можно безопасно повторить попытку. Вот как это можно реализовать, используя фильтры исключений:
Иногда полезно записывать исключение в лог, даже если оно будет перехвачено на более верхних уровнях. Вы можете использовать для этого фильтры исключений, чтобы не нарушать поток выполнения. Всё, что вам нужно, это фильтр исключений, который вызывает метод для записи в лог и возвращает
Фильтры исключений
Представьте, что вы выполняете веб-операцию и знаете, что сервер, к которому вы подключаетесь, иногда недоступен. Если вам не удаётся подключиться к нему, то у вас есть запасной вариант, однако любой другой вид сбоя должен привести к обычному выбросу исключения. До C #6 нужно было поймать исключение и выбросить его повторно, если статус отличался от нужного:
try {…}Используя фильтры исключений, если вы не хотите обрабатывать исключение, можно не перехватывать его, а отфильтровать сразу в блоке
catch (WebException e)
{
if (e.Status != WebExceptionStatus.ConnectFailure)
{
throw;
}
…
}
catch
: try {…}Варианты использования:
catch (WebException e)
when (e.Status == WebExceptionStatus.ConnectFailure) {…}
1. Перехват одного исключения несколько раз
В прошлом всегда было ошибкой указывать один и тот же тип исключения в нескольких блоках
catch
для одного и того же блока try
. Это не имело смысла, потому что второй блок никогда не достигался. С фильтрами исключений ситуация изменилась. Предположим, вы извлекаете веб-контент на основе URL-адреса, предоставленного пользователем. Возможно, вы захотите обработать сбой соединения одним способом, сбой разрешения домена другим способом и не обрабатывать другие типы сбоев:try {…}Если вы хотите обработать все другие исключения WebException на том же уровне, можно добавить общий блок
catch (WebException e)
when (e.Status == WebExceptionStatus.ConnectFailure) {…}
catch (WebException e)
when (e.Status == WebExceptionStatus.NameResolutionFailure) {…}
catch (WebException e) {…}
без фильтра последним пунктом.2. Повторные попытки
Облачные вычисления становятся все более распространенными, и мы, как правило, всё больше узнаём об операциях, которые могут привести к сбою, и о задумываемся о том, как обрабатывать сбои в нашем коде. При удаленных операциях - например, вызовах удалённых веб-служб и баз данных - иногда возникают временные сбои, и можно безопасно повторить попытку. Вот как это можно реализовать, используя фильтры исключений:
static T Retry<T>(Func<T> operation, int attempts)3. Запись в лог, как побочный эффект
{
while (true)
{
try
{
attempts--;
return operation();
}
catch (Exception e) when (attempts > 0)
{
Console.WriteLine($"Неудача: {e}");
Console.WriteLine($"Осталось попыток: {attempts}");
Thread.Sleep(5000);
}
}
}
Иногда полезно записывать исключение в лог, даже если оно будет перехвачено на более верхних уровнях. Вы можете использовать для этого фильтры исключений, чтобы не нарушать поток выполнения. Всё, что вам нужно, это фильтр исключений, который вызывает метод для записи в лог и возвращает
false
, чтобы указать, что вы не хотите перехватывать это исключение:try {…}Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 10.
catch (Exception e) when (Log(e)) {…}
…
static bool Log(Exception e)
{
Console.WriteLine($"{DateTime.UtcNow}: {e.GetType()} {e.Message}");
return false;
}
День двести пятьдесят четвёртый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
2. Применяйте принципы функционального программирования
Не так давно в сообществе программистов стал возрождаться интерес к функциональному программированию. Одна из причин – то, что
функциональная парадигма упрощает использование многоядерности, к которой движется современная ИТ индустрия. Овладение парадигмой функционального программирования может значительно улучшить качество кода, написанного в других контекстах. Если вы глубоко понимаете и применяете функциональную парадигму, в вашем коде будут преобладать чистые (детерминированные) функции.
Чистые функции - очень желательное свойство кода. Оно подразумевает, что функции дают одинаковые результаты при одинаковых входных данных, независимо от того, где и когда они вызываются. Таким образом, выполнение функции мало зависит (а в идеале, совсем не зависит) от изменяемого состояния приложения.
Основная причина дефектов в императивном коде связана с изменяемыми переменными. Наверняка каждый из вас не раз искал причину, почему значение какой-либо переменной не такое, как ожидалось в определённой ситуации. Управление областью видимости может помочь смягчить эти коварные дефекты или, по крайней мере, радикально сузить район поиска источника проблем, но их истинным виновником на самом деле может быть использование конструкций, в которых используется чрезмерная изменяемость.
И мы, к сожалению, не видим в этом помощи от индустрии. ООП неявно продвигает подобный подход к дизайну, предлагая примеры систем объектов, вовсю вызывающих методы-мутаторы друг друга. Однако при помощи разработки через тестирование можно избавиться от ненужной изменяемости.
Конечным результатом является дизайн, который обычно имеет лучшее распределение ответственности с более многочисленными, но меньшими по объёму функциями, которые действуют на передаваемые в них аргументы, а не вызывают методы, изменяющие состояние объекта. Ошибок будет меньше, кроме того, их часто будет проще отлаживать, поскольку легче определить, где в цепочку функций попало неверное значение, чем каким-либо иным образом обнаружить конкретный контекст, который приводит к ошибочному присваиванию. Это обеспечивает гораздо более высокую чистоту функций. И, безусловно, ничто не сможет так научить вас этому, как изучение функционального языка программирования, где эта модель вычислений является нормой.
Конечно, такой подход не может быть оптимальным во всех ситуациях. Например, в объектно-ориентированных системах этот стиль обычно приносит гораздо лучшие результаты при разработке бизнес-логики, чем при разработке пользовательского интерфейса.
Овладейте парадигмой функционального программирования, чтобы вы могли разумно применять полученные уроки в других областях. Ваши объектно-ориентированные системы получат огромную пользу от чистых функций и могут стать намного ближе к своим функциональным аналогам, чем вы думаете.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
97 Вещей, Которые Должен Знать Каждый Программист
2. Применяйте принципы функционального программирования
Не так давно в сообществе программистов стал возрождаться интерес к функциональному программированию. Одна из причин – то, что
функциональная парадигма упрощает использование многоядерности, к которой движется современная ИТ индустрия. Овладение парадигмой функционального программирования может значительно улучшить качество кода, написанного в других контекстах. Если вы глубоко понимаете и применяете функциональную парадигму, в вашем коде будут преобладать чистые (детерминированные) функции.
Чистые функции - очень желательное свойство кода. Оно подразумевает, что функции дают одинаковые результаты при одинаковых входных данных, независимо от того, где и когда они вызываются. Таким образом, выполнение функции мало зависит (а в идеале, совсем не зависит) от изменяемого состояния приложения.
Основная причина дефектов в императивном коде связана с изменяемыми переменными. Наверняка каждый из вас не раз искал причину, почему значение какой-либо переменной не такое, как ожидалось в определённой ситуации. Управление областью видимости может помочь смягчить эти коварные дефекты или, по крайней мере, радикально сузить район поиска источника проблем, но их истинным виновником на самом деле может быть использование конструкций, в которых используется чрезмерная изменяемость.
И мы, к сожалению, не видим в этом помощи от индустрии. ООП неявно продвигает подобный подход к дизайну, предлагая примеры систем объектов, вовсю вызывающих методы-мутаторы друг друга. Однако при помощи разработки через тестирование можно избавиться от ненужной изменяемости.
Конечным результатом является дизайн, который обычно имеет лучшее распределение ответственности с более многочисленными, но меньшими по объёму функциями, которые действуют на передаваемые в них аргументы, а не вызывают методы, изменяющие состояние объекта. Ошибок будет меньше, кроме того, их часто будет проще отлаживать, поскольку легче определить, где в цепочку функций попало неверное значение, чем каким-либо иным образом обнаружить конкретный контекст, который приводит к ошибочному присваиванию. Это обеспечивает гораздо более высокую чистоту функций. И, безусловно, ничто не сможет так научить вас этому, как изучение функционального языка программирования, где эта модель вычислений является нормой.
Конечно, такой подход не может быть оптимальным во всех ситуациях. Например, в объектно-ориентированных системах этот стиль обычно приносит гораздо лучшие результаты при разработке бизнес-логики, чем при разработке пользовательского интерфейса.
Овладейте парадигмой функционального программирования, чтобы вы могли разумно применять полученные уроки в других областях. Ваши объектно-ориентированные системы получат огромную пользу от чистых функций и могут стать намного ближе к своим функциональным аналогам, чем вы думаете.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
День двести пятьдесят пятый #Оффтоп
Давал уже ссылку на этот канал, но не могу не поделиться их новым видео. Ребята просто шикарны. ExtremeCode про парадигмы ООП https://www.youtube.com/watch?v=BHNt1fcg8iw Смотреть пришлось два раза, потому что с первого раза так ржал, что мало что понял. Осторожно, 18+
Давал уже ссылку на этот канал, но не могу не поделиться их новым видео. Ребята просто шикарны. ExtremeCode про парадигмы ООП https://www.youtube.com/watch?v=BHNt1fcg8iw Смотреть пришлось два раза, потому что с первого раза так ржал, что мало что понял. Осторожно, 18+
YouTube
САМЫЕ ЧАСТЫЕ ЗАБЛУЖДЕНИЯ ООП
В этом видео мы узнаем точно число парадигм ООП, а так же разберем каждую из существующих:
- Инкапсуляцию
- Наследование
- Полиморфизм
- Абстракцию
- Посылку сообщений
- Повторное использование кода
Реализация Message Passing на JavaScript:
https:…
- Инкапсуляцию
- Наследование
- Полиморфизм
- Абстракцию
- Посылку сообщений
- Повторное использование кода
Реализация Message Passing на JavaScript:
https:…
День двести пятьдесят шестой.
Сертификат Microsoft. Шаг 3
Прошло много времени с тех пор, как в последний раз писал что-то по этой теме. Итак, запланировал сдачу на ноябрь. Точную дату пока не выбрал. Экзамен можно сдать онлайн из дома. Нужна рабочая веб-камера и стабильное интернет-соединение. Там ещё куча ограничений по поводу того, что на столе ничего не должно быть, в комнате никого не должно быть, кроме вас, не должно быть никаких посторонних звуков. Всё время экзамена за вами будут наблюдать, если наблюдающему покажется, что вам кто-то подсказывает, вы куда-то подсматриваете или происходит ещё что-то подозрительное, экзамен прекращается без оценки, деньги не возвращаются. В общем, всё строго, подробнее опишу ближе к делу.
Пока для подготовки нашёл второе издание книги «Exam Ref 70-483 Programming in C#» https://www.microsoftpressstore.com/store/exam-ref-70-483-programming-in-c-sharp-9781509306985 Всё-таки, первое было от 2013 года, и много воды с тех пор утекло. Начал изучать. Что могу сказать. Эту книгу стоит рассматривать исключительно как материал для подготовки к этому конкретному экзамену. Более того, как руководство для повторения изученного материала. Потому что, во-первых, книга организована в порядке тем, перечисленных на странице экзамена, поэтому сначала идёт тема многопоточности (потоки, задачи, исключения), а потом – внезапно - управление логикой программы (циклы, условные операторы и т.п.), то есть сначала продвинутые программные конструкции, а потом самые базовые. Во-вторых, описано очень сжато и порой сумбурно, а также довольно много опечаток как в тексте, так и в коде. То есть научиться по этой книге не получится (хотя по некоторым темам встречаются рассуждения и подробные объяснения на несколько страниц). А вот для «вспомнить всё» вполне сойдёт.
Сертификат Microsoft. Шаг 3
Прошло много времени с тех пор, как в последний раз писал что-то по этой теме. Итак, запланировал сдачу на ноябрь. Точную дату пока не выбрал. Экзамен можно сдать онлайн из дома. Нужна рабочая веб-камера и стабильное интернет-соединение. Там ещё куча ограничений по поводу того, что на столе ничего не должно быть, в комнате никого не должно быть, кроме вас, не должно быть никаких посторонних звуков. Всё время экзамена за вами будут наблюдать, если наблюдающему покажется, что вам кто-то подсказывает, вы куда-то подсматриваете или происходит ещё что-то подозрительное, экзамен прекращается без оценки, деньги не возвращаются. В общем, всё строго, подробнее опишу ближе к делу.
Пока для подготовки нашёл второе издание книги «Exam Ref 70-483 Programming in C#» https://www.microsoftpressstore.com/store/exam-ref-70-483-programming-in-c-sharp-9781509306985 Всё-таки, первое было от 2013 года, и много воды с тех пор утекло. Начал изучать. Что могу сказать. Эту книгу стоит рассматривать исключительно как материал для подготовки к этому конкретному экзамену. Более того, как руководство для повторения изученного материала. Потому что, во-первых, книга организована в порядке тем, перечисленных на странице экзамена, поэтому сначала идёт тема многопоточности (потоки, задачи, исключения), а потом – внезапно - управление логикой программы (циклы, условные операторы и т.п.), то есть сначала продвинутые программные конструкции, а потом самые базовые. Во-вторых, описано очень сжато и порой сумбурно, а также довольно много опечаток как в тексте, так и в коде. То есть научиться по этой книге не получится (хотя по некоторым темам встречаются рассуждения и подробные объяснения на несколько страниц). А вот для «вспомнить всё» вполне сойдёт.
День двести пятьдесят седьмой. #ЗаметкиНаПолях
Использование потокобезопасных коллекций. Начало
Потокобезопасные элементы работают правильно, когда используются из нескольких потоков (задач) одновременно. Стандартные коллекции .NET (
Предоставляет потокобезопасную коллекцию, обслуживаемую по принципу "первым поступил — первым обслужен" (FIFO). Метод
Синхронизация обеспечивается внутри
Третий метод,
ConcurrentStack
Класс
Можно перечислить элементы очереди или стека (программа может использовать конструкцию
Свойство
ConcurrentBag
Представляет потокобезопасный контейнер неупорядоченной коллекции объектов. Метод
Контейнеры полезны для хранения элементов, когда порядок не имеет значения, и в отличие от наборов, контейнеры поддерживают дублирование элементов.
Источники:
- Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
- https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=netframework-4.8
Использование потокобезопасных коллекций. Начало
Потокобезопасные элементы работают правильно, когда используются из нескольких потоков (задач) одновременно. Стандартные коллекции .NET (
List
, Queue
или Dictionary
) не являются потокобезопасными. Библиотеки .NET предоставляют классы потокобезопасных коллекций, которые можно использовать при создании многозадачных приложений: BlockingCollection<Т>
, ConcurrentQueue<Т>
, ConcurrentStack<Т>
, ConcurrentBag<Т>
, ConcurrentDictionary<TKey, TValue>
ConcurrentQueueПредоставляет потокобезопасную коллекцию, обслуживаемую по принципу "первым поступил — первым обслужен" (FIFO). Метод
Enqueue
добавляет элементы в очередь, а метод TryDequeue
удаляет их. Обратите внимание, что, хотя метод Enqueue
гарантированно сработает (очереди могут иметь бесконечную длину), метод TryDequeue
вернет false
в случае сбоя при извлечении из очереди. Синхронизация обеспечивается внутри
ConcurrentQueue<T>
. Если два потока вызывают TryDequeue
в один и тот же момент, ни одна из операций не блокируется. При обнаружении конфликта между двумя потоками, один поток должен попытаться снова получить следующий элемент.TryDequeue
пытается удалить элемент из очереди. Это происходит атомарно по отношению к другим операциям в очереди. Если очередь была заполнена элементами a
, b
и c
, и два потока одновременно пытаются удалить из очереди элемент, один поток удалит из очереди a, а другой поток удалит из очереди b
. Оба вызова TryDequeue
вернут true
, потому что они оба смогли удалить элемент из очереди. Если каждый поток попытается извлечь ещё по одному элементу, один из потоков удалит из очереди c
и вернёт true
, тогда как другой поток найдёт очередь пустой и вернет false
.Третий метод,
TryPeek
, позволяет программе проверить элемент в начале очереди, не извлекая его. Заметьте, что даже если метод TryPeek
возвратит элемент, последующий вызов метода TryDequeue
в том же потоке, для извлечения этого элемента из очереди, может завершиться неудачей, если элемент будет извлечён в другом потоке.ConcurrentStack
Класс
ConcurrentStack
обеспечивает поддержку потокобезопасных стеков Элементы обслуживаются по принципу «первым пришёл – последним обслужен» (LIFO). Метод Push
добавляет элементы в стек, а метод TryPop
извлекает их. Существуют также методы PushRange
и TryPopRange
, которые можно использовать для добавления или извлечения нескольких элементов.Можно перечислить элементы очереди или стека (программа может использовать конструкцию
foreach
для работы с каждым элементом в очереди). В начале перечисления параллельная очередь предоставит моментальный снимок содержимого.Свойство
Count
возвращает количество элементов в коллекции, а свойство IsEmpty
сообщает, является ли коллекция пустой (его предпочтительнее использовать, вместо сравнения Count
с 0
). Но вследствие многопоточного использования, значения этих свойств могут сразу же терять актуальность из-за действий в других потоках.ConcurrentBag
Представляет потокобезопасный контейнер неупорядоченной коллекции объектов. Метод
Add
помещают элементы в коллекцию, а метод TryTake
извлекает их. Существует также метод TryPeek
, но он менее полезен в ConcurrentBag
, поскольку возможно, что последующий метод TryTake
вернет другой элемент.Контейнеры полезны для хранения элементов, когда порядок не имеет значения, и в отличие от наборов, контейнеры поддерживают дублирование элементов.
ConcurrentBag<T>
оптимизирован для сценариев, в которых один и тот же поток будет как производить, так и потреблять данные, хранящиеся в контейнере. ConcurrentBag<T>
может принимать null
в качестве допустимого значения для ссылочных типов. Источники:
- Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
- https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=netframework-4.8
👍1
День двести пятьдесят восьмой. #ЗаметкиНаПолях
Использование потокобезопасных коллекций. Продолжение
ConcurrentDictionary
Словарь предоставляет хранилище данных, проиндексированное по ключу.
Метод
Метод
Кроме того, хотя все методы
1) Поток A вызывает
2) Поток B одновременно вызывает
3) Продолжается выполнение делегата из потока A, он достигает блокировки, но теперь видит, что элемент уже существует.
4) Поток A выполняет «
Поэтому нет гарантии, что данные, возвращаемые
Источники:
- Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
- https://docs.microsoft.com/dotnet/standard/collections/thread-safe/how-to-add-and-remove-items
Использование потокобезопасных коллекций. Продолжение
ConcurrentDictionary
Словарь предоставляет хранилище данных, проиндексированное по ключу.
ConcurrentDictionary<TKey, TValue>
может использоваться несколькими параллельными задачами. Действия над словарем выполняются атомарно. Другими словами, действие по обновлению элемента в словаре не может быть прервано действием из другой задачи. ConcurrentDictionary
предоставляет некоторые дополнительные методы, которые необходимы, когда словарь используется несколькими задачами.ConcurrentDictionary<string, int> ages = new ConcurrentDictionary<string, int>();Метод
if (ages.TryAdd("Иван", 21))
Console.WriteLine("Иван успешно добавлен.");
Console.WriteLine("Возраст Ивана: {0}", ages["Иван"]);
// Пытаемся изменить возраст с 21 на 22
if (ages.TryUpdate("Иван", 22, 21))
Console.WriteLine("Возраст успешно обновлён");
Console.WriteLine("Новый возраст Ивана: {0}", ages["Иван"]);
// Увеличиваем возраст, используя фабричный метод
Console.WriteLine("Возраст Ивана обновлён до: {0}",
ages.AddOrUpdate("Иван", 1,
(name,age) => age = age+1));
Console.WriteLine("Новый возраст Ивана: {0}", ages["Иван"]);
TryAdd
пытается добавить новый элемент. Если элемент уже существует, метод TryAdd
возвращает false
. Метод TryUpdate
поставляется с ключом обновляемого элемента, новым значением, которое должно быть сохранено в элементе, и значением, которое должно быть перезаписано. В приведенном выше примере возраст элемента «Иван
», будет обновлен до 22
, только если существующее значение равно 21
. Это позволяет программе обновлять элемент, только если он имеет ожидаемое значение.Метод
AddOrUpdate
позволяет предоставить поведение, которое будет выполнять обновление заданного элемента или добавлять новый элемент, если он ещё не существует. В приведённом выше примере добавление элемента «Иван
» не будет выполнено при вызове AddOrUpdate
, поскольку элемент уже существует. Вместо этого выполняется пользовательский делегат, передаваемый в качестве третьего аргумента, который увеличивает возраст элемента «Иван
» на 1
.Метод
GetOrAdd
позволяет получить существующее значение для указанного ключа или, если ключ не существует, вы добавить пару ключ/значение. Методу GetOrAdd
также можно передать делегат для задания значения.ConcurrentDictionary
предназначен для многопоточных сценариев. Вам не нужно использовать блокировки в своем коде для добавления или удаления элементов из коллекции. Однако один поток всегда может извлечь значение, а другой поток - немедленно обновить коллекцию, задав этому же ключу новое значение.Кроме того, хотя все методы
ConcurrentDictionary
являются потокобезопасными, не все методы являются атомарными, в частности GetOrAdd
и AddOrUpdate
. Пользовательский делегат, который передается этим методам, вызывается вне внутренней блокировки словаря (это делается для того, чтобы неизвестный код не блокировал все потоки). Следовательно, возможна следующая последовательность событий:1) Поток A вызывает
GetOrAdd
, не находит элемента и создаёт новый элемент для добавления, вызывая делегат.2) Поток B одновременно вызывает
GetOrAdd
, вызывается его делегат, и он достигает внутренней блокировки словаря перед потоком A, поэтому его новая пара ключ-значение добавляется в словарь.3) Продолжается выполнение делегата из потока A, он достигает блокировки, но теперь видит, что элемент уже существует.
4) Поток A выполняет «
Get
» и возвращает данные, которые были ранее добавлены потоком B.Поэтому нет гарантии, что данные, возвращаемые
GetOrAdd
, являются теми же данными, которые создаются в делегате потока. Подобная последовательность событий может происходить и при вызове AddOrUpdate
.Источники:
- Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
- https://docs.microsoft.com/dotnet/standard/collections/thread-safe/how-to-add-and-remove-items
День двести пятьдесят девятый. #ЗаметкиНаПолях
Использование потокобезопасных коллекций. Окончание
BlockingCollection<Т>
С точки зрения разработки лучше всего реализовывать паттерн «производитель/потребитель», то есть рассматривать задачи в многопоточном приложении как либо производителя, либо потребителя данных. Задача, которая и производит, и потребляет данные, уязвима для ситуаций «взаимной блокировки». Если задача A ожидает чего-то, создаваемого задачей B, а задача B ожидает чего-то, создаваемого задачей A, и ни одна из задач не может быть запущена.
Класс
Следующий код создаёт поток, который пытается добавить 5 элементов в коллекцию
Пример вывода:
Источник: Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
Использование потокобезопасных коллекций. Окончание
BlockingCollection<Т>
С точки зрения разработки лучше всего реализовывать паттерн «производитель/потребитель», то есть рассматривать задачи в многопоточном приложении как либо производителя, либо потребителя данных. Задача, которая и производит, и потребляет данные, уязвима для ситуаций «взаимной блокировки». Если задача A ожидает чего-то, создаваемого задачей B, а задача B ожидает чего-то, создаваемого задачей A, и ни одна из задач не может быть запущена.
Класс
BlockingCollection<T>
обеспечивает потокобезопасное средство добавления и извлечения элементов из хранилища данных. Он называется блокирующей коллекцией, потому что действие Take блокирует задачу, если нет элементов, которые необходимо извлечь. Попытки добавить элементы в заполненную коллекцию также блокируются. Кроме того, можно установить верхний предел размера коллекции. Следующий код создаёт поток, который пытается добавить 5 элементов в коллекцию
BlockingCollection
из 3 элементов. После добавления 3-го элемента этот поток блокируется. Программа также создаёт поток, который извлекает элементы из коллекции. Как только поток чтения начинает работать и извлекает некоторые элементы из коллекции, поток записи может продолжиться:BlockingCollection<int> data = new BlockingCollection<int>(3);
Task.Run(() =>
{
for(int i=1;i<=5;i++)
{
data.Add(i);
Console.WriteLine("Число {0} добавлено.", i);
}
data.CompleteAdding();
});
Console.ReadKey();
Console.WriteLine("Чтение элементов");
Task.Run(() =>
{
while (!data.IsCompleted)
{
try
{
int v = data.Take();
Console.WriteLine("Число {0} прочитано.", v);
}
catch (InvalidOperationException) { }
}
});
Console.ReadKey();
Пример вывода:
Число 1 добавленоЗадача добавления вызывает метод
Число 2 добавлено
Число 3 добавлено
Чтение элементов
Число 1 прочитано
Число 2 прочитано
Число 3 прочитано
Число 4 добавлено
Число 5 добавлено
Число 4 прочитано
Число 5 прочитано
CompleteAdding()
, после добавления последнего элемента. Это предотвращает добавление новых элементов в коллекцию. Извлекающая задача использует свойство IsCompleted
, чтобы определить, когда прекратить получать элементы. Свойство IsCompleted
возвращает true, когда коллекция пуста и вызван CompleteAdding
. Операция извлечения выполняется внутри конструкции try
-catch
, т.к. метод Take
может выбросить исключение, если между проверкой IsCompleted
и вызовом Take добавляющая задача вызовет CompleteAdding
. То есть получающая задача попытается выполнить извлечение из коллекции, которая была помечена как завершённая.BlockingCollection
также предоставляет методы TryAdd
и TryTake
, которые используются для попытки выполнения действия. Они возвращает true, если действие выполнено успешно. Кроме того, им можно передать максимальное время ожидания завершения действия и токен отмены. Класс BlockingCollection
выступает в качестве оболочки для других потокобезопасных классов коллекций, включая ConcurrentQueue
(используется по умолчанию), ConcurrentStack
и ConcurrentBag
.Источник: Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 1.
This media is not supported in your browser
VIEW IN TELEGRAM
День двести шестидесятый. #ЧтоНовенького
Вертикальная панель вкладок
В Microsoft объявили о выпуске Visual Studio 2019 version 16.4 Preview 2.
Отображение вкладок в вертикальной панели было одной из самых востребованных функций у сообщества разработчиков. Реализация вертикальной панели вкладок, начиная с Visual Studio 2019 v16.4 Preview 2, является одним из многих шагов, позволяющих значительно улучшить управление документами. Выше приведён краткий пример того, как выглядит новая функция.
Источник: https://devblogs.microsoft.com/visualstudio/fall-sports-pumpkin-spice-and-visual-studio-2019-v16-4-preview-2/
Вертикальная панель вкладок
В Microsoft объявили о выпуске Visual Studio 2019 version 16.4 Preview 2.
Отображение вкладок в вертикальной панели было одной из самых востребованных функций у сообщества разработчиков. Реализация вертикальной панели вкладок, начиная с Visual Studio 2019 v16.4 Preview 2, является одним из многих шагов, позволяющих значительно улучшить управление документами. Выше приведён краткий пример того, как выглядит новая функция.
Источник: https://devblogs.microsoft.com/visualstudio/fall-sports-pumpkin-spice-and-visual-studio-2019-v16-4-preview-2/
День двести шестьдесят первый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
3. Спросите себя: «Что бы сделал пользователь?» (Вы не пользователь)
Мы все склонны полагать, что другие люди думают так же, как мы. Но это не так. Психологи называют это ложным консенсусом. Когда люди думают или действуют не так, как мы, мы думаем (пусть подсознательно), что с ними что-то не так.
Это предубеждение объясняет, почему программистам так сложно поставить себя на место пользователя. Пользователи не думают, как программисты. Для начала они проводят гораздо меньше времени за компьютерами. Они не знают и не заботятся о том, как работает компьютер. Это означает, что они не могут использовать ни одну из техник решения проблем, знакомых программистам. Они не распознают шаблоны и подсказки, которые программисты используют для работы с интерфейсом.
Лучший способ узнать, что думает пользователь, - понаблюдать. Попросите пользователя выполнить задачу, используя программный продукт, аналогичный тому, который вы разрабатываете. Убедитесь, что задача реальная: «Сложите столбец чисел» - нормально; «Рассчитайте свои расходы за последний месяц» - отлично. Избегайте слишком специфических задач, таких как «Можете ли вы выбрать эти ячейки электронной таблицы и ввести формулу СУММ() ниже?» - в этом вопросе заложена подсказка. Попросите пользователя рассказать о своем прогрессе. Не перебивайте. Не пытайтесь помочь. Продолжайте спрашивать себя: «Почему он это делает?» или «Почему она этого не делает?»
Первое, что вы заметите, это то, что пользователи делают основные вещи схожим образом. Они пытаются выполнять задачи в одном и том же порядке и делают одни и те же ошибки в одних и тех же местах. Вы должны проектировать вокруг этого основного поведения. Это отличается от совещаний разработчиков, когда все слушают, как кто-то говорит: «А что, если пользователь захочет ...?» Наблюдение за пользователями устраняет эту путаницу.
Вы увидите, что происходит, когда пользователи застревают. Когда вы застряли, вы оглядываетесь вокруг. Когда пользователи застревают, они сужают фокус. Им становится все труднее видеть решения в других местах на экране. Это одна из причин, почему текст справки - плохое решение для плохого дизайна пользовательского интерфейса. Если вам нужно разместить инструкции для пользователя, обязательно поместите их рядом с проблемными областями. Узкий фокус внимания пользователя - это то, почему подсказки более полезны, чем отдельные страницы справки.
Пользователи, как правило, путаются. Они найдут способ, который будет работать, и будут придерживаться его, независимо от того, насколько он запутан. Лучше предоставить один действительно очевидный способ работы, чем два или три коротких пути.
Вы также обнаружите, что существует разрыв между тем, что пользователи говорят, что они хотят, и тем, что они реально делают. В этом и проблема, так как обычный способ сбора требований пользователей - это опрос. Вот почему лучший способ определить требования - это наблюдать за пользователями. За час наблюдения за пользователями вы получите больше информации, чем за день выспрашивания чего они хотят.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
97 Вещей, Которые Должен Знать Каждый Программист
3. Спросите себя: «Что бы сделал пользователь?» (Вы не пользователь)
Мы все склонны полагать, что другие люди думают так же, как мы. Но это не так. Психологи называют это ложным консенсусом. Когда люди думают или действуют не так, как мы, мы думаем (пусть подсознательно), что с ними что-то не так.
Это предубеждение объясняет, почему программистам так сложно поставить себя на место пользователя. Пользователи не думают, как программисты. Для начала они проводят гораздо меньше времени за компьютерами. Они не знают и не заботятся о том, как работает компьютер. Это означает, что они не могут использовать ни одну из техник решения проблем, знакомых программистам. Они не распознают шаблоны и подсказки, которые программисты используют для работы с интерфейсом.
Лучший способ узнать, что думает пользователь, - понаблюдать. Попросите пользователя выполнить задачу, используя программный продукт, аналогичный тому, который вы разрабатываете. Убедитесь, что задача реальная: «Сложите столбец чисел» - нормально; «Рассчитайте свои расходы за последний месяц» - отлично. Избегайте слишком специфических задач, таких как «Можете ли вы выбрать эти ячейки электронной таблицы и ввести формулу СУММ() ниже?» - в этом вопросе заложена подсказка. Попросите пользователя рассказать о своем прогрессе. Не перебивайте. Не пытайтесь помочь. Продолжайте спрашивать себя: «Почему он это делает?» или «Почему она этого не делает?»
Первое, что вы заметите, это то, что пользователи делают основные вещи схожим образом. Они пытаются выполнять задачи в одном и том же порядке и делают одни и те же ошибки в одних и тех же местах. Вы должны проектировать вокруг этого основного поведения. Это отличается от совещаний разработчиков, когда все слушают, как кто-то говорит: «А что, если пользователь захочет ...?» Наблюдение за пользователями устраняет эту путаницу.
Вы увидите, что происходит, когда пользователи застревают. Когда вы застряли, вы оглядываетесь вокруг. Когда пользователи застревают, они сужают фокус. Им становится все труднее видеть решения в других местах на экране. Это одна из причин, почему текст справки - плохое решение для плохого дизайна пользовательского интерфейса. Если вам нужно разместить инструкции для пользователя, обязательно поместите их рядом с проблемными областями. Узкий фокус внимания пользователя - это то, почему подсказки более полезны, чем отдельные страницы справки.
Пользователи, как правило, путаются. Они найдут способ, который будет работать, и будут придерживаться его, независимо от того, насколько он запутан. Лучше предоставить один действительно очевидный способ работы, чем два или три коротких пути.
Вы также обнаружите, что существует разрыв между тем, что пользователи говорят, что они хотят, и тем, что они реально делают. В этом и проблема, так как обычный способ сбора требований пользователей - это опрос. Вот почему лучший способ определить требования - это наблюдать за пользователями. За час наблюдения за пользователями вы получите больше информации, чем за день выспрашивания чего они хотят.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
День двести шестьдесят второй. #ЧтоНовенького
Новинки Visual Studio 2019 version 16.4 Preview 2
Обновления окна терминала
Добавлена возможность создавать несколько экземпляров терминала и автоматически создавать профили для командной строки разработчика, Developer PowerShell и любых дистрибутивов WSL, доступных на вашем компьютере. Автоматическое создание профиля будет происходить при первом запуске или с помощью кнопки восстановления профилей.
Окно Инструментов Контейнера
Эта функция была доступна только в качестве расширения в Visual Studio Marketplace. Но из-за большой популярности её включили в саму IDE. Окно позволяет просматривать, проверять, останавливать, запускать и удалять образы и контейнеры Docker на локальном компьютере (см. картинку ниже). Кроме того, вы можете просматривать папки и файлы в запущенных контейнерах, открывать окно терминала и просматривать журналы.
Источник: https://devblogs.microsoft.com/visualstudio/fall-sports-pumpkin-spice-and-visual-studio-2019-v16-4-preview-2/
Новинки Visual Studio 2019 version 16.4 Preview 2
Обновления окна терминала
Добавлена возможность создавать несколько экземпляров терминала и автоматически создавать профили для командной строки разработчика, Developer PowerShell и любых дистрибутивов WSL, доступных на вашем компьютере. Автоматическое создание профиля будет происходить при первом запуске или с помощью кнопки восстановления профилей.
Окно Инструментов Контейнера
Эта функция была доступна только в качестве расширения в Visual Studio Marketplace. Но из-за большой популярности её включили в саму IDE. Окно позволяет просматривать, проверять, останавливать, запускать и удалять образы и контейнеры Docker на локальном компьютере (см. картинку ниже). Кроме того, вы можете просматривать папки и файлы в запущенных контейнерах, открывать окно терминала и просматривать журналы.
Источник: https://devblogs.microsoft.com/visualstudio/fall-sports-pumpkin-spice-and-visual-studio-2019-v16-4-preview-2/
День двести шестьдесят третий. #юмор
"Выберите все квадраты с багами." Это однозначно лучшая каптча, которую я видел.
"Выберите все квадраты с багами." Это однозначно лучшая каптча, которую я видел.
День двести шестьдесят четвёртый. #ЗаметкиНаПолях
Использование кортежей. Начало
Ранее я уже писал, что в C# 7 упрощён синтаксис использования кортежей. Кортежи можно использовать как параметры методов, переопределять методы, используя кортежи с разными типами и количеством значений, приводить кортежи с разными типами значений, проверять кортежи на равенство и многое другое. Но это не значит, что теперь обязательно их использовать везде. Далее рассмотрим некоторые альтернативы кортежам, их плюсы и минусы.
1. Тип System.Tuple<…>
Типы
Недостатки:
- отсутствие какой-либо языковой интеграции (сложнее для создания, более длинные имена типов, отсутствует поддержка приведений);
- обращаться к элементам можно только по строгим именам вида
- кортежи ссылочного типа ведут себя как полноценные объекты, а не как контейнеры значений, что может быть как хорошо, так и плохо, в зависимости от контекста.
Преимущества:
- копирование ссылки на большой объект
2. Анонимные типы
Анонимные типы были введены как часть LINQ, и это остаётся основным вариантом их использования. Можно использовать их для обычных переменных в методе, но это встречается крайне редко.
Большинство преимуществ анонимных типов также присутствуют в кортежах C# 7: именованные элементы, вывод имени элементов, естественное сравнение и чёткое строковое представление.
Недостатки:
- нельзя вернуть анонимный тип из методов или свойств, не потеряв при этом безопасность типов (использование в типе
Преимущества:
- анонимные типы поддерживаются внешними поставщиками LINQ (для баз данных и т.д.). Литералы кортежей в настоящее время не могут быть использованы в деревьях выражений.
- анонимные типы могут быть более эффективными в некоторых контекстах благодаря передаче по ссылке. Хотя, в большинстве случаев это вряд ли будет проблемой, и тот факт, что кортежи не создают никаких объектов для очистки сборщиком мусора, даёт им преимущество в эффективности.
3. Именованные типы
Кортежи - это просто контейнеры переменных. В них нет инкапсуляции; они не несут никакого значения, кроме того, которое вы подразумеваете при работе с ними. Иногда это именно то, что надо, но остерегайтесь использовать их слишком широко. Рассмотрим кортеж
- двумерные декартовы координаты (x, y)
- двумерные полярные координаты (радиус, угол)
- одномерный отрезок (начало, конец)
- любой другой вариант (тысячи их).
Каждый из этих вариантов использования подразумевает различные операции над ним при моделировании в качестве типа. Вам не нужно беспокоиться о выведении имён элементов или о том, что пользователи случайно используют, например, декартовы координаты в качестве полярных координат. Если вам нужна простая временная группировка значений, кортежи подойдут. Но если вы обнаружите, что вы используете одну и ту же форму кортежа в нескольких местах кода, я бы рекомендовал заменить её на именованный тип.
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 11.
Использование кортежей. Начало
Ранее я уже писал, что в C# 7 упрощён синтаксис использования кортежей. Кортежи можно использовать как параметры методов, переопределять методы, используя кортежи с разными типами и количеством значений, приводить кортежи с разными типами значений, проверять кортежи на равенство и многое другое. Но это не значит, что теперь обязательно их использовать везде. Далее рассмотрим некоторые альтернативы кортежам, их плюсы и минусы.
1. Тип System.Tuple<…>
Типы
System.Tuple<…>
в .NET 4 являются неизменяемыми ссылочными типами, хотя типы элементов в них могут быть изменяемыми. Недостатки:
- отсутствие какой-либо языковой интеграции (сложнее для создания, более длинные имена типов, отсутствует поддержка приведений);
- обращаться к элементам можно только по строгим именам вида
ItemX
;- кортежи ссылочного типа ведут себя как полноценные объекты, а не как контейнеры значений, что может быть как хорошо, так и плохо, в зависимости от контекста.
Преимущества:
- копирование ссылки на большой объект
Tuple<…>
более эффективно, чем копирование значимых типов кортежей ValueTuple<…>
, которое включает в себя копирование всех значений элементов. Это также влияет на безопасную многопоточность: копирование ссылки является атомарным, тогда как копирование кортежа ValueTuple
- нет.2. Анонимные типы
Анонимные типы были введены как часть LINQ, и это остаётся основным вариантом их использования. Можно использовать их для обычных переменных в методе, но это встречается крайне редко.
Большинство преимуществ анонимных типов также присутствуют в кортежах C# 7: именованные элементы, вывод имени элементов, естественное сравнение и чёткое строковое представление.
Недостатки:
- нельзя вернуть анонимный тип из методов или свойств, не потеряв при этом безопасность типов (использование в типе
dynamic
).Преимущества:
- анонимные типы поддерживаются внешними поставщиками LINQ (для баз данных и т.д.). Литералы кортежей в настоящее время не могут быть использованы в деревьях выражений.
- анонимные типы могут быть более эффективными в некоторых контекстах благодаря передаче по ссылке. Хотя, в большинстве случаев это вряд ли будет проблемой, и тот факт, что кортежи не создают никаких объектов для очистки сборщиком мусора, даёт им преимущество в эффективности.
3. Именованные типы
Кортежи - это просто контейнеры переменных. В них нет инкапсуляции; они не несут никакого значения, кроме того, которое вы подразумеваете при работе с ними. Иногда это именно то, что надо, но остерегайтесь использовать их слишком широко. Рассмотрим кортеж
(double, double)
. Он может быть использован как:- двумерные декартовы координаты (x, y)
- двумерные полярные координаты (радиус, угол)
- одномерный отрезок (начало, конец)
- любой другой вариант (тысячи их).
Каждый из этих вариантов использования подразумевает различные операции над ним при моделировании в качестве типа. Вам не нужно беспокоиться о выведении имён элементов или о том, что пользователи случайно используют, например, декартовы координаты в качестве полярных координат. Если вам нужна простая временная группировка значений, кортежи подойдут. Но если вы обнаружите, что вы используете одну и ту же форму кортежа в нескольких местах кода, я бы рекомендовал заменить её на именованный тип.
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 11.
День двести шестьдесят пятый. #ЗаметкиНаПолях
Заметки по использованию кортежей
Замечание: что здесь приведены умозаключения о вариантах использования кортежей, поскольку реальной практики их применения ещё не так много.
1. Непубличные API и легко изменяемый код
Пока сообщество не имеет достаточно опыта использования кортежей в публичных API, не набило шишек и не выработало лучших практик использования, лучше будет с ними не экспериментировать. Если же вы контролируете (и можете произвольно изменять) весь код, который взаимодействует с вашим API, можно попробовать. Но не стоит ставить себя в ситуацию, когда вы возвращаете кортеж из открытого метода только потому, что это легко сделать, чтобы потом оказалось, что значения придётся инкапсулировать. Именованный тип требует больше усилий по проектированию и реализации, но результат вряд ли будет более сложным для использования вызывающей стороной. Кортежи в основном удобны для автора, но не для пользователя.
2. Локальные переменные
Кортежи в первую очередь предназначены для того, чтобы из метода можно было возвращать несколько значений без использования параметров или специально созданного типа. Но это не значит, что это единственное место, где вы можете их использовать. Внутри метода нет ничего необычного в том, чтобы иметь естественные группы переменных. Вы можете часто замечать, что переменные имеют общий префикс. Поэтому следующий код:
Сказанное выше про локальные переменные можно применить и к полям класса. Однако существует несколько ограничений и предостережений:
- Элементы кортежа могут по-прежнему назначаться в конструкторе индивидуально, но, если вы инициализируете не все элементы, никакого предупреждения не будет.
- Либо все поле кортежа будут доступны только для чтения, либо нет. Если у вас есть группа связанных полей, некоторые из которых доступны только для чтения, а некоторые нет, вам придётся либо отказаться от использования модификатора
- Если некоторые из полей являются автоматически сгенерированными, поддерживающими автоматически реализованные свойства, вам потребуется написать полные свойства, чтобы использовать кортеж.
4. Кортежи и dynamic не дружат
Есть подозрение, что кортежи и тип
- Связыватель типа dynamic не знает об именах элементов
Имена элементов кортежа в основном касаются времени компиляции. Так как динамическое связывание происходит только во время выполнения, следующий код (несмотря на то, что выглядит безобидно):
- Связыватель типа dynamic (пока) не знает об элементах кортежей далее 8-го
Для кортежей длиннее 8 элементов компилятор использует
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 11.
Заметки по использованию кортежей
Замечание: что здесь приведены умозаключения о вариантах использования кортежей, поскольку реальной практики их применения ещё не так много.
1. Непубличные API и легко изменяемый код
Пока сообщество не имеет достаточно опыта использования кортежей в публичных API, не набило шишек и не выработало лучших практик использования, лучше будет с ними не экспериментировать. Если же вы контролируете (и можете произвольно изменять) весь код, который взаимодействует с вашим API, можно попробовать. Но не стоит ставить себя в ситуацию, когда вы возвращаете кортеж из открытого метода только потому, что это легко сделать, чтобы потом оказалось, что значения придётся инкапсулировать. Именованный тип требует больше усилий по проектированию и реализации, но результат вряд ли будет более сложным для использования вызывающей стороной. Кортежи в основном удобны для автора, но не для пользователя.
2. Локальные переменные
Кортежи в первую очередь предназначены для того, чтобы из метода можно было возвращать несколько значений без использования параметров или специально созданного типа. Но это не значит, что это единственное место, где вы можете их использовать. Внутри метода нет ничего необычного в том, чтобы иметь естественные группы переменных. Вы можете часто замечать, что переменные имеют общий префикс. Поэтому следующий код:
string bestPlayer;Можно переписать так:
int bestScore;
foreach (var game in games)
{
if (game.Score > bestScore)
{
bestPlayer = game.Player;
bestScore = game.Score;
}
}
(string player, int score) best = (null, -1);3. Поля
foreach (var game in games)
{
if (game.Score > best.score)
{
best = (game.Player, game.Score);
}
}
Сказанное выше про локальные переменные можно применить и к полям класса. Однако существует несколько ограничений и предостережений:
- Элементы кортежа могут по-прежнему назначаться в конструкторе индивидуально, но, если вы инициализируете не все элементы, никакого предупреждения не будет.
- Либо все поле кортежа будут доступны только для чтения, либо нет. Если у вас есть группа связанных полей, некоторые из которых доступны только для чтения, а некоторые нет, вам придётся либо отказаться от использования модификатора
readonly
, либо не использовать кортежи.- Если некоторые из полей являются автоматически сгенерированными, поддерживающими автоматически реализованные свойства, вам потребуется написать полные свойства, чтобы использовать кортеж.
4. Кортежи и dynamic не дружат
Есть подозрение, что кортежи и тип
dynamic
и так не будут особо пересекаться. Но всё же стоит помнить о двух проблемах, связанных с доступом к элементам:- Связыватель типа dynamic не знает об именах элементов
Имена элементов кортежа в основном касаются времени компиляции. Так как динамическое связывание происходит только во время выполнения, следующий код (несмотря на то, что выглядит безобидно):
dynamic tuple = (x: 10, y: 20);приведёт к ошибке времени выполнения:
Console.WriteLine(tuple.x);
'System.ValueTuple<int,int>' не содержит определения для 'x'Если изменить код на вывод
tuple.Item1
, он сработает (по крайней мере, для первых 8 элементов).- Связыватель типа dynamic (пока) не знает об элементах кортежей далее 8-го
Для кортежей длиннее 8 элементов компилятор использует
ValueTuple<…>
, где восьмым элементом является ещё один кортеж. Таким образом 9й элемент – это первый элемент кортежа, находящегося в 8-м элементе исходного кортежа. Таким образом, следующий код скомпилируется:var tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9);Но на настоящий момент приводит к ошибке времени выполнения:
Console.WriteLine(tuple.Item9);
dynamic d = tuple;
Console.WriteLine(d.Item9);
'System.ValueTuple<int,int,int,int,int,int,int,System.ValueTuple<int,int>>' не содержит определения для 'Item9'Возможно, это будет исправлено в новых версиях компилятора.
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 11.
День двести шестьдесят шестой. #ЧтоНовенького
.Net Conf 2019
В конце сентября прошла конференция Microsoft по .Net разработке .Net Conf 2019. И недавно наша любимая корпорация выкатила целый плейлист с выступлениями.
Его будет достаточно, чтобы занять себя как минимум на пару недель. Самому очень хочется глянуть многое из представленного. Осталось найти время.
https://www.youtube.com/playlist?list=PLReL099Y5nRd04p81Q7p5TtyjCrj9tz1t
PS: Естественно всё English only. Сорян.
.Net Conf 2019
В конце сентября прошла конференция Microsoft по .Net разработке .Net Conf 2019. И недавно наша любимая корпорация выкатила целый плейлист с выступлениями.
Его будет достаточно, чтобы занять себя как минимум на пару недель. Самому очень хочется глянуть многое из представленного. Осталось найти время.
https://www.youtube.com/playlist?list=PLReL099Y5nRd04p81Q7p5TtyjCrj9tz1t
PS: Естественно всё English only. Сорян.
День двести шестьдесят седьмой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
4. Автоматизируйте стандарт кодирования
У вас наверняка тоже такое бывало. В начале проекта у всех много добрых намерений. Многие из этих намерений документируются. Те, что касаются кода, попадают в стандарт кодирования проекта. На первом собрании тимлид просматривает документ и, в лучшем случае, все соглашаются, что они попытаются следовать ему. Однако, как только проект начинается, все эти благие намерения отпадают одно за другим. Когда проект, наконец, завершается, в коде полный бардак, и никто не знает, как это произошло.
Где вы свернули не туда? Вероятно, уже на первом собрании. Одни участники проекта не обратили внимания, другие неправильно поняли, третьи вообще не согласились и сразу решили делать по-своему. Оставшиеся всё поняли и согласились, но, когда дедлайн стал поджимать, от стандартов пришлось отступить. Правильно отформатированный код не получит очков от клиента, который хочет больше функциональности. Кроме того, следование стандарту кодирования может быть довольно скучной задачей, если оно не автоматизировано. Просто попробуйте навести порядок в классе, чтобы убедиться в этом.
Но если всё так сложно, зачем мы вообще вводим стандарт кодирования? Одна из причин форматирования кода единообразным способом заключается в том, что никто не может «владеть» частью кода, просто отформатировав его по-своему. Мы можем запретить разработчикам использовать определенные антипаттерны, чтобы избежать некоторых распространенных ошибок. Стандарт кодирования должен облегчить работу в проекте и поддерживать скорость разработки от начала до конца. Из этого следует, что все должны согласиться с принятым стандартом кодирования: если один разработчик использует три пробела для отступов кода, а другой использует четыре, ничего хорошего не выйдет.
Существует множество инструментов, которые можно использовать для составления отчетов о качестве кода, а также для документирования и поддержания стандарта кодирования, но это не панацея. Стандарт должен быть автоматизирован и применён везде, где это возможно. Вот несколько примеров:
- Сделайте форматирование кода является частью процесса сборки, чтобы оно выполнялось при каждой компиляции.
- Используйте статические анализаторы кода для обнаружения антипаттернов. Если они найдены, отменяйте сборку, как при критической ошибке.
- Научитесь настраивать эти инструменты, чтобы вы могли обнаруживать свои собственные специфические для проекта антипаттерны.
- Измеряйте не только охват кода тестами, но и автоматически проверяйте результаты. Опять же, отменяйте сборку, если покрытие тестами слишком слабое.
Попробуйте применить это ко всему, что вы считаете важным. Вы не сможете автоматизировать всё, что вас действительно волнует. Для того, что вы не можете автоматически проверять или исправить, создайте набор правил, дополняющих автоматизированный контроль. Однако смиритесь с тем, что и вы, и ваши коллеги не всегда будут им следовать.
Наконец, стандарт кодирования должен быть гибким. По мере развития проекта потребности меняются, и то, что казалось логичным вначале, не обязательно останется правильным несколько месяцев спустя.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
97 Вещей, Которые Должен Знать Каждый Программист
4. Автоматизируйте стандарт кодирования
У вас наверняка тоже такое бывало. В начале проекта у всех много добрых намерений. Многие из этих намерений документируются. Те, что касаются кода, попадают в стандарт кодирования проекта. На первом собрании тимлид просматривает документ и, в лучшем случае, все соглашаются, что они попытаются следовать ему. Однако, как только проект начинается, все эти благие намерения отпадают одно за другим. Когда проект, наконец, завершается, в коде полный бардак, и никто не знает, как это произошло.
Где вы свернули не туда? Вероятно, уже на первом собрании. Одни участники проекта не обратили внимания, другие неправильно поняли, третьи вообще не согласились и сразу решили делать по-своему. Оставшиеся всё поняли и согласились, но, когда дедлайн стал поджимать, от стандартов пришлось отступить. Правильно отформатированный код не получит очков от клиента, который хочет больше функциональности. Кроме того, следование стандарту кодирования может быть довольно скучной задачей, если оно не автоматизировано. Просто попробуйте навести порядок в классе, чтобы убедиться в этом.
Но если всё так сложно, зачем мы вообще вводим стандарт кодирования? Одна из причин форматирования кода единообразным способом заключается в том, что никто не может «владеть» частью кода, просто отформатировав его по-своему. Мы можем запретить разработчикам использовать определенные антипаттерны, чтобы избежать некоторых распространенных ошибок. Стандарт кодирования должен облегчить работу в проекте и поддерживать скорость разработки от начала до конца. Из этого следует, что все должны согласиться с принятым стандартом кодирования: если один разработчик использует три пробела для отступов кода, а другой использует четыре, ничего хорошего не выйдет.
Существует множество инструментов, которые можно использовать для составления отчетов о качестве кода, а также для документирования и поддержания стандарта кодирования, но это не панацея. Стандарт должен быть автоматизирован и применён везде, где это возможно. Вот несколько примеров:
- Сделайте форматирование кода является частью процесса сборки, чтобы оно выполнялось при каждой компиляции.
- Используйте статические анализаторы кода для обнаружения антипаттернов. Если они найдены, отменяйте сборку, как при критической ошибке.
- Научитесь настраивать эти инструменты, чтобы вы могли обнаруживать свои собственные специфические для проекта антипаттерны.
- Измеряйте не только охват кода тестами, но и автоматически проверяйте результаты. Опять же, отменяйте сборку, если покрытие тестами слишком слабое.
Попробуйте применить это ко всему, что вы считаете важным. Вы не сможете автоматизировать всё, что вас действительно волнует. Для того, что вы не можете автоматически проверять или исправить, создайте набор правил, дополняющих автоматизированный контроль. Однако смиритесь с тем, что и вы, и ваши коллеги не всегда будут им следовать.
Наконец, стандарт кодирования должен быть гибким. По мере развития проекта потребности меняются, и то, что казалось логичным вначале, не обязательно останется правильным несколько месяцев спустя.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/