День триста пятьдесят первый. #DesignPatterns
Паттерны проектирования
8. Паттерн «Состояние» (State).
Некоторые объекты предметной области могут выполнять разные наборы операций в зависимости от состояния. Паттерн Состояние моделирует конечный автомат с набором допустимых переходов.
Назначение: позволяет объекту варьировать свое поведение в зависимости от внутреннего состояния. Извне создаётся впечатление, что изменился класс объекта. Основная идея в том, что программа может находиться в одном из нескольких состояний, которые всё время сменяют друг друга. Набор этих состояний, а также переходов между ними, предопределён и конечен. Находясь в разных состояниях, программа может по-разному реагировать на одни и те же события, которые происходят с ней.
Причины использования:
Допустим, объект Документ может принимать три состояния: Черновик, Модерация или Опубликован. Для каждого состояния документа метод обработки документа выполняет разные функции. В статусе Черновик он переведёт документ на модерацию (возможно, уведомит модератора о новом задании), в статусе Модерация метод опубликует документ, а в статусе Опубликован не будет делать ничего.
Основная проблема в таком решении проявится в том случае, если в Документ добавить ещё десяток состояний. Каждый метод обработки будет состоять из увесистого условного оператора, перебирающего доступные состояния. Такой код крайне сложно поддерживать. Малейшее изменение логики переходов заставит вас перепроверять работу всех методов, которые содержат условные операторы машины состояний.
Классическая диаграмма приведена на рисунке ниже:
-
-
-
Паттерн Состояние предлагает создать отдельные классы для каждого состояния, в котором может пребывать объект, а затем вынести туда поведение, соответствующее конкретному состоянию. Вместо того, чтобы хранить код всех состояний, объект контекста будет содержать ссылку на один из объектов-состояний и делегировать ему работу, зависящую от состояния. Благодаря тому, что объекты состояний имеют общий интерфейс, контекст сможет делегировать работу состоянию, не привязываясь к его классу. Поведение контекста можно будет изменить в любой момент, подключив к нему другой объект-состояние.
Важное отличие от паттерна Стратегия в том, что и контекст, и сами конкретные состояния могут знать друг о друге и инициировать переходы от одного состояния к другому.
Паттерн «Состояние» в полноценном виде довольно редко применяется на практике. Его применимость определяется сложностью конечного автомата. Чаще всего количество состояний объекта не столь велико, а логика переходов между состояниями не слишком сложна, поэтому её включают в сам объект контекста.
Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
- https://refactoring.guru/ru/design-patterns/state
Паттерны проектирования
8. Паттерн «Состояние» (State).
Некоторые объекты предметной области могут выполнять разные наборы операций в зависимости от состояния. Паттерн Состояние моделирует конечный автомат с набором допустимых переходов.
Назначение: позволяет объекту варьировать свое поведение в зависимости от внутреннего состояния. Извне создаётся впечатление, что изменился класс объекта. Основная идея в том, что программа может находиться в одном из нескольких состояний, которые всё время сменяют друг друга. Набор этих состояний, а также переходов между ними, предопределён и конечен. Находясь в разных состояниях, программа может по-разному реагировать на одни и те же события, которые происходят с ней.
Причины использования:
Допустим, объект Документ может принимать три состояния: Черновик, Модерация или Опубликован. Для каждого состояния документа метод обработки документа выполняет разные функции. В статусе Черновик он переведёт документ на модерацию (возможно, уведомит модератора о новом задании), в статусе Модерация метод опубликует документ, а в статусе Опубликован не будет делать ничего.
Основная проблема в таком решении проявится в том случае, если в Документ добавить ещё десяток состояний. Каждый метод обработки будет состоять из увесистого условного оператора, перебирающего доступные состояния. Такой код крайне сложно поддерживать. Малейшее изменение логики переходов заставит вас перепроверять работу всех методов, которые содержат условные операторы машины состояний.
Классическая диаграмма приведена на рисунке ниже:
-
Context
– делегирует операции по переходу между своими состояниями объектам State
, вызывая методы Handle()
;-
State
- определяет интерфейс состояния;-
ConcreteState1
и ConcreteState2
- конкретные реализации состояний.Паттерн Состояние предлагает создать отдельные классы для каждого состояния, в котором может пребывать объект, а затем вынести туда поведение, соответствующее конкретному состоянию. Вместо того, чтобы хранить код всех состояний, объект контекста будет содержать ссылку на один из объектов-состояний и делегировать ему работу, зависящую от состояния. Благодаря тому, что объекты состояний имеют общий интерфейс, контекст сможет делегировать работу состоянию, не привязываясь к его классу. Поведение контекста можно будет изменить в любой момент, подключив к нему другой объект-состояние.
Важное отличие от паттерна Стратегия в том, что и контекст, и сами конкретные состояния могут знать друг о друге и инициировать переходы от одного состояния к другому.
Паттерн «Состояние» в полноценном виде довольно редко применяется на практике. Его применимость определяется сложностью конечного автомата. Чаще всего количество состояний объекта не столь велико, а логика переходов между состояниями не слишком сложна, поэтому её включают в сам объект контекста.
Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
- https://refactoring.guru/ru/design-patterns/state
День триста пятьдесят третий. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
19. Удобство как Качество
О важности и проблемах разработки хороших API уже было сказано очень много. Сделать первого раза очень сложно, а изменить потом ещё сложнее. В этом создание API похоже на воспитание детей. Большинство опытных программистов пришли к пониманию, что хороший API образует согласованную абстракцию, демонстрирует логичность и симметрию и формирует словарь для выразительного языка общения с ним. Увы, одно лишь знание руководящих принципов влечёт за собой надлежащее поведение. «Есть сладости вредно», ага)))
Вместо того, чтобы ходить вокруг да около, я хочу обратить внимание на конкретную «стратегию» разработки API, с которой я сталкиваюсь снова и снова: «Главный аргумент – удобство». Обычно дизайнерские решения принимаются на основании одной из следующих «гениальных» идей:
- «Я не хочу, чтобы другие классы делали два разных вызова, чтобы сделать одно и то же.»
- «Зачем мне делать другой метод, если он почти такой же, как этот метод? Я просто добавлю обычный параметр-переключатель.»
- «Смотрите, это очень просто: если второй параметр оканчивается на .txt, метод автоматически предполагает, что первый параметр является именем файла, поэтому два метода действительно не нужны.»
Несмотря на то, что такие аргументы могут быть хорошо спланированы и логично обоснованы, они снижают читабельность кода, использующего API. Вызов метода вроде
В таком случае метафора API как человеческого языка может привести нас к лучшим проектным решениям. API должен обеспечивать выразительный язык, который даёт следующему уровню абстракции словарный запас, чтобы задавать необходимые вопросы и получать на них ответы. Это не означает, что он должен предоставлять ровно один метод или глагол для каждого вопроса. Разнообразный словарный запас позволяет нам выразить тонкости в значении. Например, мы предпочтём использовать
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Gregor Hohpe
97 Вещей, Которые Должен Знать Каждый Программист
19. Удобство как Качество
О важности и проблемах разработки хороших API уже было сказано очень много. Сделать первого раза очень сложно, а изменить потом ещё сложнее. В этом создание API похоже на воспитание детей. Большинство опытных программистов пришли к пониманию, что хороший API образует согласованную абстракцию, демонстрирует логичность и симметрию и формирует словарь для выразительного языка общения с ним. Увы, одно лишь знание руководящих принципов влечёт за собой надлежащее поведение. «Есть сладости вредно», ага)))
Вместо того, чтобы ходить вокруг да около, я хочу обратить внимание на конкретную «стратегию» разработки API, с которой я сталкиваюсь снова и снова: «Главный аргумент – удобство». Обычно дизайнерские решения принимаются на основании одной из следующих «гениальных» идей:
- «Я не хочу, чтобы другие классы делали два разных вызова, чтобы сделать одно и то же.»
- «Зачем мне делать другой метод, если он почти такой же, как этот метод? Я просто добавлю обычный параметр-переключатель.»
- «Смотрите, это очень просто: если второй параметр оканчивается на .txt, метод автоматически предполагает, что первый параметр является именем файла, поэтому два метода действительно не нужны.»
Несмотря на то, что такие аргументы могут быть хорошо спланированы и логично обоснованы, они снижают читабельность кода, использующего API. Вызов метода вроде
parser.processNodes(text, false);
не несёт никакого смысла, если не знать о реализации или, по крайней мере, не ознакомиться с документацией. Этот метод, скорее всего, был разработан для удобства писавшего, а не для удобства использующего. На самом деле, фразу: «Я не хочу, чтобы вызывающий код делал два разных вызова» следует читать как «Мне было лень реализовывать два отдельных метода». Нет ничего принципиально плохого в удобстве, если оно призвано стать противоядием от монотонности, корявости или неловкости. Однако, если подумать, противоядия от этих симптомов - это эффективность, последовательность и элегантность, а вовсе не удобство. Предполагается, что API-интерфейсы скрывают сложность, поэтому стоит ожидать, что разработка хорошего API потребует некоторых усилий. Один большой метод, безусловно, может быть более удобным для написания, чем хорошо продуманный набор операций, но будет ли его проще использовать?В таком случае метафора API как человеческого языка может привести нас к лучшим проектным решениям. API должен обеспечивать выразительный язык, который даёт следующему уровню абстракции словарный запас, чтобы задавать необходимые вопросы и получать на них ответы. Это не означает, что он должен предоставлять ровно один метод или глагол для каждого вопроса. Разнообразный словарный запас позволяет нам выразить тонкости в значении. Например, мы предпочтём использовать
run
вместо walk(true)
, даже если эти действия можно рассматривать как по сути одно и то же, просто выполняемое на разных скоростях. Последовательный и хорошо продуманный словарь API обеспечивает выразительный и простой для понимания код на следующем уровне. Что ещё более важно, составляемый словарь позволяет другим программистам использовать API способами, о которых вы, возможно, не задумывались, а это большое удобство для пользователей API! В следующий раз, когда у вас возникнет соблазн объединить несколько вещей в один метод API, помните, что в человеческом языке нет одного слова для ПриберисьВКомнатеСидиТихоСделайУроки
, хотя это было бы очень удобно для такой часто запрашиваемой операции.Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Gregor Hohpe
👍1
День триста пятьдесят четвёртый. #ЗаметкиНаПолях
ASP.NET MVC 5.
Представления. Начало
Большинство методов действия контроллера должны отображать динамическую информацию в формате HTML. После того, как контроллер выполнил соответствующую логику для запрошенного URL, он делегирует отображение результата представлению.
В отличие от файловых веб-платформ сами представления непосредственно недоступны. Нельзя задать в браузере путь к представлению и сделать так, чтобы оно отобразилось. Вместо этого представление всегда отображается контроллером, который передаёт представлению данные для отображения.
В ASP.NET представления — это файлы
Представления, как правило, возвращаются из методов действия в виде объекта
ViewBag и ViewData
Помимо модели, для передачи небольших объёмов данных в представление из метода действия используется специальный словарь
Строго типизированные представления
Если в представление нужно передать модель, то создаётся строго типизированное представление. Оно отличается от обычного указанием в заголовке файла представления типа модели через декларацию
Модели представления
Иногда в представлении нужно отобразить дополнительную информацию, помимо данных из модели домена. Например, в представлении корзины покупок помимо списка товаров нужно отобразить общую сумму покупки и сообщение (допустим, информацию о доставке).
Есть два пути добиться этого: использовать
Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 3.
ASP.NET MVC 5.
Представления. Начало
Большинство методов действия контроллера должны отображать динамическую информацию в формате HTML. После того, как контроллер выполнил соответствующую логику для запрошенного URL, он делегирует отображение результата представлению.
В отличие от файловых веб-платформ сами представления непосредственно недоступны. Нельзя задать в браузере путь к представлению и сделать так, чтобы оно отобразилось. Вместо этого представление всегда отображается контроллером, который передаёт представлению данные для отображения.
В ASP.NET представления — это файлы
.cshtml
, в которых используется C# в разметке Razor. По соглашению представления размещаются в папке Views
, сгруппированы по папкам соответственно контроллеру, к которому они относятся, и называются по имени метода действия. Например, представления, относящиеся к контроллеру HomeController
, размещаются в папке Views/Home
. Представления для метода действия Index()
называется Index.cshtml
, для метода About()
– About.cshtml
. Общие представления, которые используются несколькими контроллерами, а также макеты страниц размещаются в папке Views/Shared
.Представления, как правило, возвращаются из методов действия в виде объекта
ViewResult
. Можно создавать и возвращать объект напрямую, однако обычно используется вспомогательный метод View
:public class HomeController : Controller {Соглашение об именовании помогает системе найти нужное представление в файле
public ActionResult About() {
return View();
}
}
Views/Home/About.cshtml
. Методу View
можно передать строковый параметр с указанием пути к нужному представлению, а также объект модели, данные которой нужно отобразить.ViewBag и ViewData
Помимо модели, для передачи небольших объёмов данных в представление из метода действия используется специальный словарь
ViewData
:ViewData["CurrentTime"] = DateTime.Now;С введением динамических типов в C#4 стало возможно использовать динамическую обёртку
ViewBag
, в котором можно задавать значения как свойства:ViewBag.CurrentTime = DateTime.Now;Этот код эквивалентен предыдущему, и технически ни один вариант не имеет преимущества перед другим. Однако в некоторых случаях
ViewData
может иметь преимущество, например, следующий код не выполнится из-за динамической природы ViewBag
: @Html.TextBox("name", ViewBag.Name)
Здесь нужно либо использовать ViewData["Name"]
, либо приводить значение к нужному типу: (string)ViewBag.Name
.Строго типизированные представления
Если в представление нужно передать модель, то создаётся строго типизированное представление. Оно отличается от обычного указанием в заголовке файла представления типа модели через декларацию
@model
. Следующая декларация объявляет строго типизированное представления для списка товаров:@model IEnumerable<Product>Это позволяет компилятору проверять типы данных модели, в отличие от передачи данных через
ViewBag
/ViewData
, а разработчикам использовать преимущества IntelliSense в представлении.Модели представления
Иногда в представлении нужно отобразить дополнительную информацию, помимо данных из модели домена. Например, в представлении корзины покупок помимо списка товаров нужно отобразить общую сумму покупки и сообщение (допустим, информацию о доставке).
Есть два пути добиться этого: использовать
ViewBag
/ViewData
, либо создать специализированный класс модели представления, содержащий как список продуктов, так и необходимые дополнительные свойства:public class ShoppingCartViewModel {Как правило, лучше использовать второй вариант. Можно рассматривать модель представления как модель, существующую только для передачи информации в представление.
public IEnumerable<Product> Products { get; set; }
public decimal CartTotal { get; set; }
public string Message { get; set; }
}
Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 3.
День триста пятьдесят пятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
Представления. Razor
Механизм представления Razor является механизмом создания представлений по умолчанию. Он предоставляет оптимизированный синтаксис, который сводит к минимуму количество необходимого кода. Cовет по использованию Razor: помните, что он был разработан, чтобы быть простым и интуитивно понятным. Чаще всего вам не нужно беспокоиться о синтаксисе: просто пишите HTML и используйте знак
Выражения
В большинстве случаев не нужно использовать закрывающий тэг:
Razor достаточно умён, чтобы понимать стандартные форматы e-mail адресов:
Это части представления, которые выполняются компилятором. Их заключают в фигурные скобки:
Razor ищет начало HTML тэга для определения окончания кода и начала разметки, однако если нужно вывести текст сразу после блока кода, можно использовать либо специальный тег
Для комментариев на стороне сервера используется конструкция
Поскольку угловые скобки используются как для HTML тегов, так и в обобщённых методах, вызвать обобщённый метод можно, заключив выражение в круглые скобки:
По умолчанию весь код HTML, выводящийся через Razor кодируется для предотвращения XSS атак (
ASP.NET MVC 5.
Представления. Razor
Механизм представления Razor является механизмом создания представлений по умолчанию. Он предоставляет оптимизированный синтаксис, который сводит к минимуму количество необходимого кода. Cовет по использованию Razor: помните, что он был разработан, чтобы быть простым и интуитивно понятным. Чаще всего вам не нужно беспокоиться о синтаксисе: просто пишите HTML и используйте знак
@
, когда нужно вставить какой-то код.Выражения
В большинстве случаев не нужно использовать закрывающий тэг:
<p>Всего @items.Length элементов</p>Razor понимает, что точка, за которой следует набор букв, означает обращение к свойству объекта, а пробел или знак < не являются валидными символами. Однако, если нужно вывести переменную, а за ней точку и текст, можно использовать круглые скобки:
<p>Всего элементов @items.Length</p>
<p>Всего элементов @items.Length.</p>
<p>@(siteName).Com</p>
Также скобки можно использовать для вычисления выражений: <p>@(1+2)</p>
E-mailRazor достаточно умён, чтобы понимать стандартные форматы e-mail адресов:
<span>email@server.com</span>
В более сложных случаях, а также для указания аккаунтов Twitter или Telegram, можно использовать @@
: <p>@@NetDeveloperDiary</p>
Блоки кодаЭто части представления, которые выполняются компилятором. Их заключают в фигурные скобки:
@{Совмещение кода и разметки
int x = 123;
string y = "hello";
}
@foreach (var item in items) {Совмещение кода и текста
<span>@item.Name.</span>
}
Razor ищет начало HTML тэга для определения окончания кода и начала разметки, однако если нужно вывести текст сразу после блока кода, можно использовать либо специальный тег
<text>
, который не выводится на странице, либо конструкцию @:
(она позволяет вывести только одну строку):@if (showMessage) {или
<text>Это простой текст</text>
}
@if (showMessage) {Серверный комментарий
@:Это простой текст
}
Для комментариев на стороне сервера используется конструкция
@* *@
:@* Это многострочный комментарий.Вызов обобщённого метода
@if (showMessage) {
<h1>@ViewBag.Message</h1>
}
Всё это закомментировано.
*@
Поскольку угловые скобки используются как для HTML тегов, так и в обобщённых методах, вызвать обобщённый метод можно, заключив выражение в круглые скобки:
@(Html.SomeMethod<AType>())Кодирование HTML
По умолчанию весь код HTML, выводящийся через Razor кодируется для предотвращения XSS атак (
<
становится <
, >
- >
и т.п.). Поэтому, если вы действительно хотите вывести HTML код, как есть, используйте вспомогательный метод HTML.Raw
:<span>@Html.Raw(model.Message)</span>Внимание: Javascript не кодируется по умолчанию, как HTML. Поэтому любые данные, которые будут использоваться внутри кода Javascript, нужно кодировать с помощью
@Ajax.JavaScriptStringEncode
:<script type="text/javascript">Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 3.
$(“.title”).text('Hello @Ajax.JavaScriptStringEncode(ViewBag.Username)');
</script>
День триста пятьдесят шестой. #DesignPatterns
Паттерны проектирования
9. Паттерн «Цепочка обязанностей» (Chain of Responsibility).
Как и многие другие поведенческие паттерны, Цепочка обязанностей базируется на том, чтобы превратить отдельные поведения в объекты-обработчики. Кроме того, паттерн подразумевает связывание объектов-обработчиков в одну цепь.
Назначение: позволяет избежать привязки отправителя запроса к его получателю, давая шанс обработать запрос нескольким объектам. Связывает объекты-получатели в цепочку и передает запрос вдоль этой цепочки, пока его не обработают. Отправитель запроса знает только о первом обработчике, но не знает, кто в итоге обработает запрос.
Причины использования:
Необходимость обработки пользовательского запроса несколькими независимыми друг от друга методами: аутентификация и авторизация, валидация данных, предотвращение массовых запросов ботами, собственно выполнение запроса и т.п. Каждая новая проверка раздувает код обработки, а для обработки схожих запросов код приходится дублировать.
Решением будет вынести каждую проверку в отдельный класс обработчика, реализующий общий интерфейс, и выстроить эти классы в цепочку обработки.
Классическая диаграмма приведена на рисунке ниже:
-
-
-
Логику обработки можно реализовать по-разному. В примере выше каждый обработчик передавал бы запрос следующему по цепочке только в случае успешной обработки. Например, только в случае успешной авторизации пользователя мы проверяем валидность данных его запроса. В противном случае обработка запроса прекращается.
Но есть и другой подход, при котором обработчики прерывают цепь только когда они могут обработать запрос. В этом случае запрос движется по цепи, пока не найдётся обработчик, который может его обработать. Очень часто такой подход используется для передачи событий, создаваемых классами графического интерфейса в результате взаимодействия с пользователем.
Например, когда пользователь кликает по кнопке, программа выстраивает цепочку из объекта этой кнопки, всех её родительских элементов и общего окна приложения на конце. Событие клика передаётся по этой цепи до тех пор, пока не найдётся объект, способный его обработать.
Важно, чтобы все объекты цепочки имели общий интерфейс. Обычно каждому конкретному обработчику достаточно знать только то, что следующий объект в цепи имеет метод
Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
- https://refactoring.guru/ru/design-patterns/chain-of-responsibility
Паттерны проектирования
9. Паттерн «Цепочка обязанностей» (Chain of Responsibility).
Как и многие другие поведенческие паттерны, Цепочка обязанностей базируется на том, чтобы превратить отдельные поведения в объекты-обработчики. Кроме того, паттерн подразумевает связывание объектов-обработчиков в одну цепь.
Назначение: позволяет избежать привязки отправителя запроса к его получателю, давая шанс обработать запрос нескольким объектам. Связывает объекты-получатели в цепочку и передает запрос вдоль этой цепочки, пока его не обработают. Отправитель запроса знает только о первом обработчике, но не знает, кто в итоге обработает запрос.
Причины использования:
Необходимость обработки пользовательского запроса несколькими независимыми друг от друга методами: аутентификация и авторизация, валидация данных, предотвращение массовых запросов ботами, собственно выполнение запроса и т.п. Каждая новая проверка раздувает код обработки, а для обработки схожих запросов код приходится дублировать.
Решением будет вынести каждую проверку в отдельный класс обработчика, реализующий общий интерфейс, и выстроить эти классы в цепочку обработки.
Классическая диаграмма приведена на рисунке ниже:
-
Client
- отправляет запрос объекту Handler
.-
Handler
- определяет интерфейс для обработки запроса. Также может определять ссылку на следующий обработчик запроса.-
ConcreteHandler1
и ConcreteHandler2
- конкретные обработчики, которые реализуют функционал обработки запроса.Логику обработки можно реализовать по-разному. В примере выше каждый обработчик передавал бы запрос следующему по цепочке только в случае успешной обработки. Например, только в случае успешной авторизации пользователя мы проверяем валидность данных его запроса. В противном случае обработка запроса прекращается.
Но есть и другой подход, при котором обработчики прерывают цепь только когда они могут обработать запрос. В этом случае запрос движется по цепи, пока не найдётся обработчик, который может его обработать. Очень часто такой подход используется для передачи событий, создаваемых классами графического интерфейса в результате взаимодействия с пользователем.
Например, когда пользователь кликает по кнопке, программа выстраивает цепочку из объекта этой кнопки, всех её родительских элементов и общего окна приложения на конце. Событие клика передаётся по этой цепи до тех пор, пока не найдётся объект, способный его обработать.
Важно, чтобы все объекты цепочки имели общий интерфейс. Обычно каждому конкретному обработчику достаточно знать только то, что следующий объект в цепи имеет метод
HandleRequest()
. Благодаря этому связи между объектами цепочки будут более гибкими. Кроме того, можно формировать цепочки на лету из разнообразных объектов, не привязываясь к конкретным классам.Источники:
- Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 7.
- https://refactoring.guru/ru/design-patterns/chain-of-responsibility
День триста пятьдесят седьмой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
20. Развёртывайте как Можно Раньше и Чаще
Отладка процесса развёртывания и установки приложения часто откладывается до конца проекта. В некоторых проектах написание инструментария установки делегируется инженеру по выпуску, который воспринимает задачу как «необходимое зло». Обзоры и демонстрации выполняются в среде, созданной вручную, чтобы убедиться, что всё работает. В результате разработчики не получают никакого опыта развёртывания или поведения приложения в реальных условиях, пока не станет слишком поздно вносить изменения.
Процесс установки/развертывания — это первое, что видит клиент, и чем он проще, тем надёжнее (или, по крайней мере, проще в отладке) производственная среда. Развёрнутое программное обеспечение — это то, что клиент будет использовать. Не обеспечив правильную настройку приложения при развёртывании, вы посеете сомнения в работе вашего приложения у клиентов ещё до того, как они начнут его использовать.
Начиная разработку вашего проекта с процесса установки, вы получите необходимое время для его эволюции по ходу разработки, а также возможность внести изменения в код приложения, чтобы упростить установку. Периодический запуск и тестирование процесса установки в чистой среде также обеспечивает гарантию того, что вы не сделали предположений в коде, основанных на среде разработки или тестирования.
Позднее развёртывание означает, что процесс развёртывания может быть более сложным, чтобы обойти предположения в коде. То, что казалось отличной идеей в IDE, где у вас есть полный контроль над средой, может сделать процесс развёртывания намного более сложным. Лучше знать об возможных проблемах раньше, чем позже.
Хотя «лёгкость развёртывания» вряд ли несёт большую коммерческую ценность на раннем этапе по сравнению с демонстрацией приложения на ноутбуке разработчика, простая истина заключается в том, что пока вы не сможете продемонстрировать своё приложение в целевой среде, вам за него не заплатят. Если ваше обоснование для откладывания процесса развертывания состоит в том, что процесс тривиален, всё равно сделайте это, поскольку стоимость этого будет низка. Если же процесс слишком сложен или имеет слишком неопределён, делайте с процессом развёртывания то же, что вы делаете с кодом приложения: экспериментируйте, оценивайте и изменяйте процесс развёртывания по мере необходимости.
Процесс установки/развёртывания важен для продуктивности ваших клиентов или вашей службы поддержки, поэтому вы должны тестировать и изменять этот процесс по мере необходимости. Мы тестируем и изменяем исходный код на протяжении всего проекта. Развёртывание заслуживает не меньшего внимания.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Steve Berczuk
97 Вещей, Которые Должен Знать Каждый Программист
20. Развёртывайте как Можно Раньше и Чаще
Отладка процесса развёртывания и установки приложения часто откладывается до конца проекта. В некоторых проектах написание инструментария установки делегируется инженеру по выпуску, который воспринимает задачу как «необходимое зло». Обзоры и демонстрации выполняются в среде, созданной вручную, чтобы убедиться, что всё работает. В результате разработчики не получают никакого опыта развёртывания или поведения приложения в реальных условиях, пока не станет слишком поздно вносить изменения.
Процесс установки/развертывания — это первое, что видит клиент, и чем он проще, тем надёжнее (или, по крайней мере, проще в отладке) производственная среда. Развёрнутое программное обеспечение — это то, что клиент будет использовать. Не обеспечив правильную настройку приложения при развёртывании, вы посеете сомнения в работе вашего приложения у клиентов ещё до того, как они начнут его использовать.
Начиная разработку вашего проекта с процесса установки, вы получите необходимое время для его эволюции по ходу разработки, а также возможность внести изменения в код приложения, чтобы упростить установку. Периодический запуск и тестирование процесса установки в чистой среде также обеспечивает гарантию того, что вы не сделали предположений в коде, основанных на среде разработки или тестирования.
Позднее развёртывание означает, что процесс развёртывания может быть более сложным, чтобы обойти предположения в коде. То, что казалось отличной идеей в IDE, где у вас есть полный контроль над средой, может сделать процесс развёртывания намного более сложным. Лучше знать об возможных проблемах раньше, чем позже.
Хотя «лёгкость развёртывания» вряд ли несёт большую коммерческую ценность на раннем этапе по сравнению с демонстрацией приложения на ноутбуке разработчика, простая истина заключается в том, что пока вы не сможете продемонстрировать своё приложение в целевой среде, вам за него не заплатят. Если ваше обоснование для откладывания процесса развертывания состоит в том, что процесс тривиален, всё равно сделайте это, поскольку стоимость этого будет низка. Если же процесс слишком сложен или имеет слишком неопределён, делайте с процессом развёртывания то же, что вы делаете с кодом приложения: экспериментируйте, оценивайте и изменяйте процесс развёртывания по мере необходимости.
Процесс установки/развёртывания важен для продуктивности ваших клиентов или вашей службы поддержки, поэтому вы должны тестировать и изменять этот процесс по мере необходимости. Мы тестируем и изменяем исходный код на протяжении всего проекта. Развёртывание заслуживает не меньшего внимания.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Steve Berczuk
День триста пятьдесят девятый. #NetInternals
Начинаю новую серию постов в стиле вопросы и ответы из книги Адама Фурманека «.NET Internals Cookbook».
От автора: "В этой серии я отвечаю на различные вопросы по .NET. Некоторые из них задают на собеседованиях, некоторые я вижу в Интернете, остальные придуманы. Цель - предоставить краткий ответ со ссылками на источники, если это необходимо. Это ни в коем случае не учебник по .NET, это просто набор полезных ответов, чтобы освежить ваши знания."
1. Что происходит, когда вы выбрасываете что-то, что не наследуется от System.Exception? Изменилось ли это со времён .NET 1?
Во-первых, у вас может возникнуть вопрос: «Как вообще это сделать?» Помимо C# существуют другие языки, работающие в CLR. Одним из них является C++/CLI, который является управляемой версией языка C++. В C++ вы можете выбрасывать что угодно –
Выбрасываемый объект помещается в System.Runtime.CompilerServices.RuntimeWrappedException, поэтому вы всё равно сможете перехватить исключение с помощью обычного блока
Однако до .NET 2 всё было иначе. Выброшенный объект оборачивался, поэтому вы не могли поймать его таким образом. Вам пришлось бы использовать нетипизированную версию блока catch в виде
Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
Начинаю новую серию постов в стиле вопросы и ответы из книги Адама Фурманека «.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После перехода на .NET 2 этот код перестанет компилироваться, потому что последний блок
{
//
}
catch(Exception e)
{
//
}
catch
{
//
}
catch
никогда не будет выполняться. Вы можете изменить код или восстановить старое поведение, используя атрибут RuntimeCompatibilityAttribute.Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
День триста шестидесятый. #ЗаметкиНаПолях
ASP.NET MVC 5.
Модели
Слово модель в разработке ПО означает сотни различных концепций. Даже применительно к MVC можно говорить о моделях бизнес-логики (домена) и модели представления. Далее под моделью понимаются объекты, которые используются для отправки информации в базу данных, выполнения бизнес-логики и даже визуализации в представлении. Другими словами, эти объекты представляют домен, на котором построено приложение, а модели — это объекты, которые вы хотите отображать, сохранять, создавать, обновлять и удалять.
ASP.NET MVC 5 предоставляет ряд инструментов и функций для построения функционала приложения, используя только определение объектов модели. Например, для корзины покупок можно написать простые классы C#
Скаффолдинг
Автоматизированные работы по созданию контроллеров и представлений называются скаффолдингом. Скаффолдинг в 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:
Для запросов HTTP POST можно использовать целый объект модели:
Привязку модели можно вызывать вручную внутри метода действия с помощью методов
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]Тогда связыватель модели для каждого свойства класса Product будет искать параметр с соответствующим именем в параметрах запроса.
public ActionResult Edit (Product product) { … }
Привязку модели можно вызывать вручную внутри метода действия с помощью методов
UpdateModel
или TryUpdateModel
. Кроме того, проверить успешность связывания можно с помощью статического свойства IsValid
класса ModelState
:[HttpPost]Источник: Jon Galloway “Professional ASP.NET MVC 5”. – John Wiley & Sons Inc., 2014. Глава 4.
public ActionResult Edit() {
var album = new Album();
TryUpdateModel(album);
if (ModelState.IsValid) {
// Успех. Обрабатываем модель
}
else {
// Неудача. Сообщаем пользователю об ошибке
}
}
День триста шестьдесят первый. #DesignPatterns
Паттерны проектирования
10. Паттерн «Одиночка» (Singleton). Начало
По определению паттерн «Одиночка» гарантирует, что у некоего класса есть лишь один экземпляр. В некоторых случаях анализ предметной области строго требует, чтобы класс существовал лишь в одном экземпляре. Однако на практике «Синглтон» обычно используется для обеспечения доступа к какому-либо ресурсу, который требуется разным частям приложения.
Назначение: гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. Другими словами, синглтон эмулирует глобальные переменные в объектно-ориентированных языках программирования.
Причины использования:
Практически в любом приложении возникает необходимость в глобальных переменных или объектах с ограниченным числом экземпляров. И самый простой способ решить эту задачу — создать глобальный объект, который будет доступен из любой точки приложения.
Классическая диаграмма приведена на рисунке ниже.
В оригинальном описании паттерна «Синглтон» «бандой четырех» на его реализацию не накладывались никакие ограничения, однако на практике любая реализация должна отвечать двум требованиям:
- в многопоточной среде должна обеспечиваться возможность доступа к синглтону;
- должна обеспечиваться «ленивость» создания синглтона.
Реализация на основе Lazy<T> (.Net 4.0+):
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 8.
Паттерны проектирования
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.
Паттерны проектирования
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
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?
Иногда в коде нужно остановить выполнение одного из потоков. Для этого можно использовать метод
Вы можете легко поймать его с помощью блока исключений, но, если вы не сбросите его, оно будет автоматически проброшено выше по стеку.
Еще одна особенность
Также вызов метода
В .NET Core метод
Источники:
- Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
- Евгений Пешков «Особые исключения в .NET» - https://youtu.be/WLSrYgMWif4
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
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, как
Атрибуты Action и Method
Форма - это контейнер для элементов ввода: кнопок, флажков, текстовых полей и многого другого. Элементы ввода в форме позволяют пользователю вводить информацию на страницу и отправлять её на сервер. Но как и на какой сервер? Ответы на эти вопросы находятся в двух наиболее важных атрибутах тега
Атрибут
Когда пользователь отправляет форму с помощью HTTP-запроса GET, браузер берёт имена и значения элементов формы и помещает их в строку запроса. Предыдущая форма отправит браузер по следующему URL-адресу (при условии, что пользователь ввёл «dotnet»):
Вы также можете присвоить атрибуту
Ещё важнее то, что глагол 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.
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.