|
|
@@ -1,3 +1,7 @@
|
|
|
+Предыдущая лекция | | Следующая лекция
|
|
|
+:----------------:|:----------:|:----------------:
|
|
|
+[Реинжиниринг бизнес-процессов в информационных системах.](./5_3_1_8_reengeniring.md) | [Содержание](../readme.md#мдк-0503-тестирование-информационных-систем) | [Создание UNIT-тестов](./5_3_1_10_unit_test.md)
|
|
|
+
|
|
|
# Разработка библиотеки классов
|
|
|
|
|
|
## Техническое задание
|
|
|
@@ -6,12 +10,9 @@
|
|
|
|
|
|
### Общие требования
|
|
|
|
|
|
-В связи со стремительным развитием нашей системы было решено вынести некоторый важный функционал за рамки основного проекта и сделать библиотеку классов, которую мы сможем подключать
|
|
|
-к любому нашему проекту в случае расширения. Данная библиотека будет подключаться к основному проекту и должна быть представлена в виде *.dll/.jar* файла или папки с файлом *.py*.
|
|
|
+В связи со стремительным развитием нашей системы было решено вынести некоторый важный функционал за рамки основного проекта и сделать библиотеку классов, которую мы сможем подключать к любому нашему проекту в случае расширения. Данная библиотека будет подключаться к основному проекту и должна быть представлена в виде `*.dll/.jar*` файла или папки с файлом `*.py*`.
|
|
|
|
|
|
-Чтобы система правильно интегрировалась вам необходимо обязательно следовать правилам именования библиотек, классов и методов в них. В случае ошибок в рамках именования ваша работа не
|
|
|
-может быть проверена и ваш результат не будет зачтен. Классы и методы должны содержать модификатор **public** (если это реализуемо в рамках платформы), чтобы внешние приложения могли
|
|
|
-получить к ним доступ.
|
|
|
+Чтобы система правильно интегрировалась вам необходимо обязательно следовать правилам именования библиотек, классов и методов в них. В случае ошибок в рамках именования ваша работа не может быть проверена и ваш результат не будет зачтен. Классы и методы должны содержать модификатор **public** (если это реализуемо в рамках платформы), чтобы внешние приложения могли получить к ним доступ.
|
|
|
|
|
|
В качестве названия для библиотеки необходимо использовать: **CompanyCoreLib**. Вам необходимо загрузить исходный код проекта с библиотекой в отдельный репозиторий с названием, совпадающим с названием приложения.
|
|
|
|
|
|
@@ -21,9 +22,8 @@
|
|
|
|
|
|
Реализуйте метод, который принимает в себя список объектов даты и времени по совершенным покупкам/заказам в рамках нашей компании, а возвращает список дат (без времени), отсортированный в порядке уменьшения частоты заказов. Это необходимо, чтобы наша компания могла прогнозировать наиболее высокий спрос на следующий год для обеспечения более качественного оказания услуг.
|
|
|
|
|
|
-Возвращаемые данные должны содержать только даты для первого числа каждого месяца и 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. В случае совпадения характеристик популярности сперва нужно вывести более
|
|
|
-ранние месяцы.
|
|
|
+Возвращаемые данные должны содержать только даты для первого числа каждого месяца и `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`. В случае совпадения характеристик популярности сперва нужно вывести более ранние месяцы.
|
|
|
|
|
|
Прогноз строится на основе предыдущего года. ~~Так что данные Вам будут выдаваться строго за предыдущий год.~~ *Данные будут произвольные, поэтому даты не удовлеторяющие этому условию нужно игнорировать.*
|
|
|
|
|
|
@@ -33,34 +33,32 @@
|
|
|
|
|
|
| C#
|
|
|
---|---
|
|
|
-Библиотека классов | CompanyCoreLib.dll
|
|
|
-Название класса | Analytics
|
|
|
-Название метода | PopularMonths()
|
|
|
-Входящие обязательные параметры | List\<DateTime\> dates
|
|
|
-Возвращаемые параметры | List\<DateTime\>
|
|
|
+Библиотека классов | `CompanyCoreLib.dll`
|
|
|
+Название класса | `Analytics`
|
|
|
+Название метода | `PopularMonths()`
|
|
|
+Входящие обязательные параметры | `List<DateTime> dates`
|
|
|
+Возвращаемые параметры | `List<DateTime>`
|
|
|
|
|
|
## Реализация
|
|
|
|
|
|
### Создание библиотеки классов
|
|
|
|
|
|
-Создаем **новый** проект, установив нужные фильтры (*C#*, *Windows*, *Библиотека*) и выбрав проект для соответствующей платформы. **Мы всё делаем для .NET Core**.
|
|
|
+Создаем **новое решение**, выбрав шаблон `.NET / Class Library`.
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
-Не забываем указывать название проекта. Как вы тут напишете, так dll и будет называться.
|
|
|
+Не забываем указывать название проекта по требованиям из ТЗ. Как вы тут напишете, так dll и будет называться.
|
|
|
|
|
|
-
|
|
|
+В итоге IDE создаст нам "рыбу" с одним файлом:
|
|
|
|
|
|
-В итоге студия создаст нам "рыбу" с одним файлом:
|
|
|
+
|
|
|
|
|
|
-```cs
|
|
|
-using System;
|
|
|
|
|
|
+```cs
|
|
|
namespace CompanyCoreLib
|
|
|
+
|
|
|
+public class Class1
|
|
|
{
|
|
|
- public class Class1
|
|
|
- {
|
|
|
- }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -68,16 +66,17 @@ namespace CompanyCoreLib
|
|
|
|
|
|
По ТЗ наш класс должен называться **Analytics** - в *обозревателе решений* переименуйте файл `Class1.cs` в `Analytics.cs`. Система спросит, переименовать ли все названия в проекте - соглашаемся. В итоге наш класс должен выглядеть так:
|
|
|
|
|
|
+
|
|
|
+
|
|
|
```cs
|
|
|
namespace CompanyCoreLib
|
|
|
+
|
|
|
+public class Analytics
|
|
|
{
|
|
|
- public class Analytics
|
|
|
- {
|
|
|
- }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Обратите внимание на модификатор **public** у класса - не во всех версиях *Visual Studio* он ставится автоматически. Добавьте, если его нет.
|
|
|
+Обратите внимание на наличие модификатора **public** у класса - не во всех версиях он ставится автоматически. Добавьте, если его нет.
|
|
|
|
|
|
### Создание метода
|
|
|
|
|
|
@@ -93,21 +92,18 @@ public List<DateTime> PopularMonths(List<DateTime> dates) {
|
|
|
|
|
|
`<Модификатор доступа> <Тип результата, возвращаемого методом> НазваниеМетода(<Тип параметра 1> <НазваниеПараметра>[, <Тип параметра 2> <НазваниеПараметра2>])`
|
|
|
|
|
|
->В ТЗ явно указано, что название параметра **dates**, и хотя это отход от стандарта, оставляем как есть.
|
|
|
-
|
|
|
-**List<DateTime>** - означает **список** объектов типа **DateTime**
|
|
|
+> `List<DateTime>` - означает **список** объектов типа **DateTime**
|
|
|
|
|
|
Для того, чтобы реализовать логику метода нам необходимо:
|
|
|
|
|
|
* перебрать список входных данных (произволные даты)
|
|
|
* отфильтровать даты по условию (только прошлый год)
|
|
|
* сохранить дату в промежуточный список с датой первого числа месяца и количеством повторений этой даты
|
|
|
-* отсортировать промежуточный список по двум условиям: популярность месяца по убываению и, если популярность одинаковая, по месяцу по возрастанию
|
|
|
+* отсортировать промежуточный список по двум условиям: популярность месяца по убыванию и, если популярность одинаковая, по месяцу по возрастанию
|
|
|
|
|
|
Для перебора элементов списка используется цикл **foreach**:
|
|
|
|
|
|
```cs
|
|
|
-// обращаем внимание на название переменных - IterDate (ПеребираемаяДата) пишем CamelCase-ом
|
|
|
foreach (DateTime IterDate in dates) {
|
|
|
...
|
|
|
}
|
|
|
@@ -116,17 +112,19 @@ foreach (DateTime IterDate in dates) {
|
|
|
Для вычисления прошлого года используем статический геттер класса **DateTime** *DateTime.Now*, который возвращает текущую дату и время. Нам остается получить у этого объекта год и вычесть единицу (естественно вычислять эту переменную нужно до цикла):
|
|
|
|
|
|
```cs
|
|
|
-int PreviousYear = DateTime.Now.Year-1;
|
|
|
-foreach (DateTime IterDate in dates) {
|
|
|
- if (IterDate.Year == PreviousYear){
|
|
|
+int previousYear = DateTime.Now.Year-1;
|
|
|
+foreach (DateTime iterDate in dates) {
|
|
|
+ if (iterDate.Year == previousYear){
|
|
|
...
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**Следующий текст устарел! Использовать его можно, но не нужно. Лучше реализовать временный список с использованием кортежей. Это покажет ваше владение средствами языка и уменьшит количество кода/файлов.**
|
|
|
+<details>
|
|
|
|
|
|
-~~Теперь нам нужно объявить список объектов, в котором будет хранится дата и количество повторений этой даты. Для этого создадим класс **DateTimeWithCounter** (вообще, по современным гайдлайнам нужно каждый класс писать в отдельном файле и на демо-экзамене помните про это)~~
|
|
|
+<summary><b>Текст под спойлером устарел! Использовать его можно, но не нужно. Лучше реализовать временный список с использованием кортежей. Это покажет ваше владение средствами языка и уменьшит количество кода/файлов.</b></summary>
|
|
|
+
|
|
|
+Теперь нам нужно объявить список объектов, в котором будет хранится дата и количество повторений этой даты. Для этого создадим класс **DateTimeWithCounter** (вообще, по современным гайдлайнам нужно каждый класс писать в отдельном файле и на демо-экзамене помните про это)
|
|
|
|
|
|
```cs
|
|
|
class DateTimeWithCounter
|
|
|
@@ -174,43 +172,60 @@ public class Analytics
|
|
|
}
|
|
|
```
|
|
|
|
|
|
+</details>
|
|
|
+
|
|
|
+<br/>
|
|
|
+
|
|
|
**Новый код с использованием кортежей**
|
|
|
|
|
|
Теперь нам нужно объявить список объектов, в котором будет хранится дата и количество повторений этой даты. Для этого используем список [кортежей](https://metanit.com/sharp/tutorial/2.19.php)
|
|
|
|
|
|
|
|
|
```cs
|
|
|
-public List<DateTime> PopularMonths(List<DateTime> dates) {
|
|
|
- var DateTimeWithCounterList = new List<Tuple<DateTime, int>>();
|
|
|
+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)
|
|
|
+ int previousYear = DateTime.Now.Year - 1;
|
|
|
+ foreach (DateTime iterDate in dates)
|
|
|
{
|
|
|
- if (IterDate.Year == PreviousYear)
|
|
|
+ if (iterDate.Year == previousYear)
|
|
|
{
|
|
|
// вычисляем начало месяца для текущей даты
|
|
|
- var DateMonthStart = new DateTime(IterDate.Year, IterDate.Month, 1, 0, 0, 0);
|
|
|
+ var dateMonthStart = new DateTime(
|
|
|
+ iterDate.Year, // год
|
|
|
+ iterDate.Month, // месяц
|
|
|
+ 1, 0, 0, 0); // день
|
|
|
|
|
|
// ищем эту дату во временном списке
|
|
|
- var index = DateTimeWithCounterList.FindIndex(item => item.Item1 == DateMonthStart);
|
|
|
+ var index = dateTimeWithCounterList
|
|
|
+ .FindIndex(
|
|
|
+ item => item.Item1 == dateMonthStart);
|
|
|
|
|
|
// кортежи можно создавать по-разному
|
|
|
if (index == -1)
|
|
|
{
|
|
|
// такой даты нет - добавляю (используя конструктор)
|
|
|
- DateTimeWithCounterList.
|
|
|
- Add( new Tuple<DateTime,int>(DateMonthStart, 1) );
|
|
|
+ dateTimeWithCounterList.Add(
|
|
|
+ new Tuple<DateTime,int>(
|
|
|
+ dateMonthStart, 1) );
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // дата есть - увеличиваем счетчик
|
|
|
- // свойства кортежа неизменяемые, поэтому перезаписываем текущий элемент новым кортежем, который создаем статическим методом
|
|
|
- DateTimeWithCounterList[index] = Tuple.Create(DateTimeWithCounterList[index].Item1, DateTimeWithCounterList[index].Item2 + 1);
|
|
|
+ /*
|
|
|
+ дата есть - увеличиваем счетчик
|
|
|
+ свойства кортежа неизменяемые,
|
|
|
+ поэтому перезаписываем текущий элемент
|
|
|
+ новым кортежем,
|
|
|
+ который создаем статическим методом
|
|
|
+ */
|
|
|
+ dateTimeWithCounterList[index] = Tuple.Create(
|
|
|
+ dateTimeWithCounterList[index].Item1, dateTimeWithCounterList[index].Item2 + 1);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return DateTimeWithCounterList
|
|
|
+ return dateTimeWithCounterList
|
|
|
.OrderByDescending(item => item.Item2)
|
|
|
.ThenBy(item => item.Item1)
|
|
|
.Select(item => item.Item1)
|
|
|
@@ -218,7 +233,7 @@ public List<DateTime> PopularMonths(List<DateTime> dates) {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Для вычисления начала месяца мы использовали один из конструкторов класса **DateTime(year, month, day, hour, minutes, seconds)**, где год и месяц берем оригиналльные, а день равен "1"
|
|
|
+Для вычисления начала месяца мы использовали один из конструкторов класса **DateTime(year, month, day, hour, minutes, seconds)**, где год и месяц берем оригинальные, а день равен "1"
|
|
|
|
|
|
Метод **FindIndex** ищет **позицию** (индекс) элемента в списке. Если элемент в списке есть, то возвращает целое от "0" и выше, а если нет, то "-1".
|
|
|
|
|
|
@@ -228,14 +243,14 @@ public List<DateTime> PopularMonths(List<DateTime> dates) {
|
|
|
|
|
|
```cs
|
|
|
...
|
|
|
-return DateTimeWithCounterList
|
|
|
+return dateTimeWithCounterList
|
|
|
.OrderByDescending(item => item.Counter)
|
|
|
.ThenBy(item => item.DateTimeProp)
|
|
|
.Select(item => item.DateTimeProp)
|
|
|
.ToList();
|
|
|
```
|
|
|
|
|
|
-**OrderByDescending** - сортирует исходный список по убыванию. В параметрах пишется лямбда функция, где *item* текущий элемент списка, а после "=>" свойство элемента, по которому производится сортировка. У еас по ТЗ первое условие сортировки по убыванию популярности, поэтому используем свойство Counter. Для прямой сортировки (по возрастанию) есть метод *OrderBy*.
|
|
|
+**OrderByDescending** - сортирует исходный список по убыванию. В параметрах пишется лямбда функция, где *item* текущий элемент списка, а после "=>" свойство элемента, по которому производится сортировка. У нас по ТЗ первое условие сортировки по убыванию популярности, поэтому используем свойство Counter. Для прямой сортировки (по возрастанию) есть метод *OrderBy*.
|
|
|
|
|
|
**ThenBy** (ЗатемПо) - при совпадающих свойствах *популярность* сортировка будет производиться по полю *дата*. У метода *ThenBy* есть пара *ThenByDescending*. Методов *ThenBy* после *OrderBy* может быть несколько.
|
|
|
|
|
|
@@ -251,47 +266,57 @@ return DateTimeWithCounterList
|
|
|
|
|
|
В контекстном меню решения добавьте новый проект
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
-Обратите внимание, тип приложения (Framework или Core) должен совпадать
|
|
|
+Обратите внимание, тип приложения (Framework или Core) должен совпадать с типом библиотеки
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
В созданном приложении к контекстном меню "Зависимостей" добавьте ссылку на проект
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
Теперь в коде консольного приложения мы можем использовать класс из библиотеки:
|
|
|
|
|
|
```cs
|
|
|
-namespace ConsoleApp1
|
|
|
-{
|
|
|
- class Program
|
|
|
- {
|
|
|
- static void Main(string[] args)
|
|
|
- {
|
|
|
- // создаём экземпляр класса аналитики
|
|
|
- var analytics = new Analytics();
|
|
|
+using CompanyCoreLib;
|
|
|
|
|
|
- // подгатавливаем массив тестовых данных
|
|
|
- var srcDates = new List<DateTime>()
|
|
|
- {
|
|
|
- new DateTime(2022,12,1,0,0,0),
|
|
|
- new DateTime(2022,11,1,0,0,0),
|
|
|
- };
|
|
|
+// создаём экземпляр класса аналитики
|
|
|
+var analytics = new Analytics();
|
|
|
|
|
|
- // выполняем метод и получаем результат
|
|
|
- var outDates = analytics.PopularMonths(srcDates);
|
|
|
+// подгатавливаем массив тестовых данных
|
|
|
+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`
|
|
|
+* Результат оформить в виде репозитория (как написано в ТЗ). Обязательно с файлом `readme.md`
|
|
|
+
|
|
|
+Предыдущая лекция | | Следующая лекция
|
|
|
+:----------------:|:----------:|:----------------:
|
|
|
+[Реинжиниринг бизнес-процессов в информационных системах.](./5_3_1_8_reengeniring.md) | [Содержание](../readme.md#мдк-0503-тестирование-информационных-систем) | [Создание UNIT-тестов](./5_3_1_10_unit_test.md)
|