Skip to content

Савицких Антон#210

Open
Xineev wants to merge 13 commits intokontur-courses:masterfrom
Xineev:master
Open

Савицких Антон#210
Xineev wants to merge 13 commits intokontur-courses:masterfrom
Xineev:master

Conversation

@Xineev
Copy link
Copy Markdown

@Xineev Xineev commented Dec 29, 2025

Copy link
Copy Markdown

@SquirrelLeonid SquirrelLeonid left a comment

Choose a reason for hiding this comment

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

  1. Правки, не связанные на прямую с переходом на Result
  2. Точно можно покрыть больше сценариев с помощью Result
  3. Для закрытия второго пункта задачи нужно внимательно посмотреть используемые методы в твоих проектах и покрыть возможные исключения Result

Comment on lines +7 to +8
using TagCloudGenerator.Infrastructure.Readers;
using static System.Runtime.InteropServices.JavaScript.JSType;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Лишние using. В других местах тоже проверь

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Проверил, постарался убрать все лишние

Comment on lines +53 to +54
string inputFile = opts.InputFile;
string outputFile = opts.OutputFile;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

string => var

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Исправил

.Then(reader => reader.TryRead(inputFile))
.Then(words => _normalizer.Normalize(words))
.Then(words => _generator.Generate(words, canvasSettings, textSettings, _filters))
.Then(image => Result.OfAction(() => image.Save(outputFile, ImageFormat.Png), "Failed to save output image"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Аргументы для метода OfAction стоит разбить по строкам (лямбда и сообщение в случае ошибки). Так будет легче читать.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Поменял

Comment on lines +22 to +23
return Result.Fail<IFormatReader>("File path is empty");
var reader = _readers.FirstOrDefault(r => r.CanRead(filePath));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Для читаемости добавить отступ между return и инструкцией вне if'а. Так ты дополнительно разграничишь небольшие кусочки логики. Можно мыслить это как запятую или точку в предложении :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Исправил

Comment on lines +26 to +27
return Result.Fail<IFormatReader>(
$"No reader found for file '{filePath}'");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

А здесь разбивка ни к чему. Все и так влезает в рекомендуемую ширину строки.

Хотя возможно это актуально для моего монитора (120 символов)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Исправил

@@ -0,0 +1,16 @@
using TagCloudGenerator.Core.Interfaces;

namespace TagCloudGenerator.Infrastructure.Sorterers
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorterers => Sorters

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Исправил

}
}

public struct Result<T>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

У меня есть несколько моментов, которые я бы переделал в этой реализации Result. Но т.к. она взята из примера к задаче, то пусть будет как есть.

Напиши отдельно если интересно, что на мой взгляд здесь не так

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Был бы очень рад, по личным ощущениям эту тему хуже остальных освоил, попробовать написать другую реализацию думаю будет полезно

It.IsAny<IEnumerable<CloudItem>>(),
It.IsAny<CanvasSettings>(),
It.IsAny<TextSettings>()))
.Returns(new Bitmap(1, 1));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

На этот bitmap нужно вызывать dispose

