.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День триста тридцать четвёртый. #ЗадачиНаСобеседовании
Ответ на задачу
Дана коллекция точек на плоскости с координатами (x, y). Для простоты допустим, что все координаты целочисленные. Нужно написать функцию, которая будет вычислять количество прямоугольников, которые образуют эти точки.

Как я уже говорил, у меня есть теория решения, в которой я более-менее уверен, но пока не проверял.
Итак, пусть у нас есть коллекция точек Point { int x; int y; }. Мы проходим по коллекции и для каждой точки добавляем в коллекцию вектор, начинающийся в этой точке и заканчивающийся в любой точке, которая выше этой точки (координата y больше координаты y текущей точки – y2 > y1), либо на том же уровне по высоте, но правее (x2 > x1, если y2 = y1). Таким образом для точки A (см. рисунок ниже) мы добавляем векторы AB, AC, AD, но не добавляем вектор AE (он будет учтён для точки E, как EA). Кроме того, для каждого вектора мы считаем его «направление» (x2 – x1, y2 – y1). Например, для точек A(0,0) и B(-1,4), направление вектора AB = (-1,4), а для AD = (11,7).
Таким образом получим коллекцию векторов
struct Vector
{
Point A { get; set; }
Point B { get; set; }
(int x, int y) Direction
{
get
{
return (B.X - A.X, B.Y - A.Y);
}
}
}
Теперь для каждого вектора из набора ищем вектор с равным ему направлением и начальной точкой, выбранной по тому же правилу (выше начальной точки текущего вектора или на том же уровне, но правее). Например, для вектора AB таким будет вектор CD (но для вектора CD мы не найдём вектора AB, т.к. A ниже C). Мы получили две параллельные и равные стороны (поскольку направления равны). Остался ещё один момент. У прямоугольников есть свойство, что их диагонали равны. Поэтому сравниваем длины диагоналей (не используем квадратный корень из формулы расстояния, чтобы не заморачиваться с округлением):
double AD = Math.Pow(vCD.A.X - vAB.B.X, 2) + Math.Pow(vCD.A.Y - vAB.B.Y, 2);
double BC = Math.Pow(vAB.B.X - vCD.A.X, 2) + Math.Pow(vAB.B.Y - vCD.A.Y, 2);
Если диагонали AD и BC равны, у нас есть прямоугольник.
Таким образом считаем количество для всех векторов и делим общее количество на 2, поскольку мы посчитаем прямоугольник ABCD дважды для векторов AB и AC).

По поводу стоимости: если у нас N точек, тогда в худшем случае у будет N(N-1)/2 векторов, то есть O(N^2) времени на создание векторов и O(N^4) на обход.

