.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
День триста пятьдесят девятый. #NetInternals
Начинаю новую серию постов в стиле вопросы и ответы из книги Адама Фурманека «.NET Internals Cookbook».
От автора: "В этой серии я отвечаю на различные вопросы по .NET. Некоторые из них задают на собеседованиях, некоторые я вижу в Интернете, остальные придуманы. Цель - предоставить краткий ответ со ссылками на источники, если это необходимо. Это ни в коем случае не учебник по .NET, это просто набор полезных ответов, чтобы освежить ваши знания."

1. Что происходит, когда вы выбрасываете что-то, что не наследуется от System.Exception? Изменилось ли это со времён .NET 1?
Во-первых, у вас может возникнуть вопрос: «Как вообще это сделать?» Помимо C# существуют другие языки, работающие в CLR. Одним из них является C++/CLI, который является управляемой версией языка C++. В C++ вы можете выбрасывать что угодно – integer, string, byte и т.д. Если вы попытаетесь сделать это в C++/CLI, вы фактически выбросите нечто, что не наследует от System.Exception.

Выбрасываемый объект помещается в System.Runtime.CompilerServices.RuntimeWrappedException, поэтому вы всё равно сможете перехватить исключение с помощью обычного блока catch (Exception e) {}.

Однако до .NET 2 всё было иначе. Выброшенный объект оборачивался, поэтому вы не могли поймать его таким образом. Вам пришлось бы использовать нетипизированную версию блока catch в виде catch {}. По этой причине вы могли видеть код, подобный следующему:
try
{
//
}
catch(Exception e)
{
//
}
catch
{
//
}
После перехода на .NET 2 этот код перестанет компилироваться, потому что последний блок catch никогда не будет выполняться. Вы можете изменить код или восстановить старое поведение, используя атрибут RuntimeCompatibilityAttribute.

Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
День триста шестидесятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
Модели
Слово модель в разработке ПО означает сотни различных концепций. Даже применительно к MVC можно говорить о моделях бизнес-логики (домена) и модели представления. Далее под моделью понимаются объекты, которые используются для отправки информации в базу данных, выполнения бизнес-логики и даже визуализации в представлении. Другими словами, эти объекты представляют домен, на котором построено приложение, а модели — это объекты, которые вы хотите отображать, сохранять, создавать, обновлять и удалять.

ASP.NET MVC 5 предоставляет ряд инструментов и функций для построения функционала приложения, используя только определение объектов модели. Например, для корзины покупок можно написать простые классы C# Product, ShoppingCart и User для представления основных объектов. После этого можно использовать инструменты MVC, для создания контроллеров и представлений для стандартных сценариев отображения, создания, редактирования и удаления для каждого из объектов модели.

Скаффолдинг
Автоматизированные работы по созданию контроллеров и представлений называются скаффолдингом. Скаффолдинг в ASP.NET MVC может сгенерировать шаблонный код, необходимый для функционала создания, чтения, обновления и удаления (CRUD) данных в приложении. Шаблоны скаффолдинга могут проверять определение типа для модели и генерировать контроллер, связанные с ним представления, а в некоторых случаях и классы доступа к данным. Скаффолдинг знает, как назвать контроллеры и представления, какой код должен идти в каждом компоненте и где разместить все эти части в проекте, чтобы приложение работало.
Не ожидайте, что скаффолдинг создаст за вас всё приложение, но он освободит вас от скучной работы по написанию 100% кода приложения вручную. Вы можете настроить и отредактировать результаты скаффолдинга, чтобы настроить приложение под себя.

Entity Framework
EF - это фреймворк объектно-реляционного отображения (object-relational mapping - ORM), которая понимает, как хранить объекты .NET в реляционной базе данных и извлекать их, используя LINQ.
EF поддерживает несколько стилей разработки: database-first («сначала база данных»), model-first («сначала модель») и code-first («сначала код»). Скаффолдинг в ASP.NET MVC использует стиль code-first. Это означает, что вы можете приступить к хранению и извлечению информации из БД, не создавая самой БД. Вместо этого вы пишете классы C#, а EF выясняет, как и где хранить экземпляры этих классов.

