![image](https://habrastorage.org/webt/w2/iq/ty/w2iqtymezwkoji62znu8yucfn3s.jpeg)
Кроме знакомых паттернов проектирования из книги «Банды четырех» вы изучите основы объектно-ориентированного программирования и принципов SOLID. Затем узнаете о функциональных, реактивных и конкурентных паттернах, с помощью которых будете работать с потоками и корутинами. Заключительная часть содержит паттерны для работы с микросервисными, бессерверными и облачно-ориентированными приложениями. Вы также узнаете, как сделать выбор архитектуры, например микросервисной или MVC.
Кому подойдет эта книга
Целевая аудитория — разработчики современных приложений. Поскольку книга содержит много кода, чтобы объяснить, как и где используются паттерны, подразумевается наличие у читателя опыта в разработке ПО. Не стоит рассматривать данную книгу как «программирование для чайников», скорее она отвечает на вопрос «Как программировать лучше». Поэтому издание будет полезно начинающим и опытным программистам, архитекторам приложений и проектировщикам.
Вы научитесь
- Повышать гибкость кода, используя принципы SOLID.
- Применять разработку через тестирование (TDD) в ваших проектах на .NET Core.
- Выполнять эффективную миграцию баз данных, обеспечивать долговременное хранение данных и их тестирование.
- Преобразовывать консольное приложение в веб-приложение с помощью подходящего MVP.
- Писать асинхронный, многопоточный и параллельный код.
- Использовать парадигму MVVM и работать с RxJS и AngularJS для управления изменениями в базах данных.
- Откроете для себя возможности микросервисов, бессерверного программирования и облачных вычислений.
Реактивность и интерфейс IObservable
В предыдущем разделе мы обсудили реактивное программирование и рассмотрели его модель. Здесь мы обсудим его реализацию компанией Microsoft. В ответ на реактивное программирование в .NET Core у нас есть различные интерфейсы, которые обеспечивают способ реализации реактивного программирования в нашем приложении.
Интерфейс IObservable<T> — универсальный, определен в пространстве имен System и объявлен как public interface IObservable<out T>. Здесь T представляет универсальный тип параметра, который дает информацию об уведомлении. Если коротко, то этот интерфейс помогает определить поставщика уведомлений и они могут быть отправлены для получения информации. Паттерн «Наблюдатель» можно использовать при реализации интерфейса IObservable<T> в приложении.
Паттерн «Наблюдатель» — реализация с помощью IObservable<T>
Говоря коротко, подписчик регистрируется у издателя (поставщика), чтобы получать уведомления, связанные с информацией о сообщении. Они уведомляют поставщика о том, что сообщения были доставлены подписчикам. Вдобавок данная информация может быть связана с изменениями в операциях или любыми другими изменениями в самом методе или объекте. Это также известно как изменения состояния.
Паттерн «Наблюдатель» определяет два термина: наблюдатель и наблюдаемое. Второе — это поставщик, также известный как субъект. Наблюдатель регистрируется в типах Observable/Subject/Provider и будет автоматически уведомлен поставщиком о любых изменениях, вызванных заранее определенными критериями/условиями, изменением или событием и т. д.
На рис. 10.16 изображено простое представление модели наблюдателя, где субъект уведомляет двух разных наблюдателей.
![image](https://habrastorage.org/webt/6h/rt/-5/6hrt-5mltceo59u-apgyw8qkqks.png)
Вернитесь к веб-приложению из главы 9, запустите программу Visual Studio и откройте файл FlixOne.sln.
Содержимое панели Solution Explorer (Обозреватель решений) должно выглядеть примерно так, как показано на рис. 10.17.
![image](https://habrastorage.org/webt/lg/q3/8h/lgq38hjghkjlad6bszdhle5qiqg.png)
Разверните папку Common на панели Solution Explorer (Обозреватель решений) и добавьте два файла: ProductRecorder.cs и ProductReporter.cs. Они являются реализацией интерфейсов IObservable<T> и IObserver<T>. Нам также нужно добавить новую модель ViewModel, чтобы мы могли сообщать пользователям об актуальных сообщениях. Для этого необходимо открыть папку Models и добавить файл MessageViewModel.cs.
Следующий код показывает наш класс MessageViewModel:
public class MessageViewModel
{
public string MsgId { get; set; }
public bool IsSuccess { get; set; }
public string Message { get; set; }
public override string ToString() => $"Id:{MsgId},
Success:{IsSuccess}, Message:{Message}";
}
Класс MessageViewModel содержит следующее:
- MsgId — уникальный идентификатор;
- IsSuccess — показывает, была ли операция успешной;
- Message — сообщение об успехе или об ошибке, которое зависит от значения IsSuccess;
- ToString() — переопределенный метод, который возвращает строку после объединения всех сведений.
Теперь обсудим два наших класса; следующий код взят из класса ProductRecorder:
public class ProductRecorder : IObservable<Product>
{
private readonly List<IObserver<Product>> _observers;
public ProductRecorder() => _observers = new List<IObserver<Product>>();
public IDisposable Subscribe(IObserver<Product> observer)
{
if (!_observers.Contains(observer))
_observers.Add(observer);
return new Unsubscriber(_observers, observer);
}
...
}
Наш класс ProductRecorder реализует интерфейс IObservable. Если вы вспомните нашу дискуссию о наблюдателе, то узнаете, что этот класс на самом деле является поставщиком, субъектом или наблюдаемым. Интерфейс IObservable<T> имеет метод Subscribe, который мы должны использовать для подписки наших подписчиков или наблюдателей (мы обсудим наблюдателя позже в этом разделе).
Должен существовать критерий или условие, чтобы подписчик мог получать уведомления. В нашем случае метод Record служит этой цели. Рассмотрим следующий код:
public void Record(Product product)
{
var discountRate = product.Discount.FirstOrDefault(x => x.ProductId ==
product.Id)?.DiscountRate;
foreach (var observer in _observers)
{
if (discountRate < 0 || discountRate - 100 > 0)
observer.OnError(
new Exception($"Product:{product.Name} has invalid discount
rate {discountRate}"));
else
observer.OnNext(product);
}
}
Здесь мы видим метод Record. Мы создали его, чтобы продемонстрировать мощь паттерна. Этот метод просто проверяет корректность скидки. Если значение discount rate недопустимо в соответствии с критерием/условием, то данный метод вызовет исключение и передаст название продукта с этим недопустимым значением.
Предыдущий метод проверяет значение discount rate в соответствии с критериями и отправляет уведомление о возникшем исключении подписчику при невыполнении критериев. Взгляните на блок итераций (цикл foreach) и вообразите ситуацию, когда нам нечего повторять, а все подписчики были уведомлены. Можно ли представить, что произойдет в этом случае? Такая же ситуация может возникнуть и для бесконечного цикла. Остановить это поможет нечто завершающее цикл. Для подобных случаев у нас есть метод EndRecording:
public void EndRecording()
{
foreach (var observer in _observers.ToArray())
if (_observers.Contains(observer))
observer.OnCompleted();
_observers.Clear();
}
Наш метод EndRecoding пропускает через цикл коллекцию _observers и запускает метод OnCompleted(). Наконец, она очистила коллекцию _observers.
Теперь обсудим класс ProductReporter. Он является примером реализации интерфейса IObserver<T>. Рассмотрим код:
public void OnCompleted()
{
PrepReportData(true, $"Report has completed: {Name}");
Unsubscribe();
}
public void OnError(Exception error) => PrepReportData(false, $"Error
ocurred with instance: {Name}");
public void OnNext(Product value)
{
var msg =
$"Reporter:{Name}. Product - Name: {value.Name},
Price:{value.Price},Desc: {value.Description}";
PrepReportData(true, msg);
}
В интерфейсе IObserver<T> есть методы OnComplete, OnError, и OnNext, которые мы должны реализовать в классе ProductReporter. Цель метода OnCompleted — уведомить подписчика о выполнении работы и затем отменить его подписку. Кроме того, OnError вызывается при возникновении ошибки во время выполнения, в то время как OnNext предоставляет информацию об очередном элементе в последовательности потока.
В следующем коде метод PrepReportData — дополнение значения, которое предоставляет пользователю форматированный отчет обо всех операциях процесса:
private void PrepReportData(bool isSuccess, string message)
{
var model = new MessageViewModel
{
MsgId = Guid.NewGuid().ToString(),
IsSuccess = isSuccess,
Message = message
};
Reporter.Add(model);
}
Описанный метод — простое добавление модели к нашей коллекции Reporter, которая представляет собой коллекцию классов MessageViewModel. Обратите внимание: в целях упрощения вы также можете использовать метод ToString() в MessageViewModel.
Следующий фрагмент кода показывает методы Subcribe и Unsubscribe:
public virtual void Subscribe(IObservable<Product> provider)
{
if (provider != null)
_unsubscriber = provider.Subscribe(this);
}
private void Unsubscribe() => _unsubscriber.Dispose();
Два предыдущих метода сообщают системе, что провайдер существует. Подписчики могут подписаться на провайдера или отказаться от подписки/избавиться от него после завершения операций.
Теперь пришло время продемонстрировать нашу реализацию и увидеть некоторые результаты. Чтобы сделать это, нам нужно внести некоторые изменения в существующую страницу Product Listing и добавить новую страницу представления в проект. Добавьте ссылку на представление Audit Report (Аудиторский отчет) на нашу страницу Index.cshtml:
<a asp-action="Report">Audit Report</a>
В предыдущем фрагменте кода мы добавили новую ссылку на страницу Audit Report
(Аудиторский отчет), основанную на нашей реализации метода Report Action,
который мы определили в нашем классе ProductConstroller.
После добавления этого кода наша страница Product Listing (Список продуктов) будет выглядеть следующим образом (рис. 10.18).
![image](https://habrastorage.org/webt/gv/xy/zl/gvxyzlssnjla8uhdyiwetbyijdi.png)
Для начала обсудим метод Report action. Для этого рассмотрим следующий код:
var mango = _repositry.GetProduct(new Guid
("09C2599E-652A-4807-A0F8-390A146F459B"));
var apple = _repositry.GetProduct(new Guid
("7AF8C5C2-FA98-42A0-B4E0-6D6A22FC3D52"));
var orange = _repositry.GetProduct(new Guid
("E2A8D6B3-A1F9-46DD-90BD-7F797E5C3986"));
var model = new List<MessageViewModel>();
// провайдер
ProductRecorder productProvider = new ProductRecorder();
// наблюдатель 1
ProductReporter productObserver1 = new ProductReporter(nameof(mango));
// наблюдатель 2
ProductReporter productObserver2 = new ProductReporter(nameof(apple));
// наблюдатель 3
ProductReporter productObserver3 = new ProductReporter(nameof(orange));
В данном коде мы берем только первые три продукта для демонстрационных целей. Обратите внимание: вы можете изменить код в соответствии с собственной реализацией. В коде мы создали класс productProvider и трех наблюдателей для подписки на наш класс productProvider.
На рис. 10.19 наглядно представлены все действия для демонстрации интерфейсов IObservable<T> и IObserver<T>, которые мы обсудили.
![image](https://habrastorage.org/webt/8b/98/sw/8b98swirudomgmaqjtqkghraxjc.png)
Следующий код используется для подписки на productrovider:
// подписка
productObserver1.Subscribe(productProvider);
productObserver2.Subscribe(productProvider);
productObserver3.Subscribe(productProvider);
Наконец, нам нужно записать отчет в журнал и затем отказаться от подписки:
// отчет и отмена подписки
productProvider.Record(mango);
model.AddRange(productObserver1.Reporter);
productObserver1.Unsubscribe();
productProvider.Record(apple);
model.AddRange(productObserver2.Reporter);
productObserver2.Unsubscribe();
productProvider.Record(orange);
model.AddRange(productObserver3.Reporter);
productObserver3.Unsubscribe();
Вернемся к нашему экрану и добавим файл Report.cshtml в Views->Product (Представления->Продукт). Следующий код — часть нашей страницы Report (Отчет). Полный код можно найти в папке Product:
@model IEnumerable<MessageViewModel>
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.IsSuccess)
</th>
<th>
@Html.DisplayNameFor(model => model.Message)
</th>
</tr>
</thead>
Этот код создаст заголовок для столбцов нашей таблицы, в котором будет показан аудиторский отчет.
Следующий код завершит таблицу и добавит значения в IsSuccess и столбцы Message:
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.HiddenFor(modelItem => item.MsgId)
@Html.DisplayFor(modelItem => item.IsSuccess)
</td>
<td>
@Html.DisplayFor(modelItem => item.Message)
</td>
</tr>
}
</tbody>
</table>
На этом мы закончили реализацию паттерна «Наблюдатель» с помощью интерфейсов IObservable<T> и IObserver<T>. Запустите проект в Visual Studio, нажав клавишу F5, щелкните кнопкой мыши на ссылке Product (Продукт) на главной странице, а затем по ссылке Audit Report (Аудиторский отчет). Вы увидите аудиторский отчет по выбранным нами продуктам, как показано на рис. 10.20.
![image](https://habrastorage.org/webt/eh/4f/zt/eh4fztjayj9vkp-mhy5cpdn8nuc.png)
Здесь представлена простая страница листинга, на которой показаны данные из класса MessageViewModel. Вы можете вносить изменения и модифицировать их в соответствии с вашими требованиями. В целом аудиторские отчеты поступают от многих видов оперативной деятельности, которые мы видим на данном экране. Вы также можете сохранить проверенные данные в базе данных, а затем обслуживать их соответствующим образом для различных целей, например для составления отчетов администратору и не только.
Реактивные расширения: .NET Rx Extensions
Обсуждение в предыдущем разделе было направлено на реактивное программирование и его реализацию с помощью интерфейсов IObservable<T> и IObserver<T> в качестве паттерна «Наблюдатель». В этом разделе мы будем развивать наше обучение с помощью Rx Extensions. Если вы хотите узнать больше о данной разработке, то вам следует обратиться к официальному репозиторию по адресу github.com/dotnet/reactive.
Обратите внимание: Rx Extensions теперь объединены с пространством имен System и вы можете найти их все в пространстве имен System.Reactive. Если у вас есть опыт работы с Rx Extensions, то вы должны знать, что пространство имен этих расширений было изменено следующим образом:
- Rx.Main изменено на System.Reactive;
- Rx.Core изменено на System.Reactive.Core;
- интерфейсы Rx.Interfaces изменены на System.Reactive.Interfaces;
- Rx.Linq изменено на System.Reactive.Linq;
- Rx.PlatformServices изменен на System.Reactive.PlatformServices;
- Rx.Testing изменено на Microsoft.Reactive.Testing.
Для начала откройте в Visual Studio проект SimplyReactive (обсуждался в предыдущем разделе) и панель NuGet Package Manager (Менеджер пакетов NuGet). Нажмите кнопку Browse (Поиск) и введите поисковый запрос System.Reactive. Вы увидите следующие результаты (рис. 10.21).
![image](https://habrastorage.org/webt/tt/gc/yo/ttgcyo2htgg4hxp5zglblbq6dag.png)
Цель данного раздела — познакомить вас с реактивными расширениями, но не углубляться в их внутреннее развитие. Они находятся под лицензией Apache2.0 и поддерживаются .NET Foundation. Мы уже реализовали реактивные расширения в приложении SimplyReactive.
Об авторах
Гаурав Арораа получил степень магистра в области компьютерных наук, имеет сертификаты Microsoft MVP, Alibaba Cloud MVP, тренера/коуча по Scrum. Член Компьютерного сообщества Индии (CSI), сотрудник IndiaMentor, сертифицированный специалист ITIL Foundation, APMG PRINCE-F и APMG PRINCE-P. Гаурав — разработчик открытого программного обеспечения, внесший вклад в развитие TechNet Wiki и основавший компанию Ovatic Systems Private Limited. За свою более чем 20-летнюю карьеру он обучил тысячи студентов. Вы можете написать Гаураву в Twitter по адресу @g_arora.
Джеффри Чилберто — консультант в сфере разработки программного обеспечения, специализирующийся на технологическом стеке Microsoft, включая Azure, BizTalk, ASP.NET, MVC, WCF и SQL Server, с опытом работы в различных сферах. Он участвовал в разработке банковских, телекоммуникационных и медицинских систем в Новой Зеландии, Австралии и США. Имеет степень бакалавра в области информационных и компьютерных наук, а также степень магистра по информационным технологиям и вычислительной технике.
О научных редакторах
Сьеки Цааль — консультант по управлению, архитектор облачных систем Microsoft и Microsoft Azure MVP с более чем 15-летним стажем в области создания архитектур, разработки, консультирования и системного дизайна. Она работает в Capgemini — одной из крупнейших в мире консалтинговых компаний в сфере менеджмента и информационных технологий. Сьеки любит делиться знаниями и принимает очень активное участие в сообществе Microsoft как одна из основателей нидерландских пользовательских групп SP&C NL и MixUG. Кроме того, является членом правления Azure Thursdays. Сьеки часто выступает с лекциями и участвует в организации мероприятий. Она написала несколько книг, ведет блоги и принимает активное участие в сообществе Microsoft Tech Community (https://techcommunity.microsoft.com/).
У Эфраима Кирякидиса за плечами более 20 лет опыта в разработке ПО. Он получил диплом инженера в Университете имени Аристотеля в Салониках (Греция). Эфраим пользуется .NET с момента создания, с версии 1.0. В своей работе он в целом сфокусирован на технологиях Microsoft. В данный момент занимает пост старшего инженера в компании Siemens AG в Германии.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Паттерны
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.