Как-то так. Если у вас есть вопросы или замечания, добро пожаловать в чат.
День триста тридцать пятый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
16. Замечание о комментариях.
На первом моём занятии по программированию в колледже преподаватель выдал по два бланка кода на Basic, написал на доске: «Напишите программу, принимающую результаты 10 игр в боулинг и рассчитывающую средний результат», - и вышел из класса. Насколько сложно это может быть? Я не помню свое окончательное решение, но я уверен, что в нем был цикл FOR/NEXT, и весь код был не длиннее 15 строк. Каждый бланк кода - да, дети, мы обычно писали код от руки перед тем, как вводить его в компьютер, - содержал около 70 строк. Мне было непонятно, почему преподаватель дал нам два бланка. Поскольку мой почерк всегда был корявым, я использовал второй бланк, чтобы аккуратно переписать свой код, надеясь получить пару дополнительных баллов.
К моему большому удивлению, когда я получил результат в начале следующего занятия, у меня была едва проходная оценка. (Это мучало меня потом всё время обучения в колледже.) Поперёк всего моего аккуратно переписанного кода было написано: «Без комментариев?»
Недостаточно того, чтобы мы с преподавателем знали, что должна делать программа. Частью задания было научить меня, что мой код должен быть понятен следующему программисту. Этот урок я не забыл.
Комментарии - не зло. Они так же необходимы в программировании, как основные конструкции кода. У большинства современных языков есть инструмент, анализирующий правильно отформатированные комментарии, чтобы автоматически генерировать документацию. Это очень хорошее начало, но этого недостаточно. Внутри вашего кода должны быть пояснения о том, что код должен делать. Кодирование по старому принципу: «Если было трудно писать, это должно быть трудно читать», - оказывает медвежью услугу вашему клиенту, вашему работодателю, вашим коллегам и в конечном итоге вам же в будущем.
С другой стороны, вы можете зайти слишком далеко в комментировании. Убедитесь, что ваши комментарии уточняют код, но не загромождают его. Добавляйте в код комментарии, объясняющие, что он должен выполнить. Заогловочные комментарии должны дать любому программисту достаточно информации, чтобы использовать ваш код без необходимости его чтения, в то время как встроенные комментарии должны помочь следующему разработчику исправить или улучшить его.
Однажды на работе я не согласился с проектом решения, принятым теми, кто был выше меня. Желая отомстить, как часто делают молодые программисты, я вставил текст письма, в котором указывалось, что нужно использовать их решение, в блок комментария в шапке файла. Оказалось, что менеджеры в этой компании перепроверяли код перед релизом. Так я познакомился со значением термина «действие, ограничивающее карьерный рост».

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Cal Evans
This media is not supported in your browser
VIEW IN TELEGRAM
День триста тридцать шестой.
С новым годом, дорогие читатели. Чистого вам кода без багов и ворнингов, зелёных тестов и шустрых IDE, адекватных заказчиков и блестящих идей для реализации их хотелок.
Много денег желать не буду, счастье не в них. Главное, чтобы их хватало на то, чтобы не волноваться об их недостатке, а получать удовольствие от работы. Ведь в этом настоящее счастье: заниматься любимым делом, приносить людям пользу. А если за это ещё и платят, так вообще жизнь удалась!
Счастья вам! С новым годом!
День триста тридцать седьмой. #Оффтоп
Пока отходите от празднования нового года, вот вам пара ненапряжных интересных видео.
Обычно объяснение того, как выполняется ваш высокоуровневый код в .Net на компьютере заканчивается на этапе JIT-компиляции. Но что же происходит потом?
В первом видео рассказывается о том, как программа вычисления чисел Фибоначчи на C (не пугайтесь, синтаксис очень похож на C#) компилируется в машинный код: https://www.youtube.com/watch?v=yOyaJXpAYZQ
А во втором видео уже скомпилированный код переводится в настоящие нули и единицы команд и аргументов и передаётся в самодельный компьютер, на котором и запускается: https://www.youtube.com/watch?v=a73ZXDJtU48

Извините, как обычно, видео на английском, но, думаю, что даже базовых знаний будет достаточно. Объясняется довольно подробно.

Источник: https://www.youtube.com/user/eaterbc
День триста тридцать восьмой. #DesignPatterns
Паттерны проектирования
6. Паттерн «Посетитель» (Visitor). Начало
Назначение:
описывает операцию, выполняемую с каждым объектом из некоторой иерархии классов. Паттерн «Посетитель» позволяет определить новую операцию, не изменяя классов этих объектов.
Причины использования:
Объектно-ориентированное программирование предполагает единство данных и операций. Обычно классы представляют некоторые операции, скрывая структуры данных, над которыми эти операции производятся. Но не всегда удобно или возможно смешивать операции и данные в одном месте. Логика операции может меняться независимо от самих данных, а вынесение такой логики за пределы иерархии данных чревато дублированием кода и хрупкостью.
В объектно-ориентированном решении базовый класс иерархии задаёт семейство операций, поведение которых определяется наследниками. Таким образом легко добавлять новый тип в иерархию, но сложно добавлять новую операцию (приходится изменять все типы в иерархии). Паттерн «Посетитель» позволяет решить эту проблему. Посетитель позволяет клиентскому коду исследовать иерархию типов и выполнять различные операции в зависимости от конкретного типа объекта.
При этом паттерн «Посетитель» усложняет добавление новых типов в иерархию наследования. Добавление нового типа требует изменения интерфейса посетителя и ломает все его реализации. Это значит, что паттерн «Посетитель» идеально подходит для расширения функциональности стабильных иерархий наследования с переменным числом операций.
Классическая диаграмма приведена на рисунке ниже:
- Visitor — определяет интерфейс посетителя;
- Element — базовый класс иерархии, для которой нужно добавить новую операцию;
- Client — использует посетитель для обработки иерархии элементов.
В базовый класс Element добавляется абстрактный метод Accept, который принимает Visitor, а каждый конкретный класс иерархии просто вызывает метод Visit переданного ему посетителя (в перегруженном методе Accept).

Интерфейс или абстрактный класс посетителя
Обычно посетитель Visitor определяется интерфейсом IVisitor. Такой подход налагает меньше ограничений на клиентов, но делает их более хрупкими. Каждый раз при добавлении типа в иерархию интерфейс посетителя обновляется и в нем появляется новый метод Visit(ConcreteElementC).
Использование абстрактного базового класса VisitorBase позволяет клиентам посещать лишь нужные типы иерархии, переопределяя лишь нужные методы Visit.

Функциональная версия
Когда количество конкретных типов иерархии наследования невелико, интерфейс посетителя можно заменить методом со списком делегатов. Для этого метод Accept можно переименовать в Match, который будет принимать несколько делегатов для обработки конкретных типов иерархии.
public abstract class Element {
public void Match(
Action<ConcreteElementA> handleA,
Action<ConcreteElementB> handleB)
{
switch (this) {
case ConcreteElementA element:
return handleA(element);
case ConcreteElementB element:
return handleB(element);
default:
throw new InvalidOperationException(…);
}
}
}
Вызов:
element.Match(a => HandleA(a), b => HandleB(b));

Применение
- Использовать паттерн «Посетитель» нужно тогда, когда набор типов иерархии стабилен, а набор операций — нет.
- Классический вариант паттерна (с выделением интерфейса) лучше всего подходит для больших составных иерархий и когда заранее не известно, какие типы будут посещаться чаще других.
- Функциональный вариант посетителя всегда можно построить на основе классической реализации, когда станет известно, что многим клиентам нужно посещать лишь небольшое число типов иерархии.

Окончание следует...

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 6.
День триста тридцать девятый. #DesignPatterns
Паттерны проектирования
6. Паттерн «Посетитель» (Visitor). Окончание
Пример использования паттерна «Посетитель»
Каноническим примером демонстрации полиморфизма является иерархия фигур с базовым классом Shape, имеющим абстрактный метод Area (площадь фигуры), и несколькими потомками, например, Circle, Rectangle и Triangle, каждый из которых переопределяет метод Area. Допустим, что нам понадобилось добавить метод вычисления периметра фигуры. Ранее на канале приводился пример с использованием сопоставления по шаблону.
Рассмотрим, как можно добавлять функциональность, используя паттерн «Посетитель».
1. Интерфейс посетителя:
public interface IShapeVisitor {
void Visit(Circle circle);
void Visit(Triangle square);
void Visit(Rectangle rectangle);
}
2. Добавим в иерархию классов метод Accept, принимающий посетителя, и вызывающий в потомках метод Visit посетителя:
public abstract class Shape {
// … другие методы …
public abstract void Accept(IShapeVisitor visitor);
}
public class Circle : Shape {
// … другие методы …
public override void Accept(IShapeVisitor visitor)
=> visitor.Visit(this);
}
// … аналогично для других потомков
3. Реализуем конкретного посетителя для вычисления периметра:
public class PerimeterVisitor : IShapeVisitor
{
public double Perimeter { get; private set; }
public void Visit(Circle circle) {
Perimeter = 2 * Math.PI * circle.Radius;
}
public void Visit(Rectangle rect) {
Perimeter = 2 * (rect.Height + rect.Width);
}
public void Visit(Triangle tri) {
Perimeter = tri.SideA + tri.SideB + tri.SideC;
}
}
4. Создаём метод расширения:
public static class ShapeExtentions {
public static double Perimeter(this Shape shape) {
var visitor = new PerimeterVisitor();
shape.Accept(visitor);
return visitor.Perimeter;
}
}
// Вызываем
Shape shape = … //получаем форму
var p = shape.Perimeter();
Таким образом для любой новой функции (например, рисования фигуры) нам достаточно выполнить пункты 3 и 4:
- создать конкретный класс посетителя (DrawVisitor);
- создать и вызвать соответствующий метод расширения.
Понятно, что для такого простого примера единого метода расширения с сопоставлением по шаблону, вероятно, будет достаточно. Однако, если добавляемый функционал сложен и объёмен, реализация его в отдельном классе посетителя с разбиением на методы под каждый тип объекта будет предпочтительнее.

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 18.
День триста сороковой. #Оффтоп
Ещё одно ненапряжное видео. Абсолютно гениальный доклад Дилана Битти “The Art of Code” на конференции Build Stuff 2019. Философские рассуждения на тему, является ли программирование искусством. С кучей разнообразных примеров, красивой презентацией и тонким английским юмором. Я бы сказал, вот так нужно завлекать людей в программирование. Знаете, что такое квайн? Или что есть программа на языке Chef, которая в то же время является рецептом шоколадного пирога? А что изображено на картинке?
Располагайтесь поудобнее и насладитесь искусством программирования: https://youtu.be/gdSlcxxYAA8

Эээм… уже неловко об этом писать, конечно, но требуется хорошее знание английского.
День триста сорок первый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
17. Комментируйте только то, что код не может выразить
Разница между теорией и практикой гораздо очевиднее на практике, чем в теории – это как нельзя лучше относится к комментариям. Теоретически идея комментирования кода звучит убедительно: предложить читателю подробности, объяснение того, что происходит в коде. Что может быть полезнее, чем полезная добавка? Однако на практике комментарии часто становятся проблемой. Как и в любой другой форме письма, нужно уметь писать хорошие комментарии. В основном это умение заключается в том, чтобы знать, когда их не писать.

Когда код некорректен, компиляторы, интерпретаторы и другие инструменты обязательно найдут ошибку. Если код каким-либо образом некорректен в функциональном отношении, обзоры кода, статический анализ, тесты и повседневное использование в производственной среде устранят большинство ошибок. Но что насчет комментариев? В книге “The Elements of Programming Style” Керниган и Плаугер отмечают, что «комментарий имеет нулевую (или отрицательную) ценность, если он неправильный». И всё же такие комментарии часто засоряют код и остаются там даже дольше, чем ошибки. Они являются постоянной причиной отвлечения и источником дезинформации - маленьким, но препятствием в работе программиста.

Что можно сказать о комментариях, которые не являются технически неправильными, но не добавляют никакой ценности к коду? Такие комментарии являются шумом. Комментарии, которые повторяют код, не добавляют ничего полезного для читателя: сообщение о чём-либо один раз в коде и ещё раз на естественном языке не делает код более правильным или более красивым. Закомментированный код не выполняется, поэтому он не несёт никакой пользы ни для читателя, ни для среды исполнения. Такие комментарии также очень быстро устаревают. Комментарии, связанные с версией программы и закомментированный код пытаются рассказать о версиях и истории программы. На эти вопросы уже отвечают (гораздо более эффективно) инструменты контроля версий.

Большое количество бесполезных и неправильных комментариев в коде побуждает программистов игнорировать вообще все комментарии, либо пропуская их, либо принимая активные меры, чтобы их скрыть. Программисты находчивы и будут обходить всё, что воспринимается ими как помеха: скрывать комментарии, изменять цветовую схему так, чтобы комментарии и фон были одного цвета, создавать скрипты для фильтрации комментариев и т.п. Чтобы уберечь код от таких неправильных применений изобретательности программиста и уменьшить риск игнорирования комментариев, имеющих подлинную ценность, комментарии должны рассматриваться как код. Каждый комментарий должен приносить пользу читателю, иначе это мусор, который следует удалить или переписать.

Что же квалифицируется как ценность? Комментарии должны сказать что-то, что код не может выразить. Комментарий, объясняющий то, о чём уже должен говорить блок кода, свидетельствует о том, что нужно изменить структуру кода или соглашения о кодировании, чтобы код говорил сам за себя. Вместо того, чтобы объяснять плохие имена методов или классов, переименуйте их. Вместо того, чтобы комментировать отдельные блоки в длинной функции, разбейте функцию на меньшие по размеру, чьи имена отражают намерения этих блоков. Попробуйте выразить как можно больше через код. Каждое место, которое нельзя выразить в коде так же хорошо, как естественным языком, становится кандидатом на добавление полезного комментария. Комментируйте лишь то, что код не может сообщить в принципе, а не объясняйте то, что он не сообщает сейчас.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Kelvin Henney
День триста сорок второй.
Сегодня расскажу о паре интересных ресурсов для обучения.
Первый (вполне прозаично) – Microsoft Learn. Ресурс относительно новый, насколько я понимаю, пришёл на смену Microsoft Virtual Academy. Пока, как мне показалось на первый взгляд, там не так много информации, однако всё равно можно найти много интересного. Ресурс построен по принципу курсов: можно отслеживать прогресс (прочитанные статьи отмечаются). Кроме того, даются очки репутации и уровень. И хотя очки добавляются просто по мере открытия страниц курсов, без проверки ваших знаний, но возможно даже это послужит дополнительным стимулом.
Пока всё только на английском, но надеюсь, что в Майкрософт займутся локализацией рано или поздно.

Карта знаний .NET Web программиста
Авторы описывают ресурс как «Детализированную карту знаний для .NET Web программистов от Trainee до Senior.». И это действительно гигантский список, охватывающий язык C#, платформу .Net, принципы проектирования и написания кода, модульное тестирование, ASP.NET, микросервисы, фронтенд разработку, базы данных, облачные сервисы (похвастаюсь, добавили после моего комментария 😊), системы контроля версий и многое другое со ссылками либо на статьи или курсы в сети, либо на книги (с точностью до главы), где можно найти эту информацию.
В списке звёздочками обозначены темы, которые "должен знать" программист определённого уровня (интерн, джун, мидл или сеньор). От себя хочу заметить, что не стоит воспринимать этот список как обязательный (маст-ноу). Там такой объём информации, что, на мой взгляд, одному фулл-стек специалисту охватить всё это просто нереально. Но это нисколько не снижает ценность ресурса. Использовать его как справочник очень полезно. Добавил себе в закладки.
Список приведён как на русском, так и на английском, но сразу предупреждаю, что далеко не все ссылки ведут на русскоязычные источники (такова се ля ви).
ПС: За ссылку на второй ресурс большое спасибо участнику нашего чата @fjod10199.
День триста сорок третий. #ЗаметкиНаПолях
Отложенная (ленивая) инициализация
Ленивая инициализация объекта означает, что его создание откладывается до его первого использования. Она в основном используется для повышения производительности, предотвращения ненужных вычислений и снижения потребления памяти программой. Наиболее распространённые сценарии:
- Ресурсоёмкий объект, который программа может и не использовать. Например, объект Customer со свойством Orders (массив заказов), для инициализации которого требуется запрос из базы данных. Если пользователю не нужны заказы, то нет причин тратить память или время на создание массива Orders. Используя Lazy<Orders>, чтобы объявить отложенную инициализацию объекта Orders, можно избежать расхода системных ресурсов на неиспользуемый объект.
- Ресурсоёмкий объект, создание которого можно отложить. Например, программа загружает несколько экземпляров объекта при запуске, но только некоторые из них требуются немедленно. Можно ускорить запуск программы, отложив инициализацию ненужных объектов до тех пор, пока они не понадобятся.

Базовое использование
Lazy<Orders> orders = new Lazy<Orders>();
...
var listOfOrders = orders.Value; //использование
В этом случае объект параметра типа создаётся при первом использовании (обращении к свойству Value) с помощью Activator.CreateInstance, а сам тип Orders должен иметь конструктор без параметров. Кроме того, ленивому конструктору можно передать параметры:
- bool isThreadSafe – по умолчанию true, то есть создаётся потокобезопасный объект. Первый поток инициализирует объект, далее все потоки используют одни и те же данные. Если предполагается использование в единственном потоке, значение false позволяет получить небольшой выигрыш в быстродействии.
- Func<Orders> valueFactory - лямбда-выражение, позволяющее выполнить любые действия, необходимые для инициализации объекта (см. пример ниже).
- Orders value – предварительно инициализированное значение.

После создания объекта Lazy экземпляр Orders не создается до первого доступа к свойству Value переменной orders. Объект Lazy<T> всегда возвращает тот же объект или значение, которые использовались для его инициализации. Следовательно, свойство Value доступно только для чтения.
Чтобы реализовать открытое свойство с помощью отложенной инициализации, определите вспомогательное поле свойства как Lazy<T> и верните свойство Value из аксессора (get) свойства:
class Customer {
private Lazy<Orders> orders;
public string CustomerID {get; private set;}
public Customer(string id) {
CustomerID = id;
orders = new Lazy<Orders>(() =>
{
// дополнительная логика инициализации
return new Orders(this.CustomerID);
});
}
public Orders MyOrders {
get {
return orders.Value;
}
}
}

Поскольку свойство Value доступно только для чтения, у свойства Orders нет мутатора (set). Изменить значение ленивого объекта нельзя, можно только инициализировать новый объект. Если требуется изменяемое ленивое свойство, мутатор должен:
- создать новое лямбда-выражение, возвращающее переданное мутатору значение,
- передать выражение в конструктор нового объекта Lazy<T>,
- присвоить новый объект вспомогательному полю.
Тогда последующее обращение к аксессору (get) свойства приведёт к новой инициализации ленивого объекта и вернёт новое значение.

Источник: https://docs.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization
День триста сорок четвёртый. #DesignPatterns
Паттерны проектирования
7. Паттерн «Команда» (Command).
Паттерн «Команда» позволяет спрятать действие в объекте и отвязать источник этого действия от места его исполнения.
Назначение: инкапсулирует запрос как объект, позволяя тем самым задавать параметры клиентов для обработки соответствующих запросов, ставить запросы в очередь или протоколировать их, а также поддерживать отмену операций.

Варианты использования:
- Система управляется событиями. При появлении такого события (запроса) необходимо выполнить определённую последовательность действий. Например, в пользовательском интерфейсе пункт меню не должен знать, что происходит при его активизации пользователем, он должен знать лишь о некотором действии, которое нужно выполнить при нажатии кнопки. Кроме того, то же действие можно выполнять при нажатии комбинации клавиш.
- Необходимо параметризировать объекты выполняемым действием, ставить запросы в очередь, хранить историю запросов или поддерживать операции отмены (undo) и повтора (redo) действий.

Классическая диаграмма приведена на рисунке ниже:
- Client – создаёт объекты конкретных команд, передавая в них все необходимые параметры, среди которых могут быть и ссылки на объекты получателей.
- Invoker – инициатор команды. Хранит ссылку на объект команды и обращается к нему, когда нужно выполнить какое-то действие.
- Receiver - получатель команды. Определяет действия, которые должны выполняться в результате запроса.
- Command - интерфейс, представляющий команду. Обычно определяет метод Execute() для выполнения действия, который может принимать необязательный контекст исполнения. Также нередко включает метод Undo(), реализация которого должна заключаться в отмене действия команды.
- ConcreteCommand - конкретная реализация команды, реализует метод Execute(), в котором вызывается метод Action() получателя. Также команда может хранить состояние (параметры, с которыми должен быть вызван метод объекта-получателя).

Аналогией паттерна «Команда» из жизни может быть ресторан. Клиент делает заказ (команду) официанту (инициатору), который передаёт её повару (получателю). Повар не знает, кто конкретно послал ему заказ. Но это ему безразлично, так как вся необходимая информация есть в листе заказа.

Существует две основные реализации паттерна:
- Делегирующая команда, представленная выше, не выполняет операцию самостоятельно, но знает, кто это может сделать, поэтому вызывает метод получателя, содержащий бизнес-логику.
- Самостоятельная команда содержит бизнес-логику внутри самого объекта команды.

Помимо объектно-ориентированной версии, в языке C# очень распространено использование функциональной версии паттерна «Команда» на основе делегатов. С помощью анонимных методов легко получить делегат, который будет захватывать внешний контекст и выполнять требуемое действие.

Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
-
https://refactoring.guru/ru/design-patterns/command
-
https://metanit.com/sharp/patterns/3.3.php
👍1
День триста сорок пятый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
18. Непрерывное обучение
Мы живём в интересное время. Разработка ПО доступна практически из любой точки мира, поэтому для вас не должно быть секретом, что есть много людей, способных выполнять вашу работу. Вы должны продолжать учиться, чтобы оставаться конкурентоспособным. В противном случае вы станете динозавром, застрявшего на одной работе, пока однажды в вас не отпадёт необходимость, либо ваша работа не станет выполняться более дешёвым способом.
Что же делать? Некоторые работодатели достаточно щедры, чтобы обеспечить обучение сотрудников, другие могут не иметь возможности делать это. Чтобы не рисковать, вы должны взять ответственность за своё образование на себя.

Вот список способов чему-нибудь научиться. Многие из них можно найти в Интернете бесплатно:
1. Читайте книги, журналы, блоги, телеграм-каналы 😉 и веб-сайты. Если вы хотите углубиться в тему, подпишитесь на рассылку новостей.
2. Если вы действительно хотите погрузиться в технологию, попробуйте написать немного кода.
3. Всегда старайтесь работать с наставником, потому что быть лучшим в команде может помешать вашему развитию. Хотя научиться чему-то можно у кого угодно, вы можете узнать намного больше от кого-то, кто умнее или опытнее, чем вы. Если вы не можете найти наставника в своей команде, подумайте над тем, чтобы двигаться дальше.
4. Используйте виртуальных наставников. Найдите в сети авторов и разработчиков, которые вам действительно нравятся, и изучите всё, что они пишут. Подпишитесь на их блоги.
5. Познакомьтесь с фреймворками и библиотеками, которые вы используете. Зная, как что-то работает, вы узнаете, как использовать это лучше. Если эти инструменты с открытым исходным кодом, вам действительно повезло. Используйте отладчик, чтобы пройтись по коду и увидеть, что происходит «под капотом». Вы увидите код, написанный и проверенный действительно умными людьми.
6. Всякий раз, когда вы делаете ошибку, исправляете ошибку или сталкиваетесь с проблемой, постарайтесь по-настоящему разобраться, что произошло. Вполне вероятно, что кто-то уже сталкивался с такой же проблемой и опубликовал решение в сети.
7. Хороший способ чему-то научиться - учить или говорить об этом. Когда люди будут слушать вас и задавать вопросы, у вас будет высокая мотивация к обучению. Попробуйте «обучение за ланчем» на работе, в группе соцсети или на местной конференции.
8. Присоединитесь или создайте группу (сообщество) для языка, технологии или дисциплины, которые вас интересуют.
9. Посещайте конференции. Если нет возможности посетить, многие конференции размещают выступления в сети бесплатно.
10. Далеко ездите? Слушайте подкасты в дороге.
11. Вы когда-нибудь запускали инструмент статического анализа кода или просматривали предупреждения в вашей IDE? Разберитесь, что они сообщают и почему.
12. Следуйте советам «Программистов-прагматиков» и изучайте новый язык каждый год. По крайней мере, изучите новую технологию или инструмент. Расширение кругозора даст вам новые идеи, которые вы можете использовать в своей текущей работе.
13. Не всё, что вы изучаете, должно быть о технологиях. Изучите домен, в котором вы работаете, чтобы лучше понять требования и помочь решить бизнес-проблему. Изучение того, как повысить свою производительность – еще одна очень полезная вещь, которую не стоит игнорировать.

Возвращайтесь в школу. Было бы неплохо иметь возможность, как у Нео в Матрице, просто загружать необходимую нам информацию в мозг. Но мы этого не умеем, поэтому нам требуется время. Не нужно тратить на обучение всё свободное время, но стоит делать это регулярно. Немного времени даже раз в неделю всё равно лучше, чем ничего. Но не забывайте, что есть и жизнь помимо работы.

Технология быстро меняется. Не отставайте.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Clint Shank
День триста сорок шестой. #Оффтоп #ЗадачиНаСобеседовании
Встречаем первые «рабочие» выходные года новой задачкой. Эту задачу предлагали на собеседовании в Amazon.
Допустим, у нас есть лестница с N ступеньками. Вы начинаете снизу и можете шагнуть на 1 ступеньку вверх, либо на 2. Нужно написать функцию, которая, получая N, будет выдавать количество вариантов, которыми вы можете пройти снизу вверх.

Например, на рисунке N=3. Мы можем сделать 3 шага по 1 ступеньке (красная линия), шаг на 1 ступеньку, потом на 2 (зелёная), и шаг на 2, потом на 1 (синяя). Понятно, что мы не можем делать шаг на 2 ступеньки со второй, поскольку впереди только одна. Таким образом, у нас всего 3 варианта.

Усложнённый вариант задания: дан набор шагов, которые вы можете сделать. Например, X = {1, 3, 5} – то есть вы можете сделать шаг на 1, 3 или 5 ступенек.

Как обычно, приглашаю всех предлагать варианты решения в чате.
День триста сорок седьмой.
Microsoft 14 января 2020 проводит конференцию ".NET Conf: Focus", темой которой будет ASP.NET Blazor. Для тех, кто ещё не в курсе, Blazor позволяет создавать клиентские приложения на C#.
Это однодневное событие с бесплатным онлайн стримом, в котором выступят спикеры из комьюнити разработчиков и команды, работающей над проектом Blazor. Расписание докладов можно посмотреть тут. Начало в 19:00 мск (16:00 по Гринвичу).
Обещают, что запись будет доступна и после ивента, но прелесть просмотра онлайн в том, что можно задавать вопросы в твиттере с хэштегом #dotNETConf

Я, похоже, знаю, что буду делать в ночь со вторника на среду)))
День триста сорок восьмой. #ЗадачиНаСобеседовании
Ответ на задачу:
Допустим, у нас есть лестница с N ступеньками. Вы начинаете снизу и можете шагнуть на 1 ступеньку вверх, либо на 2. Нужно написать функцию, которая, получая N, будет выдавать количество вариантов, которыми вы можете пройти снизу вверх.

Начнём с нескольких примеров. Перечислим в зависимости от N все возможные пути по номерам ступенек, начиная с 0 (пола):
N = 0: [0]
N = 1: [0, 1]
N = 2: [0, 1, 2], [0, 2]
N = 3: [0, 1, 3],
[0, 1, 2, 3], [0, 2, 3]
N = 4: [0, 1, 2, 4], [0, 2, 4],
[0, 1, 3, 4], [0, 1, 2, 3, 4], [0, 2, 3, 4]
Я специально расположил пути таким образом, чтобы была видна закономерность.
В случае с 0 ступенек у нас 1 путь (мы уже на ней стоим). Т.е. f(0) = 1.
Как добраться до ступеньки 1? Один путь: шагнуть на 1 ступеньку. Т.е. f(1) = 1.
Как добраться до ступеньки 2? Либо добраться до ступеньки 1 и сделать шаг на 1 ступеньку ([0, 1, 2]), либо со ступеньки 0 (с пола) сделать шаг на 2 ступеньки ([0, 2]). Т.е. f(2) = f(1) + f(0) = 2.
Как добраться до ступеньки 3? Либо добраться до ступеньки 2 (двумя способами) и сделать шаг на 1, либо добраться до ступеньки 1 и сделать шаг на 2 ([0, 1, 3]). Т.е. f(3) = f(2) + f(1) = 3.
Со ступенькой 4 аналогично: либо тремя способами добраться до ступеньки 3 и шагнуть на 1, либо двумя способами добраться до ступеньки 2 и шагнуть на 2. Т.е. f(4) = f(3) + f(2) = 5.

Думаю, закономерность понятна
f(n) = f(n - 1) + f(n - 2).
Это числа Фибоначчи.

Аналогичным образом можно доказать, что, если нам дан набор доступных шагов, например, X = {1, 3, 5}, то количество путей будет считаться по формуле
f(n) = f(n - 1) + f(n - 3) + f(n - 5),
либо в более общем случае
f(n, X) = f(n - X[0]) + f(n – X[1]) + f(n – X[2]) + …
Мы уже рассмотрели, что в случае 0 ступенек у нас получается 1 путь. А в случае отрицательного количества ступенек путей нет совсем, т.е.:
- f(n) = 0, если n < 0;
- f(0) = 1.

Напрашивается рекурсия, однако рекурсивный подсчёт будет слишком затратным. Поэтому вместо этого составим массив значений функции от 0 до N и вернём последнее значение:
static long GetNumWays(int N, int[] X)
{
long[] cache = new long[N+1];
cache[0] = 1;

// перебираем ступеньки с 1 по N
for (int i=1; i<=N; i++)
{
//перебираем доступные шаги x из набора X
//и считаем сумму значений f(i-x)
long sum = 0;
foreach (var x in X)
if ((i - x) >= 0)
sum += cache[i - x];
cache[i] += sum;
}

return cache[n];
}
Таким образом алгоритм занимает O(N*|X|) времени и O(N) памяти.

