-
Notifications
You must be signed in to change notification settings - Fork 300
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
base: master
Are you sure you want to change the base?
Сазонов Александр #233
Conversation
|
||
private static string[] SplitIntoLines(string text) | ||
{ | ||
return text.Split(['\n', '\r']); |
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.
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.
Не успел настроить Rider, а VS не дает такие подсказки=)
|
||
public class MarkdownTokenParser | ||
{ | ||
private readonly Dictionary<string, IHtmlTag> _markdownTags = new() |
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.
А правда, что MarkdownTokenParser
ответственен за теги? Тут может помочь SRP
В этом классе есть и другие примеры наршения SRP
|
||
private Token ParseWord(string word) | ||
{ | ||
return TryParseMarkdownTag(word, out var token) |
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.
Сплит по строкам - отлично, но сплит по пробелам без доп логики - это хорошо? Не будет ли в этом каких-то подводных камней?
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.
Думаю, что сплит по пробелам лишний. Строку буду парсить на токены.
if (matchingTag == null) | ||
return false; | ||
|
||
token = matchingTag.ToHtml(value); |
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.
matchingTag.ToHtml
выглядит странно. Если он про конвертацию в html, то нарушается SRP
А если какое-то сопоставление с TokenType
, то тоже не ок, т.к. нет такого md-тега как html
Или я не понял твою мысль
private IHtmlTag? FindMatchingTag(string value) | ||
{ | ||
return _markdownTags | ||
.Where(tag => value.StartsWith(tag.Key)) |
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.
Такие поиски точно закроют все-все потребности конвертации?
cs/Markdown/Html/BoldTag.cs
Outdated
@@ -0,0 +1,11 @@ | |||
using Markdown.Tokens; |
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.
Посмотрел решение в целом - пока не понимаю, будет ли оно работать вообще или нет. Подход со стороны "от общего к частному" - супер, но вот по текущему проектированию у меня не сложилось понимание, как будут закрываться на 2 и 3 балла задания
{ | ||
for (var i = 0; i < line.Length; i++) | ||
{ | ||
//TODO: ����� ����� ���������� ���������� ������ �� ������ |
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.
А что здесь написано? Какой кодировкой? Давай все оформлять в стандартной - UTF-8
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.
Здесь будет реализация парсинга строк на токены=)
//TODO: ����� ����� ���������� ���������� ������ �� ������ | ||
var currStr = line.Substring(i, 1); | ||
|
||
var isTagToken = MarkdownToken.TryGetTagToken(currStr, out var tagToken); |
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.
Раньше идея была работать с токенами абстрактно, сейчас, предполагаю, идея модернизировалась и теперь с текстом будет более плотное взаимодействие. К сожалению, я не нашел никаких сущностей, ответственных за md-теги, их обработку, логику и тд. Оттого есть подозрение, что логика их обработки будет или размазана, или храниться в одном классе потоком кода. Так делать точно не стоит) У нас ООП и DDD =) а еще всегда держим в голове SRP
cs/Markdown/Tokens/MarkdownToken.cs
Outdated
public TokenType Type { get; } = type; | ||
public bool IsClosedTag { get; } = isClosedTag; | ||
|
||
public static bool TryGetTagToken(string str, out MarkdownToken resultToken) |
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.
Кстати) давай попробуем анемичные модели данных. Т.е. оставлять объекты описательными, а все методы над ними выносить в самостоятельные классы. Зачем? Объект и бизнес-требования могут быть связаны сегодня, но нет гарантий, что завтра не появится бизнес-требование v2 или v3, а объект почти наверняка останется тот же. Также можно рассмотреть через SRP-призму: в текущем классе две ответственности
- описание объекта токена разметки
- методы формирования токена на основе строки
И следует стараться оставлять в классе только одну причину для изменений
foreach (var handler in _tokenHandlers) | ||
{ | ||
if (handler.CanHandle(tokenContext.CurrentToken) && | ||
handler.Handle(tokenContext)) |
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.
Возможно я не понял логику работы, но есть большое подозрение, что tokenContext
будет изменяться внутри Handle
. Если это действительно так, то предлагаю вспомнить комментарий из предыдущей задачи =)
kontur-courses/tdd#237 (comment) (он resolved, а гитхаб туповат, потому нужно явно развернуть комментарий...)
{ | ||
foreach (var handler in _tokenHandlers) | ||
{ | ||
if (handler.CanHandle(tokenContext.CurrentToken) && |
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.
Если у тебя предполагается всегда жесткая связка CanHandle && Handle
, то предлагаю попробовать Try-Parse
подход
public IList<MarkdownToken> Process(IList<MarkdownToken> tokens) | ||
{ | ||
var tokenContext = new TokenProcessingContext(tokens); | ||
var resultContextModifier = new TokenProcessingContextModifier(tokenContext); |
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.
Связка new TokenProcessingContextModifier(tokenContext);
с var tokenContext = new TokenProcessingContext(tokens);
выглядит "грязной" - данные меняются неявно. Такой подход стоит избегать, т.к. при работе с DI очень сильно может аукнуться) Да и в целом код, который stateless, сиииильно проще в поддержке, в отличии от stateful
Также могу прилинковать упомянутый выше коммент из прошлого ревью kontur-courses/tdd#237 (comment)
|
||
namespace Markdown.Markdown.Processing; | ||
|
||
public class MarkdownProcessor : IMarkdownProcessor |
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.
Правда ли этот класс нужен? Кажется, его логику вполне комфортно унести в Md
@@ -0,0 +1,8 @@ | |||
namespace Markdown.Markdown.Handlers; | |||
|
|||
public enum EmphasisLevel |
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.
Насколько понимаю, Emphasis
- про акцент и подчеркивание (в плане фокуса внимания) (словарь). Думаю, в данном контексте уместнее priority
{ | ||
yield return token; | ||
} | ||
yield return MarkdownTokenCreator.CreateSymbolToken("\n"); |
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.
В проекте много дублирования таких символов (\n
и прочих)
|
||
namespace Markdown.Markdown.Processing; | ||
|
||
public class TokenProcessingContext(IList<MarkdownToken> tokens) : IProcessingContext |
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.
Раз такие объекты появились, пора изучать и использовать records
=)
|
||
namespace Markdown.Markdown.Processing; | ||
|
||
public class TokenProcessingContextModifier(IProcessingContext processingContext) |
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.
Этот класс правда нужен?
" " => new MarkdownToken(content, TokenType.Space), | ||
"_" => new MarkdownToken(content, TokenType.TagPart), | ||
"#" => new MarkdownToken(content, TokenType.Header), | ||
"\n" => new MarkdownToken(content, TokenType.NewLine), |
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.
В сети наверняка есть много подробностей о прокачанном pattern mathing в c#
В данном случае "\n" or "\r" => new MarkdownToken(content, TokenType.NewLine),
cs/Markdown/Md.cs
Outdated
{ | ||
var tokens = _tokenParser | ||
.Parse(text) | ||
.ToArray(); |
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 lines = SplitIntoLines(text); | ||
var tokens = new List<IToken>(); | ||
for (var i = 0; i < lines.Length; i++) |
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.
Как будто можно просто через foreach
{ | ||
MarkdownConstants.Space => new MarkdownToken(content, TokenType.Space), | ||
MarkdownConstants.Escape => new MarkdownToken(content, TokenType.Escape), | ||
_ => CreateTextToken(content) |
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.
А зачем? Насколько понял, после строки return token.Type != TokenType.Text;
нам будет не важно на такой токен, получается, сделали работу просто так
|
||
public static bool TryCreateTagToken(string content, out IToken token) | ||
{ | ||
token = CreateTextToken(content); |
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.
Также вопрос про эту строку. Получается, у нас токен может быть заполнен текстом, хотя метод возвращает fase
=> снова сделали лишнюю работу и нагрузили heap
} | ||
|
||
private static bool IsTokenEnded(string content, string symbol, TokenType tokenType) => | ||
(tokenType == TokenType.Text && (MarkdownTagValidator.IsTagStart(symbol) || |
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.
Оооооочень сложно читать) давай пользоваться выносом в именованную функцию логики для упрощения чтения и понимания кода
cs/MarkdownTests/MdShould.cs
Outdated
public string RenderHeaders_ShouldNotParseInvalidCases(string markdownText) => | ||
markdown.Render(markdownText); | ||
|
||
//[TestCase("Markdown.md", TestName = "Convert Markdown file to HTML")] |
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.
Лишнее)
bool IsCloseTag { get; } | ||
TagType TagType { get; } | ||
IToken? TagPair { get; } | ||
Dictionary<AttributeType, IAttribute>? Attributes { get; } |
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.
Наличие этого свойства - еще один явный маркер, что архитектуру стоит перепроектировать)
По-хорошему, в наследниках интерфейсов должны использоваться все свойства, все методы и вообще, что есть в интерфейсе. Если где-то когда-то требуется использовать ?
без обоснования со стороны бизнес-логики, а со стороны чтобы как-то поддержать расширение контрактов, то это анти-паттерн. Потому что теперь появляются куски логики, где почти везде нет атрибутов (у всех токенов, кроме картинки, он не используется), но приходится про них знать
Раньше это тоже было (TagPair), но он хотя бы покрывал больше одного сценария. В данном же случае - слишком костыльно выглядит)
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="coverlet.collector" Version="6.0.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.
Все еще есть лишние подключения)
|
||
В Markdown разметке тег картинки обозначается следующим образом: | ||
|
||
![Alt text](URL) |
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.
public string RenderHeaders_NotParseInvalidCases(string markdownText) => | ||
markdown.Render(markdownText); | ||
|
||
[TestCase(@"![Alt text](URL)", ExpectedResult = """<img src="URL" alt="Alt text">""", |
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.
Не хватает тесты на реальный url
А также на взаимодействие с другими тегами (полужирный, курсив, excape)
@dmitgaranin