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

Software Developer

Send message
Буква «S» в наборе «SOLID» прямым текстом говорит нам, что не должно быть так, чтобы объект сам себя менеджил, провайдил, гетил, сетил, ридил, райтил, валидэйтил, энкодил, декодил, диспетчил.

Боюсь, она ровно это и утверждает. Положим, JSON-сериализацию можно сделать так:


public class Weapon
{
    public int Damage;
}

public static class WeaponJsonSerializer // Как будто бы Single Responsibility.
{
    public static string Serialize(Weapon weapon) =>
        $"\{\"Damage\": {weapon.Damage}\}";
}

Хотя мы и вынесли сериализацию в отдельную сущность, принцип S, как мне видится, был нарушен: теперь каждое изменение полей Weapon потребует изменений в WeaponSerializer.


Можно:


public class Weapon : IJson
{
    public int Damage;

    public string AsJson() => $"\{\"Damage\": {Damage}\}";
}

Теперь лучше, поскольку Weapon ответственен за всё, что с ним происходит, но так работать попросту неудобно, да и, к тому же, иногда не так велика разница, идти ли вниз к методу AsJson() или к лежащему недалеко WeaponJsonSerializer.


Куда сильнее — использовать возможности метапрограммирования и реализовать обобщённую сериализацию с помощью рефлексии или кодогенерации. Библиотек полно.

Разница между ListSorter и SortedList в том, что в первом случае мы говорим о списке, его сортировщике и отсортированном списке:


var list = new List<int>() { ... }; // Список.
var sortedList = ListSorter.Sort(list); // Сортировщик + отсортированный список.

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


Например:


var list = new List<int>();
var sortedList = new SortedList<int>(list);
// или
var list = new List<int>();
var sortedList = list.Sort();

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

Если вам все еще понятно что там написано внутри — добавьте ещё пару уровней.

И уже не будет иметь значения, x там или orderOfSomeItemOfSomeTaskOfSomeProject. Такой код исправляется другими средствами.


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

Хм. Ранее вы писали, что Set — это "установить", а тут уже, кажется, "наступить". Также заметил несколько смутное "наличие элементов в очереди", которое никак не следует из ManualResetEvent и того, что обсуждалось ранее.


Стало: есть ворота наличия элементов в очереди, и мы ждём их открытия...

Боюсь, мой пример был искажён. Не получится перенести низкоуровневую терминологию сигнальных состояний на повседневные ворота. Нет, тут всё гораздо проще: есть ворота; когда они закрыты, пройти дальше нельзя; когда открыты — можно.


Как выглядит ManualResetEvent, являющийся EventWaitHandle, неясно. А ворота выглядят так:
image

Вот только семантика коллекции тут ложная: она маскирует стоимость всех этих операций.

Именно поэтому я уточнил: "а уж тип, если потребуется, дополнит происходящее".

Так, большинство Params-, Args- и Options-классов нужны для того, чтобы уменьшить число параметров у метода, и иметь возможность добавлять в будущем новые параметры сохраняя совместимость со старым кодом.

Тут тогда, думается мне, есть проблема посерьёзнее: недостаточно разбили предметную область на сущности, поэтому есть методы с большим количеством параметров. Расширять это дело настоятельно не рекомендую. Спрятать много параметров в объект не поможет, ведь нагрузка на ум осталась!


А ещё персистентность всегда всё портит. Ради возможности положить объект в базу или прочитать его оттуда приходится либо держать открытыми все внутренности объекта, либо создавать те самые Data и Info.

Согласен, и такое бывает! Но это не оправдывает другие случаи, когда Info и Data — наспех сочинённая декомпозиция бизнес-логики.


Вот только интерфейсы — это не сущности. Интерфейсы — это качества и роли сущностей.

Речь не об интерфейсах и о том что они такое, а о словах, которые выбираются, чтобы отразить подразумевающееся интерфейсом. Поэтому:


Нет никаких проблем если класс Sequence будет реализовывать интерфейс IEnumerable

Проблемы есть, ведь как понятия Sequence и Enumerable очень схожи между собой, только одно основывается на уже известной последовательности, тогда как второе — на глаголе перечислять, из которого выводится, что перечисляем мы последовательность.


Идея вот какая: важно подобрать такое слово, чтобы оно как можно глубже вплеталось в опыт. IOpeneable не восходит к картинкам, а IGate — напротив.


А как ещё разделять внешнее и внутреннее API? Если всё делать публичным — это и будет то самое ружье.

Думаю, нет никакой связи между тем, что пишут HumanBase (вместо Animal, например), и публичностью и непубличностью. Речь об архитектурной сложности решения. С Base, Impl, Internal, Raw и прочим ничего понятного и простого, как правило, не получается.


Проще-то проще, но когда рядом находятся IOrdersRepository, List<Order> и Dictionary<Guid, Order> — приходится их хоть как-то различать.

Полагаю, наличие частного случая никак не влияет на все остальные, где IOrdersRepository — единственный объект, представляющий заказы.


В таких простых ситуациях — да, понимаем. А вот в трехуровневых запросах на самом внутреннем уровне уже хотелось бы видеть item вместо простого x.

Если вы имеете ввиду нечто вроде:


Inventory.Items
    .Where(item => item.IsWeapon)
    .Select(item => item.Damage)
    .Where(damage => damage > 10);

То, мне кажется, куда просторнее смотрится:


Inventory.Items
    .Where(x => x.IsWeapon)
    .Select(x => x.Damage)
    .Where(x => x > 10);

Что x > 10 — про урон, думаю, всё ещё понятно, а дышать стало легче.


Set переводится как "установить", Reset — как "сбросить". Название же вашего класса "ThreadGate" ничего не говорит о том, будут ли ворота закрыты после выхода из WaitForOpen или нет, а ведь это важная информация.

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


Например, можно убрать оттуда слово Thread (блокировка потока как будто следует из WaitForOpen) и получится два типа: Gate и AutoCloseGate. Всё ещё понятнее, чем стандартный аналог.

В примере orders вместо orderRepository есть и плюсы и минусы

Полностью согласен. Среди множества всех возможных сценариев, существуют такие, в которых _ordersRepository смотрится лучше, чем _orders, но в большинстве (95%), на мой взгляд, Repository — избыточное уточнение.


Как минимум, сходу такое имя воспринимается как коллекция

Мне кажется, IOrdersRepository как раз имеет семантику коллекции: получить заказы, добавить, удалить. Так что он вполне себе может представлять множество заказов (или заказы), а уж тип, если потребуется, дополнит происходящее.


Я заметил, что такие упрощения имен допустимо делать только в зрелом коде, который уже не так часто меняется, при условии что изменение API не создаст проблем

На моём опыте люди, как правило, напротив, всё слишком переусложняют и переуточняют: _currentSelectedSpecificItemIndex. Превратить такую запись в _selectedIndex (убираем Item, поскольку, скорее всего, итак работаем в контексте какого-то типа SomeItem) — не упростить, но убрать избыточность.

А иногда что писать — знаешь, а как описать — нет. Это что же, долго перебирать варианты как описать, чтобы решить как назвать, чтобы наконец то написать?

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

Information

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