Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Холстинин Егор #208

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open

Conversation

Yrwlcm
Copy link

@Yrwlcm Yrwlcm commented Dec 3, 2023

Comment on lines 16 to 26
public CircularCloudLayouter(Point center)
{
CenterPoint = center;
spiralGenerator = new SpiralGenerator(center);
}

public CircularCloudLayouter(Point center, int radiusDelta, double angleDelta)
{
CenterPoint = center;
spiralGenerator = new SpiralGenerator(center, radiusDelta, angleDelta);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай посмотрим на это с точки зрения связности кода:

  1. В обоих конструкторах мы жестко завязались на генерацию точек исключительно по спирали, а что, если мы захотим генерировать точки каким-нибудь другим алгоритмом? Как много изменений придется внести в код, чтобы подключить другой алгоритм? (Вспоминаем O из SOLID'a) Можно ли это побороть?

  2. Второй конструктор используется только в тесте, но при этом существует в основном коде, дополнительно нагружая его собой. В продакшене лучше несколько раз подумать, чем втащить в и без того сложный код ещё больше сложности, которая не участвует в основной логике. Но возможно его и так не станет, когда ты сделаешь 1 пункт)

if (rectangleSize.Width < 0 || rectangleSize.Height < 0)
throw new ArgumentException("Rectangle can't have negative width or height");

// Необходимо чтобы значительно увеличить плотность, очень сильно жертвуем производительностью
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Безусловно, при чтении кода в IDE такие комментарии помогают понять ход твоих мыслей и то, чего ты хотел достичь, они полезны

Просто обычно такие комментарии пишутся прямо в PR в комментариях (которые я пишу сейчас, например, тебе). Потому что нужны они только ревьюеру и только в конкретном PR. В коде такое держать точно нельзя

А ещё потому, что тебе в любом случае напишут удалить их из кода)) Что собственно и тут нужно сделать (не беспокойся, я все прочитал :D )

{
var nextPoint = spiralGenerator.GetNextPoint();

var locationForRect = new Point(nextPoint.X - rectangleSize.Width / 2,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. В случаях, когда в переменную врываются предлоги, союзы и артикли, по возможности стараемся от них избавиться - либо просто выбрасыванием их из низвания, либо используем инверсию слов, чтобы уменьшить длину переменной без потери смысла - locationForRect = rectLocation

  2. Схожие переменные (характеристики прямоугольника) - rectangleSize и locationForRect называются по-разному - в первом случае ты написал слово полностью, а во втором сократил. Читаемость круче маленького объема)

var locationForRect = new Point(nextPoint.X - rectangleSize.Width / 2,
nextPoint.Y - rectangleSize.Height / 2);

var newRect = new Rectangle(locationForRect, rectangleSize);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогичный комментарий про сокращение названия, жертвуя читаемостью

nextPoint.Y - rectangleSize.Height / 2);

var newRect = new Rectangle(locationForRect, rectangleSize);
if (createdRectangles.Any(rectangle => rectangle.IntersectsWith(newRect))) continue;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как считаешь, насколько оптимально для каждой новой точки заново обсчитывать абсолютно все уже расположенные прямоугольники?
На маленьких данных понятное дело мы это не заметим, а если захотим располагать миллионы фигур?

{
var centerPoint = new Point(centerX, centerY);
var spiral = new SpiralGenerator(centerPoint, radiusLambda, angleLambda);
spiral.GetNextPoint().Should().NotBeNull().And.Be(new Point(centerX, centerY));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Первое условие не нужно, ведь второе условие и так подразумевает, что точка не null

Comment on lines 60 to 65
(new Point(9, 6), 0),
(new Point(8, 9), Math.PI/4),
(new Point(5, 10), Math.PI/2),
(new Point(2, 9), Math.PI*3/4),
(new Point(1, 6), Math.PI),
(new Point(2, 3), Math.PI*5/4),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Форматирование чуть поехало - цифры слиплись

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Понимаю, что это не входило в рамки задания, но вкину немного продакнш практик. Тесты и основной код обычно делятся на два отдельных проекта - основной код, который раскатается потом на прод, и тесты - которые отработают на этапе тестирования и всё.

Так сборка основного кода не получит лишнюю нагрузку в виде компиляции и рестора зависимостей тестов и будет чуть быстрее собираться, что может значительно повлиять на CI/CD пайплайн

К чему я это - давай и тут сделаем так же)