Источники:
-
https://youtu.be/5o-kdjv7FD0
-
https://www.dailycodingproblem.com/
День триста сорок девятый.
Продолжаю готовиться к сертификации по ASP.NET MVC. Поскольку в экзамене вопросы будут как по Core, так и по Framework, для начала решил освежить знания по Framework. До этого читал «ASP.NET MVC 3 Framework» Фримена. Сейчас удалось заполучить вот такую книжку «Professional ASP.NET MVC 5» Джона Гэллоуэя.

Книги очень похожи по структуре и содержанию. Но есть подозрение, что Фримен всё-таки, производный от Гэллоуэя. Например, у Гэллоуэя описан интернет-магазин MusicStore – открыто распространяемый Microsoft код с руководством по его созданию (от самого же Гэллоуэя).
*Руководство описывает аж версию MVC 3, а распространяемый в GitHub код уже на Core, код на Framework убран в архив и больше не поддерживается.
У Фримена это SportsStore. Однако во многих нюансах у Гэллоуэя описание гораздо более подробное, плюс сдобренное неплохим юмором. Хотя, возможно, многое потерялось в переводе, Фримена я в оригинале не видел. Так что тем, у кого проблем с английским нет, советую читать Гэллоуэя.
День триста пятидесятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
Контроллеры
Контроллеры в шаблоне MVC отвечают за реакцию на действия пользователя, зачастую внося изменения в модель, используя данные, введённые пользователем. Таким образом, контроллеры в шаблоне MVC связаны с потоком приложения, отвечая за работу с входящими данными и предоставление результирующих данных, в соответствующем представлении.

Изначально веб-серверы предоставляли статический HTML, хранящийся в файлах на диске. Динамические веб страницы позволили генерировать HTML на лету с помощью скриптов. Однако соответствие между URL и физическим файлом на диске более-менее сохранялось. В MVC реализован другой подход.
Вместо того, чтобы иметь прямую связь между URL и файлом на жестком диске веб-сервера, существует связь между URL и методом действия (Action), в классе контроллера. То есть работу MVC в веб-сценарии можно представить как возврат результата вызова метода, а не динамически генерируемой страницы.

По соглашению в MVC Framework классы контроллеров хранятся в папке Controllers и имеют суффикс Controller в имени класса, например HomeController, AccountController.

В принципе методу-действия можно указать любой тип возврата, но чаще всего в качестве типа результата используется один из типов, унаследованных от абстрактного класса ActionResult:
1. ContentResult – текст;
2. EmptyResult – отсутствие результата;
3. FileResult – загружаемый файл (базовый класс);
3.1 FileContentResult – двоичные данные;
3.2 FilePathResult – выдаёт файл по указанному пути;
3.3 FileStreamResult – поток данных;
4. JavaScriptResult - код JavaScript;
5. JsonResult – JSON данные, обычно возвращаемые как результат AJAX запроса;
6. ViewResult – разметка HTML;
6.1 PartialviewResult – частичная разметка HTML, используется для включения блока (например, меню) в другую страницу;
7. HttpStatusCodeResult – выдаёт указанный код ответа HTTP;
7.1 HttpNotFound – выдаёт код ответа 404 (не найдено);
7.2 HttpUnauthorizedResult - выдаёт код ответа 401 (не авторизовано);
7.3 RedirectResult – перенаправление на другой URL (возвращает код постоянного 302 или временного 301 перенаправления в зависимости от параметра Permanent);
7.4 RedirectToRouteResult – перенаправление на заданные метод-действие и контроллер.

Бывают случаи, когда у вас есть очень простой метод действия, предназначенный только для возврата примитивного типа данных. В этом случае полезно, чтобы сигнатура метода действия отражала тип возврата, например:
public double Distance(int x1, int y1, int x2, int y2) { … }
Обратите внимание, что возвращаемый тип double, а не производный от ActionResult. Это вполне приемлемо. Преимущества такого подхода в том, что он проясняет ваши намерения, а сам метод проще для модульного тестирования.
Когда ASP.NET MVC вызывает этот метод и видит, что возвращаемый тип не является производным от ActionResult, он автоматически создаёт ContentResult, содержащий результат метода действия. Следует иметь в виду, что ContentResult требует строковое значение, поэтому результат должен быть сначала преобразован в строку. Для этого ASP.NET MVC вызывает метод ToString() для результата, используя InvariantCulture. Если вам нужно отформатировать результат в соответствии с определенной культурой, вы должны явно возвращать ContentResult самостоятельно.

Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Главы 2, 16.
День триста пятьдесят первый. #DesignPatterns
Паттерны проектирования
8. Паттерн «Состояние» (State).
Некоторые объекты предметной области могут выполнять разные наборы операций в зависимости от состояния. Паттерн Состояние моделирует конечный автомат с набором допустимых переходов.

Назначение: позволяет объекту варьировать свое поведение в зависимости от внутреннего состояния. Извне создаётся впечатление, что изменился класс объекта. Основная идея в том, что программа может находиться в одном из нескольких состояний, которые всё время сменяют друг друга. Набор этих состояний, а также переходов между ними, предопределён и конечен. Находясь в разных состояниях, программа может по-разному реагировать на одни и те же события, которые происходят с ней.

Причины использования:
Допустим, объект Документ может принимать три состояния: Черновик, Модерация или Опубликован. Для каждого состояния документа метод обработки документа выполняет разные функции. В статусе Черновик он переведёт документ на модерацию (возможно, уведомит модератора о новом задании), в статусе Модерация метод опубликует документ, а в статусе Опубликован не будет делать ничего.
Основная проблема в таком решении проявится в том случае, если в Документ добавить ещё десяток состояний. Каждый метод обработки будет состоять из увесистого условного оператора, перебирающего доступные состояния. Такой код крайне сложно поддерживать. Малейшее изменение логики переходов заставит вас перепроверять работу всех методов, которые содержат условные операторы машины состояний.

Классическая диаграмма приведена на рисунке ниже:
- Context – делегирует операции по переходу между своими состояниями объектам State, вызывая методы Handle();
- State - определяет интерфейс состояния;
- ConcreteState1 и ConcreteState2 - конкретные реализации состояний.

Паттерн Состояние предлагает создать отдельные классы для каждого состояния, в котором может пребывать объект, а затем вынести туда поведение, соответствующее конкретному состоянию. Вместо того, чтобы хранить код всех состояний, объект контекста будет содержать ссылку на один из объектов-состояний и делегировать ему работу, зависящую от состояния. Благодаря тому, что объекты состояний имеют общий интерфейс, контекст сможет делегировать работу состоянию, не привязываясь к его классу. Поведение контекста можно будет изменить в любой момент, подключив к нему другой объект-состояние.
Важное отличие от паттерна Стратегия в том, что и контекст, и сами конкретные состояния могут знать друг о друге и инициировать переходы от одного состояния к другому.

Паттерн «Состояние» в полноценном виде довольно редко применяется на практике. Его применимость определяется сложностью конечного автомата. Чаще всего количество состояний объекта не столь велико, а логика переходов между состояниями не слишком сложна, поэтому её включают в сам объект контекста.

Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
-
https://refactoring.guru/ru/design-patterns/state