Pull to refresh
17
0
Джошуа Лайт @JoshuaLight

Software Developer

Send message
Простой пример, который доставлял бы мне дискомфорт в программировании если бы английский язык был бы для меня родной. Door->open() vs Open->door().

Да, в ООП предполагается такое незначительное нарушение грамматики, но из него никак не следует, что нужно идти в диаметрально противоположное направление и писать хелперы.

К сожалению, не могу подтвердить или опровергнуть это мнение. В основном наблюдаю пренебрежение к языку и словам, из-за которого код получается неопрятным, неаккуратным, но зато рабочим, как если бы его работоспособность была конечным продуктом.


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

На мой взгляд, сама семантика вызова: ThreadPool.Execute предполагает, что действие не будет выполнено мгновенно, поскольку мы работаем в контексте ThreadPool.


Никогда бы не подумал что-то вызвать на тредпуле, не понимая, что это в действительности значит: что там есть потоки, они управляются, что не нужно их надолго задерживать. Тип ThreadPool сообщает вполне достаточно.


Но аргумент понятен. Как вам: ThreadPool.Queue(action)? Зачем WorkItem? Что это значит?

Как финтифлюшка в HelloWorld забавно смотрится, но не масштабируется для большого проекта.

Хм. Сперва постулируется: "не масштабируется для большого проекта", а затем как подтверждение — довольно частный пример, который может касаться лишь одной из подсистем этого самого большого проекта.


Кроме этого, надо понимать, 5.Minutes() работает там, где работает, а там, где не работает, ищется другое.


Положим, описываю я дебаффы в игре:


Debuff(CriticalChanceDecrease, power: 10, duration: 5.Minutes());

Теперь масштабируем эту строчку в десятки раз, и получаем вполне приличный, читаемый код. Система, которой нужны тики, а также иные, сложные, требующие технической сноровки, не пострадали.

Но при разработке обычно у каждого класса/модуля есть ответственный владелец (человек или команда). Было бы неприятно зайти в свой идеально причёсанный класс Directory и обнаружить там наваленную гору кода от метода Clean и его подчинённых, который написан другой командой и другим стилем, с другими соглашениями.

Поэтому методы расширений, которые есть в C#, или же т.н. Uniform Function Call Syntax (в некоторых других, хороших языках) позволяют добиться нужного уровня декомпозиции (разумеется, там, где не подходит объектная), сохраняя синтаксис (и семантику, надеюсь) вызова метода у экземпляра.


Передать класс в хелпер, который есть только в том проекте, где он нужен, намного элегантнее.

Поддержка хелпера хуже, чем перенасыщенный поведением класс. Когда мы говорим о Clean в Directory, то отражаем реально существующее действие очистки папки в коде, а вот в случае с хелпером — нет.


Да и раз уж говорить про статические хелперы, то зачем писать:


DirectoryHelper.CleanDirectory(path);
FileSystemUtil.CleanDirectory(path);

если можно хотя бы:


Directory.Clean(path);
Clean.Directory(path);

Ведь читается приятнее, проще, а перенасытить поведением такие штуки довольно сложно: возможно, в самой предметной области не найдётся столько слов.

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

Да, существуют ситуации, когда уже есть какие-то переменные или поля с такими же именами. Но и их можно обойти, всё зависит от конкретного случая, главное понимать, зачем глагол, а зачем существительное. В случае с Calculate глагол уж точно ни к чему.


Как вы должно быть поняли — пишу с точки зрения джависта и джавовский гайдлайнов, где это вопрос именования расписан и стандартный вариант именно глагол-существительное (что и над чем).

В C# для методов существует такое же правило. Интересно, что если метод, то Get (или какой-нибудь Calculate) пишется, а вот если свойство (синтаксически это метод без параметров и без скобочек) — то нет. Как если бы от физического воплощения (свойство или метод) одного и того же понятия мы бы по разному говорили о нём между собой.


Думается мне, критерий всегда один: говорят "скидка", значит Discount, и т.д., тут в комментарии ниже описывал.


Код тогда строится не по действиям, императивно, а описательно: не "скидка считается считателем скидок по формуле", а "скидка — это...". Разумеется, там где действие, там остаётся царствие глагола.

Я хочу именно что указать, что мне нужно получить пользователя, разве нет?

В том то и дело, что вот это "получить" — лишнее, ненужное. Получить пользователя и пользователь подразумевают одно и то же (пользователя), только первое зачем-то что-то уточняет.

Ну, название интерфейса IDiscount очевидно не хорошее, с точки зрения естественного языка, так как не передает совершаемого им действия

Совершаемое действие не имеет значения. Имеет значения результирующее понятие.

Я бы не хотел отходить от темы, размышляя "а что если". Боюсь, тогда мы тут надолго застрянем.


Совершенно точно, что простое IDiscount имеет свои пределы, но пределы эти — часть другого обсуждения, мне же оно нужно было для того, чтобы показать идею.


Кстати, если взять CalculateDiscount(), CalculateNDS() и CalculateTotalSum(), то проще — Discount(), NDS() и TotalSum(). Что оно Calculate, и так понятно.

Как раз об этом и будет заключительная статья!

Ну а кто будет диалог сохранения показывать?

Диалог сохранения показывает метод-обработчик кнопки сохранить.


Это ж какой годкласс на сколько тысяч строк получится? Да и зависимости левые получаются. Зачем документу знать о графической системе даже просто транзитом?

На мой взгляд, показать диалоговое окно с выбором пути для сохранения, а потом вызвать document.SaveTo(path) или document.SaveTo(stream) — вполне достаточно. Если нужно обобщить для Unit-тестов, то document.SaveTo(storage). Да и говорим мы: "Сохрани документ". Опять же, посредники и помощники — избыточны.


Попытки играться с зависимостями, перенося их туда, куда не требуется, приводит к ложному впечатлению, будто всяческие SOLID соблюдены, а код понятен и расширяем. Само наличие слова Helper — прямая дорога в технологическую сингулярность, и ваши слова про "экселевскую мусорку" это, к сожалению, подтверждают.


Там почти монаду сделали. (т.е. ExcelHelper.setWorkingDir(path)

Боюсь, это не монада...

Это лёгкая шалость в сторону экзистенциализма, там есть идея с похожим узором.


"Глагол предшествует существительному" — примерно то же самое, вы всё верно указали.

Если нужно инжектить, то пожалуйста:


public interface IDiscount
{
    decimal Of(Customer customer, Order order);
}

Вызов тогда:


var discount = _discount.Of(customer, order);
// Но никак не:
var discount = _discountCalculator.CalculateDiscountBy(customerId, order);

Суть осталась та же, изменилась только форма.

Книга отличная!


Но этот цикл про несколько иную идею, хотя и пересекается с некоторыми разделами книги.

Потому что Get — это глагол, а никакого значимого действия мы указывать не хотим, только то, что вот пользователь с идентификатором ID.


Но и User — не предел. Я улучшу это имя, развив ряд соображений из этой статьи, в статье следующей.

Можно было бы и по другому сделать, согласен, но не надо вешать на документ, то что собственно документа не касается

Полностью согласен, но не вижу ExcelHelper как подходящее решение такой проблемы.


Мне кажется, выразительнее и понятнее:


var document = ExcelDocument.Of(path);

а не:


var document = ExcelHelper.LoadDocument(path);

Или можно ещё:


var document = file.AsFile().AsExcelDocument();

Написанное полно описывает происходящее, и не привлечены дополнительные не классы, но понятия: говоря о загрузке документа из файла, мы ограничились известными словами.


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

Ну тут больше вопрос архитектуры — как вы делите программу на куски: фасадами или фабриками?

На мой взгляд, такое разделение не совсем корректно. Возьмём выражение:


var document = new ExcelDocument();
var sheet = document.NewSheet();

Будет ли ExcelDocument фасадом или фабрикой, не имеет решительно никакого значения, поскольку главное — с помощью названия подвести к уже существующему опыту (о создании документов и таблиц в Excel многие имеют представление).


Как только мы согласились написать ExcelDocument вместо ExcelHelper, мы уже как бы предначертали его судьбу и что за методы в нём можно ожидать.

«Система контроля версий», а не «Коммиты»

Если описываете "систему контроля версий", то VersionControlSystem; если "коммиты репозитория"Commits; если "текстовый редактор"TextEditor, если "тексты" (редкий какой-то случай) — Texts; если "веб-браузер"WebBrowser, если "веб-страницы"WebPages.


На мой взгляд, всё банально, но эту банальность часто избегают с помощью всяческих VCSManager, CommitsHelper, TextEditorUtils, WebHelper и т.д., как если бы простота была чем-то преступным и недостойным.

Боюсь, я не до конца понял вашу мысль.


То есть навернуть уровень абстракции

Что вы понимаете под уровнем абстракции в примере с Weapon, как он повысился и в какой момент?


Мы смотрим, как у нас обвешан загадочными гроздьями мета-штук Weapon

Вот это, кстати, понял. Возможно, не достаточно точно выразился, но если под "мета-штуками" вы имеете ввиду фразу про "метапрограммирование", то речь же была о простейшей библиотеке сериализации вроде Newtonsoft.Json.


Так что Weapon ничем, выходит, и не будет обвешан. Ответственность перенесли, как того требует S, но сохранили удобство, и никаких WeaponDamageGetter, WeaponJsonProvider, WeaponManager после себя не оставили.

Боюсь, мы тогда отдалимся от темы.


Мысль была вот какая: порой метафора, восходящая к опыту, понятнее и проще, чем низкоуровневая деталь. ManualResetEvent — наглядный пример.

Information

Rating
Does not participate
Location
Харьков, Харьковская обл., Украина
Date of birth
Registered
Activity