| Предыдущая лекция | Следующая лекция | |
|---|---|---|
| Реинжиниринг бизнес-процессов в информационных системах. | Содержание | Создание UNIT-тестов |
Задание взято из демо-экзамена
В связи со стремительным развитием нашей системы было решено вынести некоторый важный функционал за рамки основного проекта и сделать библиотеку классов, которую мы сможем подключать к любому нашему проекту в случае расширения. Данная библиотека будет подключаться к основному проекту и должна быть представлена в виде *.dll/.jar* файла или папки с файлом *.py*.
Чтобы система правильно интегрировалась вам необходимо обязательно следовать правилам именования библиотек, классов и методов в них. В случае ошибок в рамках именования ваша работа не может быть проверена и ваш результат не будет зачтен. Классы и методы должны содержать модификатор public (если это реализуемо в рамках платформы), чтобы внешние приложения могли получить к ним доступ.
В качестве названия для библиотеки необходимо использовать: CompanyCoreLib. Вам необходимо загрузить исходный код проекта с библиотекой в отдельный репозиторий с названием, совпадающим с названием приложения.
Вам необходимо создать класс с названием Analytics, который будет позволять проводить аналитику различных процессов в рамках компании.
Реализуйте метод, который принимает в себя список объектов даты и времени по совершенным покупкам/заказам в рамках нашей компании, а возвращает список дат (без времени), отсортированный в порядке уменьшения частоты заказов. Это необходимо, чтобы наша компания могла прогнозировать наиболее высокий спрос на следующий год для обеспечения более качественного оказания услуг.
Возвращаемые данные должны содержать только даты для первого числа каждого месяца и 00:00 минут.
Например, вам поступили следующие данные: 2019-12-12 14:43, 2019-12-01 15:05, 2019-11-04 09:01, а, значит, самый популярный месяц - декабрь. Вам необходимо вернуть следующие данные: 2019-12-01 00:00, 2019-11-01 00:00. В случае совпадения характеристик популярности сперва нужно вывести более ранние месяцы.
Прогноз строится на основе предыдущего года. Так что данные Вам будут выдаваться строго за предыдущий год. Данные будут произвольные, поэтому даты не удовлеторяющие этому условию нужно игнорировать.
Метод должен принимать список объектов даты и времени, а возвращать список дат (без времени).
| C# | |
|---|---|
| Библиотека классов | CompanyCoreLib.dll |
| Название класса | Analytics |
| Название метода | PopularMonths() |
| Входящие обязательные параметры | List<DateTime> dates |
| Возвращаемые параметры | List<DateTime> |
Создаем новое решение, выбрав шаблон .NET / Class Library.
Не забываем указывать название проекта по требованиям из ТЗ. Как вы тут напишете, так dll и будет называться.
В итоге IDE создаст нам "рыбу" с одним файлом:
namespace CompanyCoreLib
public class Class1
{
}
По ТЗ наш класс должен называться Analytics - в обозревателе решений переименуйте файл Class1.cs в Analytics.cs. Система спросит, переименовать ли все названия в проекте - соглашаемся. В итоге наш класс должен выглядеть так:
namespace CompanyCoreLib
public class Analytics
{
}
Обратите внимание на наличие модификатора public у класса - не во всех версиях он ставится автоматически. Добавьте, если его нет.
В спецификации задано и название метода, его параметры и возвращаемый результат - запишем (не забывая про модификатор доступа public):
public List<DateTime> PopularMonths(List<DateTime> dates) {
return dates;
}
Напомню синтаксис объявления метода:
<Модификатор доступа> <Тип результата, возвращаемого методом> НазваниеМетода(<Тип параметра 1> <НазваниеПараметра>[, <Тип параметра 2> <НазваниеПараметра2>])
List<DateTime>- означает список объектов типа DateTime
Для того, чтобы реализовать логику метода нам необходимо:
Для перебора элементов списка используется цикл foreach:
foreach (DateTime IterDate in dates) {
...
}
Для вычисления прошлого года используем статический геттер класса DateTime DateTime.Now, который возвращает текущую дату и время. Нам остается получить у этого объекта год и вычесть единицу (естественно вычислять эту переменную нужно до цикла):
int previousYear = DateTime.Now.Year-1;
foreach (DateTime iterDate in dates) {
if (iterDate.Year == previousYear){
...
}
}
Новый код с использованием кортежей
Теперь нам нужно объявить список объектов, в котором будет хранится дата и количество повторений этой даты. Для этого используем список кортежей
public List<DateTime> PopularMonths(List<DateTime> dates)
{
var dateTimeWithCounterList = new List<Tuple<DateTime, int>>();
int previousYear = DateTime.Now.Year - 1;
foreach (DateTime iterDate in dates)
{
if (iterDate.Year == previousYear)
{
// вычисляем начало месяца для текущей даты
var dateMonthStart = new DateTime(
iterDate.Year, // год
iterDate.Month, // месяц
1, 0, 0, 0); // день
// ищем эту дату во временном списке
var index = dateTimeWithCounterList
.FindIndex(
item => item.Item1 == dateMonthStart);
// кортежи можно создавать по-разному
if (index == -1)
{
// такой даты нет - добавляю (используя конструктор)
dateTimeWithCounterList.Add(
new Tuple<DateTime,int>(
dateMonthStart, 1) );
}
else
{
/*
дата есть - увеличиваем счетчик
свойства кортежа неизменяемые,
поэтому перезаписываем текущий элемент
новым кортежем,
который создаем статическим методом
*/
dateTimeWithCounterList[index] = Tuple.Create(
dateTimeWithCounterList[index].Item1, dateTimeWithCounterList[index].Item2 + 1);
}
}
}
return dateTimeWithCounterList
.OrderByDescending(item => item.Item2)
.ThenBy(item => item.Item1)
.Select(item => item.Item1)
.ToList();
}
Для вычисления начала месяца мы использовали один из конструкторов класса DateTime(year, month, day, hour, minutes, seconds), где год и месяц берем оригинальные, а день равен "1"
Метод FindIndex ищет позицию (индекс) элемента в списке. Если элемент в списке есть, то возвращает целое от "0" и выше, а если нет, то "-1".
А дальше мы либо добавляем новую дату во временный список, либо увеличиваем счетчик у найденной даты.
Осталось только вернуть отсортированный результат. Для этого в C# есть очень мощный инструмент - LINQ.
...
return dateTimeWithCounterList
.OrderByDescending(item => item.Counter)
.ThenBy(item => item.DateTimeProp)
.Select(item => item.DateTimeProp)
.ToList();
OrderByDescending - сортирует исходный список по убыванию. В параметрах пишется лямбда функция, где item текущий элемент списка, а после "=>" свойство элемента, по которому производится сортировка. У нас по ТЗ первое условие сортировки по убыванию популярности, поэтому используем свойство Counter. Для прямой сортировки (по возрастанию) есть метод OrderBy.
ThenBy (ЗатемПо) - при совпадающих свойствах популярность сортировка будет производиться по полю дата. У метода ThenBy есть пара ThenByDescending. Методов ThenBy после OrderBy может быть несколько.
Select - выбирает из объекта нужные свойства. Нам из этого объекта нужна только дата. (Если нужно выбрать несколько объектов, то они перечисляются через запятую: Select(item => item.Prop1, item.Prop2, item.Prop1 + item.Prop2))
ToList преобразует полученный после сортировки объект (IEnumerable) в список, который и возвращается методом.
Библиотека классов не может работать сама по-себе. Нужна программа, которая будет использовать реализованные в ней классы.
В следующей лекции мы рассмотрим как создавать UNIT-тесты, а пока создадим простое консольное приложение, к которому подключим библиотеку.
В контекстном меню решения добавьте новый проект
Обратите внимание, тип приложения (Framework или Core) должен совпадать с типом библиотеки
В созданном приложении к контекстном меню "Зависимостей" добавьте ссылку на проект
Теперь в коде консольного приложения мы можем использовать класс из библиотеки:
using CompanyCoreLib;
// создаём экземпляр класса аналитики
var analytics = new Analytics();
// подгатавливаем массив тестовых данных
var srcDates = new List<DateTime>()
{
new DateTime(2022,12,1,0,0,0),
new DateTime(2022,11,1,0,0,0),
};
// выполняем метод и получаем результат
var outDates = analytics.PopularMonths(srcDates);
// выводим результат в консоль
foreach (var date in outDates)
{
Console.WriteLine(date.ToString());
}
У меня получилось так:
01.11.2023 00:00:00
01.12.2023 00:00:00
readme.md| Предыдущая лекция | Следующая лекция | |
|---|---|---|
| Реинжиниринг бизнес-процессов в информационных системах. | Содержание | Создание UNIT-тестов |