Как стать автором
Обновить

Книга «Паттерны проектирования для C# и платформы .NET Core»

Время на прочтение11 мин
Количество просмотров17K
image Привет, Хаброжители! Паттерны проектирования — удобный прием программирования для решения рутинных задач разработки ПО. Грамотное использование паттернов позволяет добиться соответствия любым требованиям и снизить расходы. В этой книге описаны эффективные способы применения паттернов проектирования с учётом специфики языка C# и платформы .NET Core.

Кроме знакомых паттернов проектирования из книги «Банды четырех» вы изучите основы объектно-ориентированного программирования и принципов 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

Вернитесь к веб-приложению из главы 9, запустите программу Visual Studio и откройте файл FlixOne.sln.

Содержимое панели Solution Explorer (Обозреватель решений) должно выглядеть примерно так, как показано на рис. 10.17.

image

Разверните папку 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

Для начала обсудим метод 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

Следующий код используется для подписки на 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

Здесь представлена простая страница листинга, на которой показаны данные из класса 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

Цель данного раздела — познакомить вас с реактивными расширениями, но не углубляться в их внутреннее развитие. Они находятся под лицензией 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 высылается электронная книга.
Теги:
Хабы:
+5
Комментарии5

Публикации

Информация

Сайт
piter.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия