Буква «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, неясно. А ворота выглядят так:
Так, большинство 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.
Что x > 10 — про урон, думаю, всё ещё понятно, а дышать стало легче.
Set переводится как "установить", Reset — как "сбросить". Название же вашего класса "ThreadGate" ничего не говорит о том, будут ли ворота закрыты после выхода из WaitForOpen или нет, а ведь это важная информация.
Понимаю ваш аргумент. Я думал над тем, чтобы повысить точность этого названия, но опыт подсказывает, от ворот в первую очередь ожидаешь, что они просто закрываются и открываются, а уж потом можно дополнить, автоматически закрываются или нет.
Например, можно убрать оттуда слово Thread (блокировка потока как будто следует из WaitForOpen) и получится два типа: Gate и AutoCloseGate. Всё ещё понятнее, чем стандартный аналог.
В примере orders вместо orderRepository есть и плюсы и минусы
Полностью согласен. Среди множества всех возможных сценариев, существуют такие, в которых _ordersRepository смотрится лучше, чем _orders, но в большинстве (95%), на мой взгляд, Repository — избыточное уточнение.
Как минимум, сходу такое имя воспринимается как коллекция
Мне кажется, IOrdersRepository как раз имеет семантику коллекции: получить заказы, добавить, удалить. Так что он вполне себе может представлять множество заказов (или заказы), а уж тип, если потребуется, дополнит происходящее.
Я заметил, что такие упрощения имен допустимо делать только в зрелом коде, который уже не так часто меняется, при условии что изменение API не создаст проблем
На моём опыте люди, как правило, напротив, всё слишком переусложняют и переуточняют: _currentSelectedSpecificItemIndex. Превратить такую запись в _selectedIndex (убираем Item, поскольку, скорее всего, итак работаем в контексте какого-то типа SomeItem) — не упростить, но убрать избыточность.
А иногда что писать — знаешь, а как описать — нет. Это что же, долго перебирать варианты как описать, чтобы решить как назвать, чтобы наконец то написать?
На мой взгляд, да, нужно перебирать. Не что писать, а как — вот что важно.
Боюсь, она ровно это и утверждает. Положим, JSON-сериализацию можно сделать так:
Хотя мы и вынесли сериализацию в отдельную сущность, принцип S, как мне видится, был нарушен: теперь каждое изменение полей
Weapon
потребует изменений вWeaponSerializer
.Можно:
Теперь лучше, поскольку
Weapon
ответственен за всё, что с ним происходит, но так работать попросту неудобно, да и, к тому же, иногда не так велика разница, идти ли вниз к методуAsJson()
или к лежащему недалекоWeaponJsonSerializer
.Куда сильнее — использовать возможности метапрограммирования и реализовать обобщённую сериализацию с помощью рефлексии или кодогенерации. Библиотек полно.
Разница между
ListSorter
иSortedList
в том, что в первом случае мы говорим о списке, его сортировщике и отсортированном списке:Но ведь чувствуется, что сортировщик тут избыточен, нам важен не он и не его наличие, а результат — отсортированный список, потому что именно он представляет некоторое понятие из предметной области, скажем, рейтинг фильмов.
Например:
Смотрится элегантнее, на мой взгляд. Работа не перенаправляется на аутсорс какому-то третьему лицу.
И уже не будет иметь значения,
x
там илиorderOfSomeItemOfSomeTaskOfSomeProject
. Такой код исправляется другими средствами.Хм. Ранее вы писали, что
Set
— это "установить", а тут уже, кажется, "наступить". Также заметил несколько смутное "наличие элементов в очереди", которое никак не следует изManualResetEvent
и того, что обсуждалось ранее.Боюсь, мой пример был искажён. Не получится перенести низкоуровневую терминологию сигнальных состояний на повседневные ворота. Нет, тут всё гораздо проще: есть ворота; когда они закрыты, пройти дальше нельзя; когда открыты — можно.
Как выглядит
ManualResetEvent
, являющийсяEventWaitHandle
, неясно. А ворота выглядят так:Именно поэтому я уточнил: "а уж тип, если потребуется, дополнит происходящее".
Тут тогда, думается мне, есть проблема посерьёзнее: недостаточно разбили предметную область на сущности, поэтому есть методы с большим количеством параметров. Расширять это дело настоятельно не рекомендую. Спрятать много параметров в объект не поможет, ведь нагрузка на ум осталась!
Согласен, и такое бывает! Но это не оправдывает другие случаи, когда
Info
иData
— наспех сочинённая декомпозиция бизнес-логики.Речь не об интерфейсах и о том что они такое, а о словах, которые выбираются, чтобы отразить подразумевающееся интерфейсом. Поэтому:
Проблемы есть, ведь как понятия
Sequence
иEnumerable
очень схожи между собой, только одно основывается на уже известной последовательности, тогда как второе — на глаголе перечислять, из которого выводится, что перечисляем мы последовательность.Идея вот какая: важно подобрать такое слово, чтобы оно как можно глубже вплеталось в опыт.
IOpeneable
не восходит к картинкам, аIGate
— напротив.Думаю, нет никакой связи между тем, что пишут
HumanBase
(вместоAnimal
, например), и публичностью и непубличностью. Речь об архитектурной сложности решения. СBase
,Impl
,Internal
,Raw
и прочим ничего понятного и простого, как правило, не получается.Полагаю, наличие частного случая никак не влияет на все остальные, где
IOrdersRepository
— единственный объект, представляющий заказы.Если вы имеете ввиду нечто вроде:
То, мне кажется, куда просторнее смотрится:
Что
x > 10
— про урон, думаю, всё ещё понятно, а дышать стало легче.Понимаю ваш аргумент. Я думал над тем, чтобы повысить точность этого названия, но опыт подсказывает, от ворот в первую очередь ожидаешь, что они просто закрываются и открываются, а уж потом можно дополнить, автоматически закрываются или нет.
Например, можно убрать оттуда слово
Thread
(блокировка потока как будто следует изWaitForOpen
) и получится два типа:Gate
иAutoCloseGate
. Всё ещё понятнее, чем стандартный аналог.Полностью согласен. Среди множества всех возможных сценариев, существуют такие, в которых
_ordersRepository
смотрится лучше, чем_orders
, но в большинстве (95%), на мой взгляд,Repository
— избыточное уточнение.Мне кажется,
IOrdersRepository
как раз имеет семантику коллекции: получить заказы, добавить, удалить. Так что он вполне себе может представлять множество заказов (или заказы), а уж тип, если потребуется, дополнит происходящее.На моём опыте люди, как правило, напротив, всё слишком переусложняют и переуточняют:
_currentSelectedSpecificItemIndex
. Превратить такую запись в_selectedIndex
(убираемItem
, поскольку, скорее всего, итак работаем в контексте какого-то типаSomeItem
) — не упростить, но убрать избыточность.На мой взгляд, да, нужно перебирать. Не что писать, а как — вот что важно.