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

Сазонов Александр #233

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

Conversation

AlexxSaz
Copy link


private static string[] SplitIntoLines(string text)
{
return text.Split(['\n', '\r']);

Choose a reason for hiding this comment

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

Все-таки рекомендую использовать IDE полностью)
image

Copy link
Author

Choose a reason for hiding this comment

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

Не успел настроить Rider, а VS не дает такие подсказки=)


public class MarkdownTokenParser
{
private readonly Dictionary<string, IHtmlTag> _markdownTags = new()

Choose a reason for hiding this comment

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

А правда, что MarkdownTokenParser ответственен за теги? Тут может помочь SRP
В этом классе есть и другие примеры наршения SRP


private Token ParseWord(string word)
{
return TryParseMarkdownTag(word, out var token)

Choose a reason for hiding this comment

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

Сплит по строкам - отлично, но сплит по пробелам без доп логики - это хорошо? Не будет ли в этом каких-то подводных камней?

Copy link
Author

Choose a reason for hiding this comment

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

Думаю, что сплит по пробелам лишний. Строку буду парсить на токены.

if (matchingTag == null)
return false;

token = matchingTag.ToHtml(value);

Choose a reason for hiding this comment

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

matchingTag.ToHtml выглядит странно. Если он про конвертацию в html, то нарушается SRP
А если какое-то сопоставление с TokenType, то тоже не ок, т.к. нет такого md-тега как html

Или я не понял твою мысль

private IHtmlTag? FindMatchingTag(string value)
{
return _markdownTags
.Where(tag => value.StartsWith(tag.Key))

Choose a reason for hiding this comment

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

Такие поиски точно закроют все-все потребности конвертации?

@@ -0,0 +1,11 @@
using Markdown.Tokens;

Choose a reason for hiding this comment

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

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

{
for (var i = 0; i < line.Length; i++)
{
//TODO: ����� ����� ���������� ���������� ������ �� ������

Choose a reason for hiding this comment

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

А что здесь написано? Какой кодировкой? Давай все оформлять в стандартной - UTF-8

Copy link
Author

Choose a reason for hiding this comment

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

Здесь будет реализация парсинга строк на токены=)

//TODO: ����� ����� ���������� ���������� ������ �� ������
var currStr = line.Substring(i, 1);

var isTagToken = MarkdownToken.TryGetTagToken(currStr, out var tagToken);

Choose a reason for hiding this comment

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

Раньше идея была работать с токенами абстрактно, сейчас, предполагаю, идея модернизировалась и теперь с текстом будет более плотное взаимодействие. К сожалению, я не нашел никаких сущностей, ответственных за md-теги, их обработку, логику и тд. Оттого есть подозрение, что логика их обработки будет или размазана, или храниться в одном классе потоком кода. Так делать точно не стоит) У нас ООП и DDD =) а еще всегда держим в голове SRP

public TokenType Type { get; } = type;
public bool IsClosedTag { get; } = isClosedTag;

public static bool TryGetTagToken(string str, out MarkdownToken resultToken)

Choose a reason for hiding this comment

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

Кстати) давай попробуем анемичные модели данных. Т.е. оставлять объекты описательными, а все методы над ними выносить в самостоятельные классы. Зачем? Объект и бизнес-требования могут быть связаны сегодня, но нет гарантий, что завтра не появится бизнес-требование v2 или v3, а объект почти наверняка останется тот же. Также можно рассмотреть через SRP-призму: в текущем классе две ответственности

