-
Notifications
You must be signed in to change notification settings - Fork 201
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
Сибогатов Ринат #177
base: master
Are you sure you want to change the base?
Сибогатов Ринат #177
Conversation
|
||
var currentRectangleResult = CreateNewRectangle(rectangleSize); | ||
|
||
if (currentRectangleResult.IsSuccess) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
можно попробовать сделать более "функционально":
currentRectangleResult
.OnSuccess(() => /*...*/)
.OnFail(() => /*...*/);
|
||
private void ValidateRectangleSize(Size rectangleSize) | ||
{ | ||
if (rectangleSize.Width < MinPositiveValue || rectangleSize.Height < MinPositiveValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, что саму проверку можно зашить в экстеншн, передавая туда MinPositiveValue
для сравнения.
{ | ||
return Math.Max(BaseFontSize, BaseFontSize + frequency * FontSizeMultiplier); | ||
} | ||
return DefaultFontSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Пустую строчку упустил)
{ | ||
var wordFrequencies = new Dictionary<string, int>(); | ||
|
||
foreach (var word in words) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно применить LINQ
- сгруппировать и посчитать
|
||
public Font GetFont() | ||
{ | ||
return new Font("Verdana", 20); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Зачем каждый раз возвращать новый объект шрифта?
Font
реализуетIDisposable
- выше по колстеку это не учитывается
{ | ||
foreach (var word in words) | ||
{ | ||
Console.WriteLine(word); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Зачем выводить слова на консоль?
|
||
private IFileReader GetFileReader(string filePath) | ||
{ | ||
if (Path.GetExtension(filePath).Equals(".docx", StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, что чуть лучше было бы создать словарь
[Test] | ||
public void WithSpecificContent_ReturnsExpectedWords() | ||
{ | ||
var filePath = "src/boring_words.txt"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
подобный хардкод делает тесты весьма хрупкими. Мало ли как всё изменится в процессе рефакторинга? И когда-нибудь попадешь в ситуацию "хотел сделать лучше, а упала половина тестов")
public void Constructor_ShouldNotThrow_WithValidArguments() | ||
{ | ||
Action constructorAction = () => new Spiral(center, 0.1, 0.2); | ||
constructorAction.Should().NotThrow(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В одном случае у тебя было Assert.DoesNotThrow
, тут уже .Should().NotThrow();
. Стоит делать единообразно.
private ITagCloudGenerator tagCloudGenerator; | ||
|
||
[SetUp] | ||
public void SetUp() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SetUp
стоит использовать тогда, когда в нем есть необходимость. Необходимость в нем может появиться тогда, когда каждому тесту нужен новый объект sut
, причем все объекты идентичны по своим параметрам. Если хоть одно правило не выполняется - SetUp
может начать приносить больше боли, чем пользы.
Прочитай, пожалуйста, пункты с 3.2 до 3.4 (ссылку пришлю в телеге)
Кстати, ты уже использовал токен sut
. Почему бы и тут это не сделать?)
private const double DefaultRadiusStep = 0.04; | ||
private const int HalfWidthHeight = 2; | ||
|
||
public string FontName { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Теперь непонятно, зачем тебе _fontName
и FontName
одновременно)
Height = height; | ||
} | ||
|
||
~ImageSettings() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почему именно деструктор, а не реализация типом интерфейса IDisposable
?
{ | ||
var lines = File.ReadAllLines(filePath); | ||
|
||
var words = lines.SelectMany(line => line.Split(' ')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
Если не ошибаюсь, то Split
по умолчанию делит строки по пробелу ' '
graphics.Clear(imageSettings.BackgroundColor); | ||
Font font = imageSettings.GetFont(); | ||
|
||
int OffsetY = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var
?
var word = uniqueWords.ElementAt(i).ToLower(); | ||
|
||
var fontSize = fontSizes[i]; | ||
var font = new Font(fontName, fontSize, FontStyle.Regular); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
using
. Ещё можно подумать над тем, как бы постоянно не выделить память для font
и brush
{ | ||
readerCreators = new Dictionary<string, Func<IFileReader>>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ ".docx", () => new DocxReader() }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: можно подумать над тем, как с FileType
использовать
|
||
foreach (var word in processedWords) | ||
{ | ||
Console.WriteLine(word); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Снова - зачем тут вывод в консоль?
} | ||
else | ||
{ | ||
Console.WriteLine($"Error generating tag cloud image: {tagCloudImageResult.Error}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Всё ещё остался вывод в консоль. Я не думаю, что тут место для класса Console
. Вся прилага автоматически стала привязана к консоли, это не оч хорошо
{ | ||
public class ImageSettings : IImageSettings, IDisposable | ||
{ | ||
private readonly Font font; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Зачем для этого поля сделан самописный геттер?
- Для чего инициализация поля вынесена в конструктор?
Теперь расскажу, в чем суть придирок.
- Ты захламляешь код ненужными деталями. Чтоб этого не делать и были введены автосвойства. Стоит их и использовать.
- Если есть возможность что-то инициализировать сразу в полях - так и надо сделать. Компилятор всё равно подставит инициализацию поля внутрь конструктора. Однако у тебя сразу появится полезная привычка, т.к. иногда конструкторами (если их более одного) злоупотребляют, делая копипаст. Так что мини-совет на все случаи жизни:
а. инициализируй поля на месте объявления, если это возможно
б. не делай копипаст в конструкторах, вместо этого стоит использовать конструкцию: this()
} | ||
} | ||
|
||
private string GetMostPopularWord(Dictionary<string, int> wordFrequencies) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Думаю, можно обойтись Linq, даже не выделяя метод
? Result.Ok(new CircularCloudLayouter(center, spiralResult.Value)) | ||
: Result.Fail<CircularCloudLayouter>($"Error creating CircularCloudLayouter: {spiralResult.Error}"); | ||
} | ||
catch (Exception ex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
На самом деле, обычно рекомендуют указывать минимально допустимый тип исключения, а уже далее по нарастающей, вплоть до базового Exception
. Хотя на практике это делается редко, только если их надо по-разному обрабатывать. Но если не знал, то вот)
{ | ||
try | ||
{ | ||
var points = GeneratePointsOnSpiral().ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Исключительно странная строка) У тебя есть потенциально бесконечный набор точек, создаваемый GeneratePointsOnSpiral
. После этого ты аллоцируешь коллекцию в памяти вызовом ToList
, после чего отдаешь сгенерированный лист как IEnumerable
. В чем суть этого всего?)
Мало того, что аллоцируешь всю коллекцию в памяти - ты это делаешь многократно, т.к. конечный размер коллекции, который выдаст GeneratePointsOnSpiral
неизвестен, а значит создаваемый List
будет расширяться (путем копирования старого листа в новый лист, размер которого в 2 раза больше предыдущего) n раз до тех пор, пока не сможет вместить в себя все точки исходной коллекции.
Ради интереса можешь заняться расчетами, сколько в итоге сожрет памяти многократная аллокация коллекций структур)
EnsureDirectoryExists(pathToDirectory); | ||
|
||
var safeFileName = GetSafeFileName(fileName); | ||
bitmap.Save(Path.Combine(pathToDirectory, safeFileName), ImageFormat.Png); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
не стеняйся выделять переменные.
По-моему, запись
var safeFileName = GetSafeFileName(fileName);
var savePath = Path.Combine(pathToDirectory, safeFileName);
bitmap.Save(savePath, ImageFormat.Png);
намного легче читается
|
||
private static void EnsureDirectoryExists(string directory) | ||
{ | ||
if (!Directory.Exists(directory)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, что если переписать без отрицания, то будет чуть нагляднее
|
||
secondRectangleResult.OnSuccess(secondRectangle => | ||
{ | ||
var expectedRectangleCenter = new Point( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
В итоге, выделяли мы или нет константу Half...
всё равно есть деление на 2, пусть и в другом месте. Повод задуматься о том, чтоб научить точку саму по себе становиться центром прямоугольника / либо вынести эту ответственность ещё куда-то.
|
||
secondRectangleResult.OnSuccess(secondRectangle => | ||
{ | ||
secondRectangle.IntersectsWith(firstRectangle).Should().BeFalse(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
К комментарию выше: тут всё весьма прилично, мы не руками реализовываем поиск пересечения, а используем уже написанный метод. Было бы круто с точкой то же самое провернуть.
[SetUp] | ||
public void SetUp() | ||
{ | ||
preprocessor = new WordPreprocessor(new List<string>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SetUp
не несет никакой пользы. Если бы preprocessor
как-то конфигурировался - ок, но тут можно сделать поле вычисляемым, и всё будет работать точно так же, т.к. каждый тест будет обращаться к новому объекту препроцессора.
Описание проекта:
Проект представляет собой программу для генерации облака тегов из файла с набором слов. Программа осуществляет удаление скучных слов, перечень которых указан в отдельном файле, а также приведение всех слов к нижнему регистру для унификации.
Внесенные изменения:
@pakapik