.Then(reader => reader.TryRead(inputFile))
.Then(words => _normalizer.Normalize(words))
.Then(words => _generator.Generate(words, canvasSettings, textSettings, _filters))
.Then(image => Result.OfAction(() => image.Save(outputFile, ImageFormat.Png), "Failed to save output image"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

image нужно задиспоузить после сохранения


namespace TagCloudGenerator.Algorithms
{
public class BasicTagCloudAlgorithm : ITagCloudAlgorithm
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь тоже переход на Result нужен

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Обновил

Copy link
Copy Markdown

@SquirrelLeonid SquirrelLeonid left a comment

Choose a reason for hiding this comment

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

Поправить еще пару моментов с using и немного перетащить логику из FrequencyDescendingSorter. В остальном ок, следующие правки, вероятно, будут завершающими

return Result.Ok(PlaceNextRectange(rectangleSize));
}

private Rectangle PlaceNextRectange(Size rectangleSize)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Опечатка

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Исправил


var initializedItems = InitializeCloudItems(sortedWords.Value, textSettings).ToList();

return _renderer.Render(initializedItems, canvasSettings, textSettings);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Предлагаю вынести шаг по рендеру из CloudGenerator. На то две причины:

  1. Класс должен генерировать облако, но вот его отрисовка - уже ответственность другого модуля. Грубо говоря - одну и ту же раскладку мы должны уметь скармливать разным рендерам.
  2. На то, что упоминание BItmap в этом классе излишне меня натолкнули строчки выше, где возвращается Result.Fail. Если провалиться в класс Result, то увидим, что в этом случае полю Value присвоится значение по умолчанию для Generic-типа. В данном случае это будет Null. Проблема в том, что это не говорит внешнему коду о том, что вызывать Dispose на Bitmap в этом случае не нужно. а попытка его вызова приведет к NullReferenceException.

Если сказать коротко - утащив отсюда упоминание Bitmap мы разгрузим класс. Ему не нужно будет брать ответственность за отрисовку картинки - только за генерацию раскладки.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Вынес шаг с рендером в классы клиентов


foreach (var word in words)
{
foreach (var filter in filters)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь все еще возможно множественное перечисление по filters. Вообще на всей цепочке фильтров уместно передавать / получать ICollection или лист / массив. Это следует из вполне разумных предположений:
а) Количество фильтров конечно.
б) Это количество почти наверняка будет невелико

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Обновил

Comment on lines +39 to +44
if (!wordsWithFreq.IsSuccess || wordsWithFreq.Value == null)
return Result.Fail<Bitmap>("Analyzed words collection is null");

var sortedWords = _sorter.Sort(wordsWithFreq.Value);
if (!sortedWords.IsSuccess || sortedWords.Value == null)
return Result.Fail<Bitmap>("Sorted words collection is null");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь тоже предлагаю не рассматривать отсутствие слов как ошибочное состояние. В обоих случаях мы по прежнему можем сгенерировать пустой холст.

Copy link
Copy Markdown
Author

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 +12
if (words == null)
return Result.Fail<Dictionary<string, int>>("Missing words list to analyze");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

И еще один момент, в котором отсутствие слов не должно являться исключительным состоянием. Вероятно ты имел ввиду, что коллекция не должна быть null, но на мой взгляд в данном случае Null и Count = 0 это одно и то же

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Заменил на Result.Ok(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

У тебя там Result.Ok не от Null, а с конкретным объектом. Вообще, так даже лучше, только MaxFreq и MinFreq я бы указал по нулям.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Сначала также хотел, но дело в том что minFreq и maxFreq дальше используются для вычисления frequencyRatio и там их разность берется как делитель, если оставить их нулями то получим деление на 0 поэтому решил изначально оставлять такими значениями, в любом случае это ситуация при которой мы ничего не рендерим поэтому полученные там коэффициенты при текущих частотах ни на что не влияют

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Пересмотрел свой код, понял что это не должно ничего сломать

Указал по нулям

return Result.Fail<Bitmap>("Invalid canvas size");

var bitmap = new Bitmap(canvasSettings.CanvasSize.Width, canvasSettings.CanvasSize.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphics => var. В других подобных местах аналогично

Copy link
Copy Markdown
Author

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 +12
if (items == null)
return Result.Fail<Bitmap>("Cloud items are null");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь ошибочное состояние уже более уместно. Но все равно можно подумать, не заменить ли ошибку рисованием пустого холста

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Заменил на создание пустого листа

Comment on lines +23 to +44
ConfigureGraphics(graphics);
graphics.Clear(canvasSettings.BackgroundColor);
var (offsetX, offsetY) = CalculateOffset(itemsList, canvasSettings);
using (SolidBrush brush = new SolidBrush(textSettings.TextColor))
{
using (StringFormat stringFormat = new StringFormat())
{
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;

using (Pen pen = new Pen(textSettings.TextColor, 1))
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

return Result.Of(() =>
{
foreach (var item in itemsList)
DrawCloudItem(graphics, item, offsetX, offsetY, canvasSettings, textSettings, brush, pen, stringFormat);
return bitmap;
});
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь аналогичная проблема.

graphics, brush, pen, stringFormat используются в лямбде, но уничтожаются вне ее. Лучше тоже отступить на полшага назад и завернуть все внутрь одного ResultOf. Просто разбей это на два шага.

  1. var result = ResultOf...
  2. return result;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Вроде как исправил

Comment on lines +13 to +14
var minFreq = int.MaxValue;
var maxFreq = int.MinValue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Этому классу точно не нужно заниматься подсчетом максимальной / минимальной частоты. Эту логику нужно унести в WordsFrequencyAnalyzer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Вынес логику в WordsFrequencyAnalyzer

It.IsAny<IEnumerable<CloudItem>>(),
It.IsAny<CanvasSettings>(),
It.IsAny<TextSettings>()))
.Returns(new Bitmap(1, 1));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Все еще актуально. Вынеси Bitmap(1,1) чуть выше и заверни его в using

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Это из тестов ушло

@SquirrelLeonid
Copy link
Copy Markdown

Оставшиеся замечания учтены, задачу засчитываю на полный балл

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