Привязка модели
Привязка модели — это процесс создания объектов модели, используя данные, отправленные браузером в HTTP-запросе. Вместо того, чтобы перебирать все значения, отправленные на сервер формой, мы полагаемся на соглашение об именовании и используем привязку модели, которая сделает это за нас. Привязка модели использует компоненты, которые называются провайдерами значений для поиска нужных значений в различных параметрах запроса: маршруте, строке запроса или данных формы. Это позволяет использовать, например, следующий метод действия для запроса HTTP GET:
public ActionResult Edit (int id) { … }
Здесь привязка модели будет искать параметр id в маршруте, например, /Store/Edit/5, либо в строке запроса /Store/Edit/?id=5.
Для запросов HTTP POST можно использовать целый объект модели:
[HttpPost]
public ActionResult Edit (Product product) { … }
Тогда связыватель модели для каждого свойства класса Product будет искать параметр с соответствующим именем в параметрах запроса.
Привязку модели можно вызывать вручную внутри метода действия с помощью методов UpdateModel или TryUpdateModel. Кроме того, проверить успешность связывания можно с помощью статического свойства IsValid класса ModelState:
[HttpPost]
public ActionResult Edit() {
var album = new Album();
TryUpdateModel(album);
if (ModelState.IsValid) {
// Успех. Обрабатываем модель
}
else {
// Неудача. Сообщаем пользователю об ошибке
}
}

Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 4.
День триста шестьдесят первый. #DesignPatterns
Паттерны проектирования
10. Паттерн «Одиночка» (Singleton). Начало
По определению паттерн «Одиночка» гарантирует, что у некоего класса есть лишь один экземпляр. В некоторых случаях анализ предметной области строго требует, чтобы класс существовал лишь в одном экземпляре. Однако на практике «Синглтон» обычно используется для обеспечения доступа к какому-либо ресурсу, который требуется разным частям приложения.

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

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

Классическая диаграмма приведена на рисунке ниже.

В оригинальном описании паттерна «Синглтон» «бандой четырех» на его реализацию не накладывались никакие ограничения, однако на практике любая реализация должна отвечать двум требованиям:
- в многопоточной среде должна обеспечиваться возможность доступа к синглтону;
- должна обеспечиваться «ленивость» создания синглтона.

Реализация на основе Lazy<T> (.Net 4.0+):
public sealed class LazySingleton
{
private static readonly Lazy<LazySingleton>
_instance =
new Lazy<LazySingleton>(
() => new LazySingleton());

LazySingleton() { }

public static LazySingleton Instance {
get { return _instance.Value; }
}
}

Главными недостатками синглтонов считаются невозможность юнит-тестирования классов, которые пользуются услугами синглтона, и низкая гибкость. Но при небольшой модификации это можно исправить. Основная суть такой модификации состоит в том, что статическое свойство Instance вместо возврата конкретного класса возвращает экземпляр абстрактного класса или интерфейса. Также появляется мутатор (set) свойства, который позволяет установить нужный экземпляр синглтона при старте приложения, во время смены контекста или во время инициализации юнит-тестов. В результате решение будет более гибким и тестируемым.

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 8.
День триста шестьдесят второй. #DesignPatterns
Паттерны проектирования
10. Паттерн «Одиночка» (Singleton). Окончание
«Синглтон» — это самый критикуемый паттерн, описанный «бандой четырех», главный недостаток которого кроется в его определении: синглтон гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.

Проблема № 1: «Синглтон гарантирует, что у класса есть только один экземпляр…»
На самом деле бизнес-логика очень редко накладывает жесткие ограничения на количество экземпляров класса. Обычно это наши с вами уловки и попытки оправдать ошибки дизайна: легче связать несколько кусков системы с помощью синглтонов, вместо того чтобы изменить дизайн и передать классам лишь нужные зависимости.

Проблема № 2: «… и предоставляет глобальную точку доступа»
Главная проблема паттерна «Синглтон» заключается в том, что он является глобальной переменной со всеми её недостатками.
- Необходимость конструктора по умолчанию (для ленивой реализации). Это значит, что синглтону нельзя передать требуемые зависимости и он будет использовать другие глобальные объекты. В результате легко прийти к дизайну приложения, состоящего из набора глобальных объектов.
- Неявные зависимости. Если класс имеет много открытых полей или его конструктор принимает слишком большое число аргументов, то он считается сложным, и с его дизайном что-то не так. Но что, если класс не содержит полей и не принимает никаких зависимостей через конструктор, но использует несколько синглтонов? Тогда, чтобы понять его сложность, придется проанализировать все закрытые методы.
- Изменяемое состояние. Это является источником очень коварных ошибок. Внесение изменений в одну часть системы может изменить работу произвольного числа модулей, у которых были определенные предположения относительно состояния синглтона. Синглтон может обладать невидимым состоянием, например кэшированием, но полноценной изменяемости нужно избегать всеми силами.

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

Советы по использованию
1. Минимальная область использования. Ограничьте использование синглтона минимальным числом классов/модулей. Чем меньше у него прямых пользователей, тем легче будет от него избавиться и перейти на более продуманную модель управления зависимостями. Чем больше у класса пользователей, тем сложнее его изменить. Если вы вынуждены использовать синглтон, то пусть лишь несколько высокоуровневых классов-медиаторов используют его напрямую и передают его экземпляр в качестве зависимостей классам более низкого уровня.
2. Сделайте использование синглтона явным. Если передать зависимость через аргументы конструктора не удается, то сделайте использование синглтона явным. Вместо обращения к синглтону из нескольких методов сделайте статическую переменную и проинициализируйте её экземпляром синглтона.
3. Синглтон или статический класс. Альтернативой паттерну «Синглтон» является использование класса с исключительно статическими членами. Синглтон явно обладает большей гибкостью, но статическими функциями проще пользоваться. Можно предложить следующее правило: при отсутствии состояния и небольшом числе операций используйте статические методы. Если же глобальный объект обладает состоянием, то синглтон будет проще.

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 8.
День триста шестьдесят третий. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
21. Разделяйте Исключения Бизнес-логики от Технических
По сути, есть всего две причины, из-за которых что-то может пойти не так во время выполнения программы. Это технические проблемы, мешающие работе приложения, и бизнес-логика, не дающая нам использовать приложение неправильно. Большинство современных языков, таких как LISP, Java, Smalltalk и C#, используются исключения для сообщения о возникновении обеих ситуаций. Тем не менее, эти ситуации настолько различны, что относиться к ним следует по-разному. Использование для них одной иерархии исключений, не говоря уже об одних и тех же классах исключений, - это потенциальный источник путаницы.

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

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

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

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

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

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Dan Bergh Johnson
День триста шестьдесят четвёртый. #юмор
В опенспейсах лично мне не доводилось работать. Зато дома при наличии детей - это да, очень похоже.
День триста шестьдесят пятый. #NetInternals
2. Как проглотить ThreadAbortException?
Иногда в коде нужно остановить выполнение одного из потоков. Для этого можно использовать метод thread.Abort(). При вызове метода Abort в останавливаемом потоке выбрасывается исключение ThreadAbortException.
Вы можете легко поймать его с помощью блока исключений, но, если вы не сбросите его, оно будет автоматически проброшено выше по стеку.
var thread = new Thread(() => {
try { … }
catch (ThreadAbortException e) {

}
});

thread.Abort();
Если всё-таки нужно обработать ThreadAbort и выполнить еще какие-то действия в останавливаемом потоке, то можно использовать метод Thread.ResetAbort(). Он прекращает процесс остановки потока и исключение перестаёт прокидываться выше по стеку. Важно понимать, что метод thread.Abort() сам по себе ничего не гарантирует — код в останавливаемом потоке может препятствовать остановке.

Еще одна особенность thread.Abort() заключается в том, что он не сможет прервать код в том случае, если он находится в блоках catch и finally. Внутри кода фреймворка часто можно встретить методы, у которых блок try пустой, а вся логика находится внутри finally. Это делается как раз с той целью, чтобы этот код не мог быть прерван через ThreadAbortException.

Также вызов метода thread.Abort() дожидается выброса ThreadAbortException. Объединим эти два факта и получим, что метод thread.Abort() может заблокировать вызывающий поток:
var thread = new Thread(() =>
{
try { }
catch {
//ThreadAbortException не выбрасывается в catch
}
finally {
//ThreadAbortException не выбрасывается в finally
Thread.Sleep(-1);
}
});
thread.Start();

thread.Abort(); //Никогда не вернётся

В реальности с этим можно столкнуться при использовании конструкции using. Она разворачивается в try/finally, внутри finally вызывается метод Dispose. Он может быть сколь угодно сложным, содержать вызовы и обработчики событий, использовать блокировки. И если thread.Abort был вызван во время выполнения Dispose, то thread.Abort будет его ждать. Так мы получаем блокировку почти на пустом месте.

В .NET Core метод thread.Abort() выбрасывает PlatformNotSupportedException. И это очень хорошо, потому что мотивирует пользоваться не thread.Abort(), а неинвазивными методами остановки выполнения кода, например с помощью CancellationToken.

Источники:
- Adam Furmanek «.NET Internals Cookbook» -
https://blog.adamfurmanek.pl/
- Евгений Пешков «Особые исключения в .NET» -
https://youtu.be/WLSrYgMWif4
День триста шестьдесят шестой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
22. Занимайтесь Осознанной Практикой
Осознанная практика – это не просто выполнение работы. Если вы спросите себя «Почему я решаю эту задачу?» и ответом будет «Чтобы её решить», то вы не занимаетесь осознанной практикой.

Вы практикуетесь, чтобы улучшить ваш навык решать задачи. Речь идет о мастерстве и технике. Осознанная практика означает повторение. То есть выполнение задания с целью оттачивания мастерства в том или ином аспекте задания. Повторение уже повторенного. Методично, снова и снова, пока вы не достигнете желаемого уровня мастерства. Вы практикуетесь, чтобы отточить мастерство решения задачи, а не просто решить задачу.

Основная цель оплачиваемой разработки - завершить продукт, в то время как основная цель осознанной практики - повысить производительность. Это не одно и то же. Спросите себя, сколько времени вы тратите на разработку чужого продукта? А сколько на саморазвитие?

Сколько практики требуется, чтобы достичь мастерства? Peter Norvig пишет, что «около 10 000 часов… это магическое число». Mary Poppendieck в «Leading Lean Software Development» заметила, что «Хорошему исполнителю потребуется минимум 10 тысяч часов практики, чтобы стать экспертом».

Навык приходит постепенно, не сразу за 10 000 часов! Тем не менее, 10 000 часов это много: около 20 часов в неделю в течение 10 лет. Увидев эти цифры, можно подумать, что достичь уровня эксперта просто невозможно. Это не так. Уровень во многом зависит от осознанного выбора. Выбор за вами. Исследования последних двух десятилетий показали, что основным фактором приобретения опыта является время, затрачиваемое на осознанную практику. Врожденный талант не является основным фактором. По словам Mary Poppendieck: «Практически все исследователи уровня мастерства сходятся в том, что врожденный талант лишь начало. Необходимо иметь самый базовый уровень, чтобы начать. После чего успеха достигают те, кто больше других работает над этим.»

Нет смысла намеренно практиковать то, в чём вы уже являетесь экспертом. Осознанная практика означает практиковать то, в чем вы не очень хороши.
Peter Norvig: «Ключевой фактор в развитии навыка – практика. Не просто делать что-то снова и снова, но ставить себе задачи чуть выше вашего уровня, пытаться их решать, анализируя свою эффективность и исправляя ошибки.»
Mary Poppendieck: «Осознанная практика не значит делать то, в чем вы сильны, это значит бросать себе вызов, делая то, в чем вы слабы. Это не обязательно удовольствие.»

Осознанная практика - это обучение. Обучение, которое меняет вас, обучение, которое меняет ваше поведение. Удачи.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Jon Jagger
День триста шестьдесят седьмой. #ЗаметкиНаПолях
ASP.NET MVC 5.
HTML Формы
Этот материал может показаться довольно примитивным и не относящимся напрямую к ASP.NET MVC, но, как это ни удивительно, такой простой на первый взгляд элемент HTML, как form, часто используется неверно даже опытными разработчиками.

Атрибуты Action и Method
Форма - это контейнер для элементов ввода: кнопок, флажков, текстовых полей и многого другого. Элементы ввода в форме позволяют пользователю вводить информацию на страницу и отправлять её на сервер. Но как и на какой сервер? Ответы на эти вопросы находятся в двух наиболее важных атрибутах тега form: action и method.

Атрибут action сообщает браузеру, куда отправлять информацию, поэтому содержит URL-адрес. Он может быть относительным, или абсолютным, если вы хотите отправить информацию в другое приложение или на другой сервер. Следующая форма отправляет поисковый запрос (текстовое поле с именем q) на страницу поиска Google из любого приложения:
<form action="https://www.google.com/search">
<input name="q" type="text" />
<input type="submit" value="Search!" />
</form>

Тег формы в предыдущем фрагменте кода не включает атрибут method. Атрибут method сообщает браузеру, какой HTTP метод (POST или GET) использовать при отправке информации. Хотя может показаться, что для отправки информации на сервер значением по умолчанию должно быть post, на самом деле значением по умолчанию является get. То первая строчка кода выше эквивалентна следующей:
<form action="https://www.google.com/search" method="get">


Когда пользователь отправляет форму с помощью HTTP-запроса GET, браузер берёт имена и значения элементов формы и помещает их в строку запроса. Предыдущая форма отправит браузер по следующему URL-адресу (при условии, что пользователь ввёл «dotnet»): https://www.google.com/search?q=dotnet

GET или POST?
Вы также можете присвоить атрибуту method значение post, и в этом случае браузер помещает данные формы в тело HTTP-запроса. И хотя можно успешно отправить POST-запрос в поисковую систему и получить результаты, GET-запрос предпочтительнее. В отличие от POST, вы можете сохранить запрос GET в закладках, потому что все параметры находятся в URL. Вы можете использовать URL-адрес в качестве гиперссылки в электронной почте или на веб-странице и сохранять при этом все значения формы.

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

С другой стороны, POST - это тип запроса, который используется для отправки транзакции по кредитной карте, добавления товара в корзину или изменения пароля. Запрос POST обычно изменяет состояние сервера, и повторение запроса может привести к нежелательным последствиям. Многие браузеры помогают пользователю избежать повторения запроса POST, показывая предупреждающее сообщение. Веб-приложения обычно используют GET-запросы для чтения данных и POST-запросы для записи (обновления, создания или удаления) данных.