  • описание объекта токена разметки
  • методы формирования токена на основе строки

И следует стараться оставлять в классе только одну причину для изменений

foreach (var handler in _tokenHandlers)
{
if (handler.CanHandle(tokenContext.CurrentToken) &&
handler.Handle(tokenContext))

Choose a reason for hiding this comment

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

Возможно я не понял логику работы, но есть большое подозрение, что tokenContext будет изменяться внутри Handle. Если это действительно так, то предлагаю вспомнить комментарий из предыдущей задачи =)
kontur-courses/tdd#237 (comment) (он resolved, а гитхаб туповат, потому нужно явно развернуть комментарий...)

{
foreach (var handler in _tokenHandlers)
{
if (handler.CanHandle(tokenContext.CurrentToken) &&

Choose a reason for hiding this comment

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

Если у тебя предполагается всегда жесткая связка CanHandle && Handle, то предлагаю попробовать Try-Parse подход

public IList<MarkdownToken> Process(IList<MarkdownToken> tokens)
{
var tokenContext = new TokenProcessingContext(tokens);
var resultContextModifier = new TokenProcessingContextModifier(tokenContext);

Choose a reason for hiding this comment

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

Связка new TokenProcessingContextModifier(tokenContext); с var tokenContext = new TokenProcessingContext(tokens); выглядит "грязной" - данные меняются неявно. Такой подход стоит избегать, т.к. при работе с DI очень сильно может аукнуться) Да и в целом код, который stateless, сиииильно проще в поддержке, в отличии от stateful

Также могу прилинковать упомянутый выше коммент из прошлого ревью kontur-courses/tdd#237 (comment)


namespace Markdown.Markdown.Processing;

public class MarkdownProcessor : IMarkdownProcessor

Choose a reason for hiding this comment

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

Правда ли этот класс нужен? Кажется, его логику вполне комфортно унести в Md

@@ -0,0 +1,8 @@
namespace Markdown.Markdown.Handlers;

public enum EmphasisLevel

Choose a reason for hiding this comment

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

Насколько понимаю, Emphasis - про акцент и подчеркивание (в плане фокуса внимания) (словарь). Думаю, в данном контексте уместнее priority

{
yield return token;
}
yield return MarkdownTokenCreator.CreateSymbolToken("\n");

Choose a reason for hiding this comment

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

В проекте много дублирования таких символов (\n и прочих)


namespace Markdown.Markdown.Processing;

public class TokenProcessingContext(IList<MarkdownToken> tokens) : IProcessingContext

Choose a reason for hiding this comment

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

Раз такие объекты появились, пора изучать и использовать records =)


namespace Markdown.Markdown.Processing;

public class TokenProcessingContextModifier(IProcessingContext processingContext)

Choose a reason for hiding this comment

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

Этот класс правда нужен?

" " => new MarkdownToken(content, TokenType.Space),
"_" => new MarkdownToken(content, TokenType.TagPart),
"#" => new MarkdownToken(content, TokenType.Header),
"\n" => new MarkdownToken(content, TokenType.NewLine),

Choose a reason for hiding this comment

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

В сети наверняка есть много подробностей о прокачанном pattern mathing в c#
В данном случае "\n" or "\r" => new MarkdownToken(content, TokenType.NewLine),

{
var tokens = _tokenParser
.Parse(text)
.ToArray();

Choose a reason for hiding this comment

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

А зачем финализация здесь?

{
var lines = SplitIntoLines(text);
var tokens = new List<IToken>();
for (var i = 0; i < lines.Length; i++)

Choose a reason for hiding this comment

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

Как будто можно просто через foreach

{
MarkdownConstants.Space => new MarkdownToken(content, TokenType.Space),
MarkdownConstants.Escape => new MarkdownToken(content, TokenType.Escape),
_ => CreateTextToken(content)

Choose a reason for hiding this comment

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

А зачем? Насколько понял, после строки return token.Type != TokenType.Text; нам будет не важно на такой токен, получается, сделали работу просто так


public static bool TryCreateTagToken(string content, out IToken token)
{
token = CreateTextToken(content);

Choose a reason for hiding this comment

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

Также вопрос про эту строку. Получается, у нас токен может быть заполнен текстом, хотя метод возвращает fase => снова сделали лишнюю работу и нагрузили heap

}

private static bool IsTokenEnded(string content, string symbol, TokenType tokenType) =>
(tokenType == TokenType.Text && (MarkdownTagValidator.IsTagStart(symbol) ||

Choose a reason for hiding this comment

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

Оооооочень сложно читать) давай пользоваться выносом в именованную функцию логики для упрощения чтения и понимания кода

public string RenderHeaders_ShouldNotParseInvalidCases(string markdownText) =>
markdown.Render(markdownText);

//[TestCase("Markdown.md", TestName = "Convert Markdown file to HTML")]

Choose a reason for hiding this comment

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

Лишнее)

bool IsCloseTag { get; }
TagType TagType { get; }
IToken? TagPair { get; }
Dictionary<AttributeType, IAttribute>? Attributes { get; }

Choose a reason for hiding this comment

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

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

Раньше это тоже было (TagPair), но он хотя бы покрывал больше одного сценария. В данном же случае - слишком костыльно выглядит)

</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />

Choose a reason for hiding this comment

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

Все еще есть лишние подключения)


В Markdown разметке тег картинки обозначается следующим образом:

![Alt text](URL)

Choose a reason for hiding this comment

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

А еще бы в основном файле спецификации это добавить, т.к. без этого не выполняется требование выполнения ДЗ
image

public string RenderHeaders_NotParseInvalidCases(string markdownText) =>
markdown.Render(markdownText);

[TestCase(@"![Alt text](URL)", ExpectedResult = """<img src="URL" alt="Alt text">""",

Choose a reason for hiding this comment

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

Не хватает тесты на реальный url
А также на взаимодействие с другими тегами (полужирный, курсив, excape)

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