После разделения проверь пакеты - чтобы ничего лишнего не попало в тот или иной проект

[TestCase(-1, 0, TestName = "Negative width")]
[TestCase(0, -1, TestName = "Negative height")]
[TestCase(-5, -5, TestName = "Negative width and height")]
public void PutNextRectangle_ThrowsArgumentException_WhenNegativeParameters(int rectWidth, int rectHeight)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Конвент именования немного потерял в этом тесте

using System.Threading.Tasks;

namespace TagsCloudVisualization
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошая практика - использовать file-scoped неймспейсы, лишяя вложенность ни к чему, без нее код становится проще читать.

Давай пройдемся по всем файлам в проекте и сделаем это (в отдельном коммите)

nextPoint.Y - rectangleSize.Height / 2);

var newRectangle = new Rectangle(rectangleLocation, rectangleSize);
if (createdRectangles.Any(rectangle => rectangle.IntersectsWith(newRectangle))) continue;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вроде зарефакторил все из запрошенного, кроме этого. Особенно в условиях DI не приходит в голову как это можно оптимизировать, хоть и понимаю, что потенциально убийственная строчка.

var minimalDistanceToClosestRectangle = rectanglesWithoutCurrent
.Min(existingRectangle => CalculateDistanceBetweenRectangles(currentRectangle, existingRectangle));

minimalDistanceToClosestRectangle.Should().BeLessThan(2 * squareSide);
Copy link
Author

@Yrwlcm Yrwlcm Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут дистанция взята из головы, по схождению звезд и с учетом гороскопа. Может конечно какую-то более оптимальную можно подобрать, но лень

Comment on lines 12 to 17
var bitmap = new Bitmap(imageWidth, imageHeight);

var graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.Wheat);

var blackPen = new Pen(Color.Black);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все ли ресурсы освобождены в этих трех объектах?

graphics.DrawRectangles(blackPen, offsettedRectangles);
bitmap.Save(filePath + @$"\{fileName}.png", ImageFormat.Png);

//Console.WriteLine($"Image is saved to {filePath}" + @$"\{fileName}.png");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я писал в том плане, чтобы прям раскомментить её и оставить в качестве логирования


public class SpiralGenerator : IPointGenerator
{
private readonly Point center = new(0, 0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас пропала возможность задать центр спирали (из условий задачи)

Comment on lines 11 to 14
private int radius;
private double angle;
private const int RadiusDelta = 1;
private const double AngleDelta = Math.PI / 60;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зависит от конвентов в команде конечно, но обычно вот такие простые поля (не считая констант) делаются свойствами, так удобнее регулировать правила доступа, которые в будущем могут измениться

Приватными полями делаются зависимости классов (как у тебя сделано с IPointGenerator в лейаутере)

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
Copy link

@desolver desolver Dec 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Какая-то жесть пролезла во фреймворк (во втором проекте тоже)

</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.2.0" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот эта штука нужна для проверки покрытия кода тестами, по идее нужна только для проекта с тестами, тут можно дропнуть

rectangleCreation.Should().Throw<ArgumentException>();
}

private static IPointGenerator[] Generators =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Приватные методы должны стоять в классе после публичных

new SpiralGenerator()
};

[TestCaseSource(nameof(Generators))]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Совсем не факт, что при появлении нового генератора точек этот тест один-в-один сможет проходить с новой реализацией. Скорее всего будет написан отдельный тест с отдельными правилами.

Сейчас мы можем лишь предположить, что может измениться, если вдруг поменяется раскладка - самый яркий параметр тот, который проверяется в .Should, ты как раз о нем писал https://github.com/kontur-courses/tdd/pull/208/files#r1416175977

Давай прокинем его в аргументы теста, мотивацию я думаю ты понял

.Min(existingRectangle => CalculateDistanceBetweenRectangles(currentRectangle, existingRectangle));

minimalDistanceToClosestRectangle.Should().BeLessThan(2 * squareSide);
rectanglesWithoutCurrent.Add(currentRectangle);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не находишь эту строчку странной с точки зрения нейминга?)


public class CloudLayouter
{
private readonly IPointGenerator pointsGenerator;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не критично совсем, но в конструкторе ты принимаешь pointGenerator, а поле в классе называется pointS Generator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants