Skip to content

Медведев Фома#240

Open
Kpokoko wants to merge 7 commits intokontur-courses:masterfrom
Kpokoko:master
Open

Медведев Фома#240
Kpokoko wants to merge 7 commits intokontur-courses:masterfrom
Kpokoko:master

Conversation

@Kpokoko
Copy link
Copy Markdown

@Kpokoko Kpokoko commented Nov 16, 2025

@tripples25
Выполнил минимальные требования по задаче

person.Phone = new Phone {Name = "Смартфон Vivo", Owner = person};

var printer = ObjectPrinter.For<Person>();
var printer = ObjectPrinter.For<Person>()
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 Сейчас из интерфейса торчит куча методов. Пользоваться таким будет сложно. Давай подумаем, как можно убрать часть из этих вещей, чтобы они не торчали наружу. Тут пока вижу два способа - как-то изменить способ работы Serializaers(возможно вернуть логику внутрь конфига), либо закрыть внутренние методы ключевым словом internal, но тут возникнет проблема расширяемости.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Тут ещё и доступен TrimStringToLength, который по факту должен быть доступен только для типа string

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 readonly Dictionary<Type, CultureInfo> CustomCultureInfos;
public readonly Dictionary<string, ISerializer> PropertySerializers;
public readonly Dictionary<string, int> MaxStringLength;
public readonly HashSet<string> ExcludedProperties;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Давай для всех полей сделаем модификаторы private, а также заиспользуем ридонли коллекции (IReadOnlyDictionary, IReadOnlySet). Следует инкапсулировать данные, которые нужны для внутренней логики, а доступ к ним осуществлять через методы

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 PrintingConfig<TOwner> ExcludePropertyOfType<T>()
{
var tempSet = new HashSet<Type>(ExcludedTypes);
tempSet.Add(typeof(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.

Тут можно добавить проверку на то, что данный тип уже есть в сете, если есть - не копировать сет

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 PrintingConfig<TOwner> WithTypeSerializtionType<T>(ISerializer serializer)
{
var tempDict = new Dictionary<Type, ISerializer>(TypeSerializers);
tempDict.Add(typeof(T), serializer);
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.

👍

ExcludedProperties);
}

public PrintingConfig<TOwner> TrimStringToLength(string propertyName, int length)
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.

👍

continue;
}

sb.Append(identation + propertyInfo.Name + " = " +
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.

👍

}
}

public class GuidSerializer : ISerializer
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.

👍

private readonly PrintingConfig<TOwner> _config;
private Expression<Func<TOwner, TPropType>>? _propertySelector;

public PropertyPrintingConfig(PrintingConfig<TOwner> config, Expression<Func<TOwner, TPropType>>? propertySelector)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Этот Expression никак не валидируется, по факту я могу туда засунуть что угодно

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.

👍

//3. Для числовых типов указать культуру
.SetTypeCulture<double>(CultureInfo.InvariantCulture)
//4. Настроить сериализацию конкретного свойства
.GetProperty(x => x.Name).SetSerializationType(new NameSerializer())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Странное название GetProperty. Честно, по нему я не могу понять что именно он должен делать. Давай подумаем, как сделать более понятные названия

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.

👍

}

[Test]
public void ExcludeTypeTest()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Тесты стоит писать по AAA, более понятные названия дать, а также лучше строковые значения в них более приятно оформить, не в одну строку

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 interface ISerializer
{
public Func<object?, int, int, string> SerializerFunc { get; }
public Func<object?, int, int, string> GetSerializerFunc => SerializerFunc;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Не используется

public Func<object?, int, int, string> GetSerializerFunc => SerializerFunc;
}

public class Serializer : ISerializer
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Кажется в этом классе теперь не сильно много смысла и вместо его использования можно было просто использовать Func

public Func<object?, int, int, string> SerializerFunc { get; }
public Func<object?, int, int, string> GetSerializeFunc => SerializerFunc;

public Serializer(Func<object?, int, int, string> serializerFunc)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Контракт входной функции не понятен - требуется передать функцию от объекта, числа и числа. Без понимания как это используется другими классами не понять для чего нужны эти числа, да и в итоге они могут никак и не применяться. Если ты хотел всё-таки как-то это обыграть, то числа скорее должны были передаваться отдельно под понятными именами. То есть примерно так:

public Serializer(Func<object?, string> serializator, int identation, int deepnessLevel)

Но как мне кажется это не ответственность конкретного сериализатора работать с этими значениями

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.

Я решил просто ликвидировать и интерфейс, и этот класс, и оставить просто Func<>, обёрнутый в делегат. Что-то такое будет нормальным вариантом?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Да, вполне. Либо можно оставить Func<object, string>. То и то будет норм вариантом в целом

public static class PropertyPrintingConfigExtensions
{
public static PrintingConfig<TOwner> SetSerializationStyle<TOwner, TPropType>
(this PropertyPrintingConfig<TOwner, TPropType> propertyConfig, Serializer serializer)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Здесь почему-то с классом работаешь, а не с интерфейсом. Я не смогу сюда передать другой наследник ISerializer

}

public PrintingConfig<TOwner> ParentConfig => config;
public Expression<Func<TOwner, TPropType>>? PropertySelector => propertySelector;
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

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.

Я посидел-поделал, но мыслей, как это реализовать не пришло, кроме как попытаться убрать Extensions и сделать эти методы приватными, но тогда возникает другой вопрос: как мне ограничить вызов метода Trim только для строк, кроме как where T : string, ведь так тут нельзя. Как тут поступить стоит: развивать эту мысль, или подумать над другими способами?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Так я не совсем про то. Сейчас у тебя торчат наружу свойства PropertyName, PropertySelector и ParentConfig. Это засоряет список, их стоит сделать приватными

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

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.

👍, действительно тонко, я думал про это изначально, но что-то не так, видимо, сделал, и оно не сработало тогда

{
[Test]
public void Demo()
public void DemonstrationTest()
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.

👍

return sb.ToString();
}

private string SerializeDictionaryElement(object obj, int deepnessLevel)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

А не логичнее тут работать с KeyValuePair или чем-то более удобным? Сейчас из-за этого приходится тебе хардкодить доступ к объектам в паре

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 deepnessExceededString;
var type = obj.GetType();
var key = type.GetProperty("Key")!.GetValue(obj);
var value = type.GetProperty("Value")!.GetValue(obj);
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.

👍

if (IsDeepnessOverflow(deepnessLevel))
return deepnessExceededString;
var type = obj.GetType();
var key = type.GetProperty("Key")!.GetValue(obj);
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.

👍

@@ -1,3 +1,3 @@
using System;

namespace ObjectPrinting.Tests
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

@Kpokoko Kpokoko Nov 24, 2025

Choose a reason for hiding this comment

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

👍 (на приватные поля и свойства добавил отдельный тест, сделал Age публичным полем, теперь сериализуется)

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