Skip to content

Result pattern - #1

Open
d2em0n wants to merge 18 commits into
masterfrom
resultPattern
Open

Result pattern#1
d2em0n wants to merge 18 commits into
masterfrom
resultPattern

Conversation

@d2em0n

@d2em0n d2em0n commented Jan 19, 2025

Copy link
Copy Markdown
Owner

No description provided.

@GlazProject GlazProject left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В целом направление верное. Осталось доработать оставшиеся классы.

  • TagGenerator
  • PictureMaker
  • TextProcessor
  • IWordFilter

[PictureMaker] _tags = tagGenerator.GenerateTags(textProcessor.WordFrequencies()); лучше перенести в метод, а не в конструктор. Это связано с тем, что в идеале создание класса не должно зависеть от аргументов исполнения, а сам класс должен быть переиспользуемым, если это не data-class. То есть один раз созданный PictureMaker должен без пересоздания уметь читать подряд разные файлы. Для этого ITextProcessor должен в методе принимать параметры чтения, чтобы их использовал TextProvider. Это к вопросу о том, что должно быть в конструкторе, а что в методе. В качестве одной из аналогий можно привести грузовик. Без колёс, кузова и кабины он не будет грузоваиком. Поэтому они передаются в конструкторе. А вот наполнение кузова (песок, щебень, скала) - это уже объекты, требуемые для осуществления конкретной операции - перевозки. Поэтому груз будет передаваться как аргумент метода перевозки

Comment thread Client/Program.cs Outdated
Comment on lines 21 to 24
var container = DependencyInjection.BuildContainer(config);
using var scope = container.BeginLifetimeScope();
scope.Resolve<PictureMaker>().DrawPicture();
Console.WriteLine($"результат сохранен в {config.PicturePath}");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Нужно обернуть внутрь Then. Иначе мы попытаемся собрать контейнер с неверным Config

throw new FileNotFoundException();
return new Result<string>($"File not found: {_filePath}");
using var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read);
var document = new HWPFDocument(stream);

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 непросто, так как его нужно диспоузить. Поэтому для простоты можно считать, что он откроется всегда
return Result.Of(() => new HWPFDocument(stream)).Then(document=> document.GetRange())

return File.ReadAllText(_filePath);
return !File.Exists(_filePath)
? Result.Fail<string>($"File {_filePath} does not exist")
: File.ReadAllText(_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.

ReadAllText может вернуть целый ворох исключений. Обернём его в Result.Of(), чтобы они не сломали наше приложение?

Comment thread TagsCloudContainer/CloudLayout.cs Outdated

return Result.Fail<Rectangle>("Вышли за границы рисунка");
}
throw new ArgumentException("Not Enough Points Generated");

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, то не должно быть throw

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
{
var rectangle = layout.PutNextRectangle(tag.Frame);
DrawTag(image, rectangle, tag);
DrawTag(image, rectangle.GetValueOrThrow(), tag);

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 как возвращаемое значение, чем бросать исключение просто так. Не стоит смешивать всё в одно. Либо исключения, либо Result. В этом задании нужно именно Result использовать, чтобы почувствовать, как с ним работать. Пусть, возможно, без него и легче читается конкретно этот пример.

}
}

public static Result<TOutput> Then<TInput, TOutput>(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Этот метод и ниже можно вынести в Extensions, чтобы не забивать всем подряд Result

public Dictionary<Word, int> WordFrequencies()
{
var words = parser.GetWordsFromString(provider.ReadFile());
var words = parser.GetWordsFromString(provider.ReadFile().GetValueOrThrow());

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 подхода. Тогда должно быть

return provider.ReadFile().Then(text => parser.GetGetWordsFromString(text)).Then(words => filters.Aggregate(...))

Comment thread Client/Program.cs Outdated
using var scope = container.BeginLifetimeScope();
scope.Resolve<PictureMaker>().DrawPicture();
Console.WriteLine($"результат сохранен в {config.PicturePath}");
scope.Resolve<PictureMaker>().DrawPicture().OnFail(error => Console.WriteLine($"Ошибка обработки: {error}"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OnFail лучше с новой строки )

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
using var image = new Bitmap(layout.Size.Width, layout.Size.Height);
foreach (var tag in _tags)
var wordsDictionary = _textProcessor.WordFrequencies();
if (!wordsDictionary.IsSuccess)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

А как же Then?

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
{
var rectangle = layout.PutNextRectangle(tag.Frame);
DrawTag(image, rectangle.GetValueOrThrow(), tag);
if (!rectangle.IsSuccess)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь без явной проверки к сожалению не обойтись. Всё правильно

var document = new HWPFDocument(stream);
var range = document.GetRange();
return range.Text;
return Result.Of(() => new HWPFDocument(stream)).Then(document=> document.GetRange().Text);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

С переносами будет чуть читаемее

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
DrawTag(image, rectangle, tag);

return _textProcessor.WordFrequencies()
.Then(wordsDict =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Огромный кусок кода в Then. В отладке это место будет очень больно проходить

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
.Then(wordsDict =>
{
var tags = _tagGenerator.GenerateTags(wordsDict);
if (!tags.IsSuccess) return new Result<None>(tags.Error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Для предотвращения такого кода был сделан Then

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
image.Save(_fileName);
return Result.Ok();
})
.OnFail(error => Result.Fail<None>(error));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Кается этот метод выглядит также как код ниже

var a = 15;
var b = a;
return b;

Comment thread TagsCloudContainer/PictureMaker.cs Outdated
Comment on lines +36 to +42
foreach (var tag in tags.Value)
{
var rectangle = layout.PutNextRectangle(tag.Frame);
if (!rectangle.IsSuccess)
return Result.Fail<None>(rectangle.Error);;
DrawTag(image, rectangle.Value, tag);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Уже не первый раз вызывается ForEach. Поэтому лучше вынести в экстеншн и не писать никакие

if (!rectangle.IsSuccess)
                        return Result.Fail<None>(rectangle.Error);

if (string.IsNullOrWhiteSpace(input))
return Result.Fail<IEnumerable<Word>>("Input cannot be empty");
return Result.Ok(_regex.Matches(input)
.Cast<Match>()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Matches уже возвращает IEnumerable

Comment on lines +23 to +39
.Select(kvp =>
{
var colorResult = _colorProvider.GetColor();
if (!colorResult.IsSuccess)
return Result.Fail<Tag>(colorResult.Error);

var fontResult = SetFont(_defaultFont, kvp.Value);
if (!fontResult.IsSuccess)
return Result.Fail<Tag>(fontResult.Error);

var frameSizeResult = SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics);
if (!frameSizeResult.IsSuccess)
return Result.Fail<Tag>(frameSizeResult.Error);

return Result.Ok(new Tag(kvp.Key, fontResult.Value, colorResult.Value,
frameSizeResult.Value));
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment on lines +40 to +41
return Result.Ok(tagsResult.Select(t => t.Value))
.OnFail(error => Result.Fail<Tag>(error));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

t.Value всегда вернёт значение. Либо null, либо настоящее. В итоге в OnFail никогда не попадёт исполнение кода. Если вызывать GetValueOrThrow, то на пустом месте получим исключение, которое затормозит процесс.

Так как IEnumerable ленивый, то мы исключение получим только в рантайме при итерации по нему. А Result будет всегда успешный

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