Удивительно, но мне буквально недавно пришлось объяснять это старшему коллеге, который решил изменить GET-запрос поиска в старой версии сайта на POST-запрос в новой версии по причине большого (относительно) объёма данных формы и длинного URL запроса.

Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 5.
День триста шестьдесят восьмой. #DesignPatterns
Паттерны проектирования
11. Паттерн «Абстрактная фабрика» (Abstract Factory)
Фабрика — это второй по популярности паттерн после паттерна «Синглтон». Существуют две классические разновидности фабрик: «Абстрактная фабрика» и «Фабричный метод», предназначенные для инкапсуляции создания объекта или семейства объектов. На практике очень часто отходят от классических реализаций этих паттернов и называют фабрикой любой класс, инкапсулирующий в себе создание объектов.

Назначение: абстрактная фабрика предоставляет интерфейс для создания семейства взаимосвязанных или родственных объектов.

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

Классическая диаграмма приведена на рисунке ниже:
- AbstractFactory — объявляет интерфейс (с возможной базовой реализацией) для создания семейства продуктов;
- AbstractProductA, AbstractProductB — семейство продуктов, которые будут использоваться клиентом для выполнения своих задач;
- ProductA1, ProductB1 — конкретные типы продуктов;
- Client — клиент фабрики, который получает конкретные продукты для реализации своего поведения.

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

Одной из разновидностей реализации паттерна «Абстрактная фабрика» является обобщённая фабрика, которая позволяет создавать произвольные типы объектов:
class GenericAbstractFactory {
public object Make(string id) { ... }
public IProduct MakeProduct(string id) { ... }
public T MakeGeneric<T>(string id) where T : IProduct { ... }
}

var factory = new GenericAbstractFactory();
var o = factory.Make("1");
var p = factory.MakeProduct("2");
var t = factory.MakeGeneric<Product>("3");
В данном случае абстрактная фабрика становится конфигурируемой, с её помощью можно создавать объект любого или заданного типа (в данном случае IProduct).

Применимость паттерна
Абстрактная фабрика представляет собой слой для полиморфного создания семейства объектов. Её использование подразумевает обязательное наличие двух составляющих: 1) семейства объектов и 2) возможности замены создаваемого семейства объектов во время исполнения.
Иногда необходимость полноценной абстрактной фабрики очевидна с самого начала, но обычно лучше начать с наиболее простого решения и добавить гибкость лишь в случае необходимости.

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 9.
День триста шестьдесят девятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
HTML Helpers. Начало
HTML helpers (помощники) - это методы, которые вы можете вызывать на свойстве Html представления для генерации HTML-разметки. Их цель - облегчить создание представления. Например, помощник BeginForm можно использовать для создания тега формы для формы поиска (вместо конструкции using можно использовать помощник EndForm, закрывающий форму):
@using (Html.BeginForm("Search", "Home", FormMethod.Get)) {
<input type="text" name="q" />
<input type="submit" value="Search" />
}
Скорее всего будет выведена та же разметка, что и в предыдущем посте. Однако за кулисами помощник координирует свою работу с механизмом маршрутизации для создания правильного URL-адреса, поэтому код более устойчив к изменениям и при развертывании приложения.

Каждое представление Razor наследует свойство Html из своего базового класса. Свойство Html имеет тип System.Web.Mvc.HtmlHelper<T>, где T - это тип модели для представления (по умолчанию dynamic). Однако метод BeginForm, как и большинство других, определён как метод расширения.

Методы расширения используются по двум причинам:
1. Они доступны только тогда, когда пространство имён метода расширения находится в области видимости. Все методы расширения MVC для HtmlHelper находятся в пространстве имен System.Web.Mvc.Html (которое находится в области видимости благодаря ссылке на него в файле Views/web.config). Вы можете удалить его.
2. Вы можете создавать собственные методы расширения для замены или дополнения встроенных помощников.

Помощники существуют для любых HTML элементов, используемых в формах:
- Label – метка label
- BeginForm, EndForm – форма form
- TextBox – поле для ввода input type=”text”
- TextArea – многострочное поле textarea
- CheckBox - флажок input type=”checkbox”
- RadioButton – переключатель input type=”radio”
- DropDownList – выпадающий список select
- ListBox – список с множественным выбором select multiple
- Hidden – скрытое поле input type=”hidden”
- Password – поле пароля input type=”password”

Большинство помощников для полей проверяют наличие значений для отображения в коллекции ViewData (ViewBag). Например, если в методе действия задать:
ViewBag.Price = 10.0;
а в представлении использовать
@Html.TextBox("Price")
то будет выведен следующий HTML код:
<input id="Price" name="Price" type="text" value="10" />
Это также работает со свойствами объектов, помещённых в ViewData:
ViewBag.Product = new Product { Title=”Book”, Price=11 }

@Html.TextBox("Product.Title")
@Html.TextBox("Product.Price")

Строго типизированные помощники
Тем же способом помощники можно использовать в строго типизированных представлениях. Однако для этого больше подойдут строго типизированные помощники. Они отличаются от обычных наличием суффикса For и принимают первым параметром лямбда-выражение для выбора свойства модели. Например, в строго типизированном представлении для отображения класса Product:
@model MyStore.Models.Product
можно использовать строго типизированные помощники:
@Html.TextBox(m => m.Title)
@Html.TextBox(m => m.Price)
Преимуществами использования строго типизированных помощников являются доступность IntelliSense, проверка ошибок во время компиляции и упрощенный рефакторинг (если вы измените имя свойства в вашей модели, Visual Studio может автоматически изменить код в представлении).

Продолжение следует…

Источник: Jon Galloway “Professional
ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 5.
День триста семидесятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
HTML Helpers. Окончание
Помощники и Метаданные модели
Кроме поиска данных внутри ViewData, помощники также используют доступные метаданные модели. Например, если свойство модели пометить атрибутом DisplayName:
public class Product {
[DisplayName("Название")]
public string Title { get; set; }

}
а в представлении использовать помощник
@Html.Label("Title")
то будет выведен следующий код HTML:
<label for="Title">Название</label>
Помощник запрашивает среду выполнения, доступны ли какие-либо метаданные модели для свойства Title, и среда выполнения предоставляет информацию из атрибута DisplayName. Более подробно об атрибутах модели в следующих постах.

Шаблонизированные Помощники
Шаблонизированные помощники строят HTML, используя метаданные и шаблон. Метаданные включают в себя информацию о значении модели (его имя и тип), а также метаданные модели (добавляются с помощью аннотаций или поставщика метаданных). В качестве шаблонизированных помощников выступают Html.Display (Html.DisplayFor) и Html.Editor (Html.EditorFor), а также их аналоги для целой модели Html.DisplayForModel и Html.EditorForModel.
Используя шаблонизированные помощники, вы просите среду выполнения подобрать наиболее подходящий элемент для отображения или редактирования. Например, EditorFor сгенерирует тот же код HTML, что и TextBoxFor. Однако, его можно настроить с помощью аннотаций. Например, следующая аннотация:
[DataType(DataType.MultilineText)]
public string Title { get; set; }
Приведёт к тому, что EditorFor сгенерирует многострочное поле textarea.
Помощники DisplayForModel и EditorForModel создают HTML-код для целой модели. Используя их, вы можете добавлять новые свойства в модель и мгновенно видеть изменения в пользовательском интерфейсе, не внося никаких изменений в представления.

Помощники по Рендерингу
Помощники по рендерингу создают ссылки на другие ресурсы внутри приложения, а также частичные представления.

Html.ActionLink и Html.RouteLink
Метод ActionLink визуализирует гиперссылку на другой метод действия или контроллер. Он использует API маршрутизации для создания URL-адреса.
@Html.ActionLink("Ссылка", "AnotherAction")
Это производит следующую разметку для маршрута по умолчанию:
<a href="/Home/AnotherAction">Ссылка</a>
Помощник RouteLink следует тому же шаблону, но также принимает имя маршрута и не имеет аргументов для контроллера и метода действия:
@Html.RouteLink("Ссылка", new {action="AnotherAction"})

Html.Partial и Html.RenderPartial
Partial визуализирует частичное представление. Как правило, оно содержит общую разметку, которую вы хотите отобразить в нескольких представлениях:
@Html.Partial("ProductDisplay")
Здесь указано только имя представления. Среда выполнения ищет нужное представление, используя все доступные механизмы. Кроме того, методу можно передать модель и коллекцию ViewData.
RenderPartial похож на Partial, но он пишет код непосредственно в выходной поток. По этой причине RenderPartial нужно помещать внутрь блока кода. Следующие две строки приведут к одинаковому выводу в выходной поток:
@{ Html.RenderPartial("ProductDisplay"); }
@Html.Partial("ProductDisplay")

Html.Action и Html.RenderAction
Action и RenderAction аналогичны Partial и RenderPartial. Но, тогда как Partial отображает код из другого представления, Action, выполняет другой метод контроллера и отображает его результат. Action предлагает больше гибкости, потому что метод контроллера может построить другую модель и использовать другой контекст:
[ChildActionOnly]
public ActionResult Menu() {
var menu = GetMenuFromSomewhere();
return PartialView(menu);
}
В представлении:
@Html.Action("Menu")
Обратите внимание, что метод-действие Menu помечен атрибутом ChildActionOnly. Атрибут не позволяет среде выполнения вызывать метод напрямую через URL. Он может быть вызван только через Action или RenderAction. Атрибут ChildActionOnly не требуется, но обычно рекомендуется для дочерних методов-действий.

Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 5.
День триста семьдесят первый. #NetInternals
3. Можно ли использовать методы расширения с dynamic?
Нет. Код можно проверить здесь
using System;
public class Program {
public static void Main() {
int x = 5;
x.Extend();
dynamic y = x;
y.Extend();
}
}

static class Foo {
public static void Extend(this int x) {
Console.WriteLine(x);
}
}

Это потому, что методы расширения компилируются в статические. Код выше фактически компилируется в это:
IL_0004: call void Foo::Extend(int32)

В C# вы могли бы написать его как:
Foo.Extend(x);

Однако DLR (Динамическая Среда Выполнения) ищет методы объекта во время выполнения, поэтому вы не можете использовать расширения с динамическими переменными.

Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
День триста семьдесят второй. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
23. Языки предметной области
Когда вы слушаете дискуссию между экспертами в любой области, будь то шахматисты, воспитатели детских садов или страховые агенты, вы заметите, что их словарный запас сильно отличается от повседневного языка. Это то, что называется «язык предметной области» (Domain Specific Language): набор слов и выражений, описывающий вещи, характерные для данной предметной области.

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

DSL обычно делят на внутренние или внешние.

Внутренние DSL написаны на языке программирования общего назначения, синтаксис которого был изменён, чтобы выглядеть наиболее похожим на естественный язык. Это проще сделать в языках, которые предлагают много синтаксического сахара и возможностей форматирования (например, Ruby и Scala), чем в других (например, Java). Большинство внутренних DSL являются обёртками для существующих API и библиотек и предоставляют оболочку для более понятного доступа к функциональности. Они исполняются напрямую. В зависимости от области и реализации они могут использоваться для построения структур данных, определения зависимостей, запуска процессов или задач, взаимодействия с другими системами или проверки ввода пользователя. Синтаксис внутреннего DSL ограничен языком программирования, в котором он создан. Существует много шаблонов - например, построитель выражений, цепочка методов и аннотации - которые могут помочь вам изменить основной язык под ваш DSL. При участии эксперта в области внутренний DSL может быть разработан довольно быстро.

Внешние DSL - это текстовые или графические представления языка, причём текстовые, как правило, встречаются чаще. Текстовые выражения могут обрабатываться набором инструментов, который включает в себя лексический анализатор, грамматический разбор, преобразователь модели, генераторы и другие виды постобработки. Внешние ЯПО часто преобразуются во внутреннюю модель, представляющую базис для дальнейшей обработки. Хорошо помогает задать грамматику (например, расширенную форму Бэкуса-Наура). Грамматика обеспечивает отправную точку для генерации частей цепочки инструментов (например, редактор, визуализатор, синтаксический анализатор). Для простых DSL может быть достаточно парсера, написанного вручную, например, с помощью регулярных выражений. Пользовательские парсеры могут стать громоздкими, если от них слишком много просят, поэтому имеет смысл взглянуть на инструменты, разработанные специально для работы с языковыми грамматиками и DSL, например openArchitectureWare, ANTLR, SableCC, AndroMDA. Определение внешнего DSL как диалекта XML также довольно распространено, хотя читаемость часто является проблемой, особенно для тех, кто не знаком с XML.

Вы всегда должны принимать во внимание целевую аудиторию вашего DSL: разработчики, менеджеры, бизнес-клиенты или конечные пользователи. Вы должны адаптировать технический уровень языка, доступные инструменты, синтаксическую справку (например, IntelliSense), раннюю проверку, визуализацию и представление для целевой аудитории. Скрывая технические детали, DSL могут расширить возможности пользователей, предоставляя им возможность адаптировать системы к их потребностям без помощи разработчиков. Это также может ускорить разработку из-за потенциального распределения работы после создания исходной языковой структуры. Язык может развиваться постепенно.

Неплохой пример DSL для тестирования описан в этом видео.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Michael Hunger
День триста семьдесят третий. #DesignPatterns
Паттерны проектирования
12. Паттерн «Фабричный метод» (Factory Method)
Когда при обсуждении дизайна упоминается фабрика, то в подавляющем большинстве случаев имеется в виду одна из разновидностей паттерна «Фабричный метод».

Назначение: определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс создавать. Фабричный метод позволяет классу делегировать создание объекта подклассам.

Причины использования:
Иерархии классов обеспечивают гибкость за счет полиморфного использования, но привносят дополнительную сложность. Абстрактная фабрика решает задачу полиморфного создания семейства объектов, но очень часто возникает более простая задача — создание одного экземпляра иерархии наследования.

На практике встречаются три вида паттерна «Фабричный метод», которые приведены на рисунке ниже:
- Creator — объявляет абстрактный или виртуальный метод создания продукта, использует фабричный метод в своей реализации;
- ConcreteCreator — реализует фабричный метод, который возвращает ConcreteProduct;
- Product — определяет интерфейс продуктов, создаваемых фабричным методом;
- ConcreteProduct — определяет конкретный вид продуктов.

1. Классический фабричный метод является частным случаем паттерна «Шаблонный метод», переменный шаг которого отвечает за создание нужного типа объекта. Это значит, что фабричный метод привязан к текущей иерархии типов и не может быть использован повторно в другом контексте.

2. Статический фабричный метод является самой простой версией. Основное отличие его от классического в том, что тип создаваемого объекта определяется не типом наследника, а аргументами, переданными методу. Типичная реализация такого метода содержит оператор switch, в котором происходит выбор создаваемого типа. Вместо этого можно воспользоваться словарём, ключом которого будет некоторый идентификатор (строка, перечисление и т. п.), а значением — делегат, который будет создавать объект нужного класса. Статический метод позволяет обойти ограничения конструкторов. Например, тип создаваемого объекта может зависеть от аргументов метода, экземпляр может возвращаться из кэша, а не создаваться заново или же фабричный метод может быть асинхронным.

3. Полиморфный фабричный метод определяет интерфейс фабрики, а за создание конкретного экземпляра продукта отвечает конкретная фабрика. Является стратегией создания экземпляров некоторого семейства типов, что позволяет использовать одну фабрику в разных контекстах. Тип создаваемого объекта определяется типом фабрики и обычно не зависит от аргументов фабричного метода.

Конструктор vs. фабричный метод
В большинстве случаев конструкторы прекрасно справляются со своей задачей. Но есть две причины использовать фабричный метод вместо конструктора:
1. Именованные конструкторы. В языке C# невозможно использовать два конструктора с одним набором и типом параметров. Хорошим примером такого ограничения является структура Timespan. Очень удобно создавать интервал времени по количеству секунд, минут, часов и дней, но сделать несколько конструкторов, каждый из которых принимает один параметр типа double, невозможно. Для этого структура Timespan содержит набор фабричных методов.
public struct Timespan {
public Timespan(double ticks) {…}
public static Timespan FromMilliseconds(double value) {…}
public static Timespan FromSeconds(double value) {…}
public static Timespan FromMinutes(double value) {…}

}

2. Тяжеловесный процесс создания. Обычно логика конструктора относительно простая, но слишком тяжеловесную логику лучше вынести в статический фабричный метод. Если для конструирования объекта требуется обращение к внешним ресурсам, то лучше сразу же выделять эту логику в фабричный метод. Это позволяет сделать фабричный метод асинхронным, а также упростить эволюцию решения и разбиение данного класса на более мелкие составляющие в случае необходимости.

Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 10.
День триста семьдесят четвёртый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
24. Не бойтесь что-то сломать
Каждому работнику сферы ИТ, несомненно, приходилось работать над проектом, код которого был, мягко говоря, ненадёжным. Система плохо спроектирована, и изменение одной вещи всегда приводило к нарушению другой не связанной функции. Всякий раз, когда добавлялся новый модуль, цель программиста состояла в том, свести изменения к минимуму и молиться при каждом релизе. Это программный эквивалент игры Дженга, где надо извлекать из небоскрёба несущие балки. Рано или поздно такая игра закончится катастрофой.

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

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

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

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Mike Lewis
День триста семьдесят пятый. #Оффтоп
Продолжаю серию постов «попалось мне тут в рекомендациях ютуба». На канале Up and Atom симпатичная девушка (что выгодно отличает этот канал от других 😊) довольно доступно, интересно, и не без доли юмора рассказывает о различных научных темах.
А попалось мне в рекомендациях видео про самую большую неразрешённую проблему в компьютерной науке: «Равенство классов P и NP». Собственно проблема состоит в следующем: если положительный ответ на какой-то вопрос (допустим, верность решения судоку) можно довольно быстро проверить (за полиномиальное время), то правда ли, что ответ на этот вопрос можно довольно быстро найти (также за полиномиальное время и используя полиномиальную память)? Другими словами, действительно ли решение задачи проверить не легче, чем его отыскать?
В видео подробно объясняется, в чём, собственно, проблема, зачем нам это знать и как бы выглядел мир, если бы P было равно NP.

PS: уже стандартное замечание, касающееся чуть менее, чем всех, интересных видео: оно на английском.
День триста семьдесят шестой. #юмор
Да, бывает))) Всем продуктивной рабочей недели.