Как стать автором
Обновить

Комментарии 37

Зачем, в таком случае, нам поле, отвечающее за свежесть, если все фрукты заведомо свежие?

А чем оно вам мешает?


Наш функционал снова стал умнее, отлично.

Неа, не стал.

А чем оно вам мешает?
тем, что оно присутствует в объекте, и объект, вполне вероятно, имеет функционал для работы с этим полем, но в ряде ситуаций оно не используется. Когда я хочу работать с абстракцией этого типа, включающей в себя только те поля, которые нужны для конкретной ситуации — мне, как вариант, придется написать обёртку для этого класса, дабы упростить восприятие тех данных, с которыми я работаю. Если же абстрагировать хранение данных, скоуп можно редактировать на много быстрее.
тем, что оно присутствует в объекте, и объект, вполне вероятно, имеет функционал для работы с этим полем, но в ряде ситуаций оно не используется.

И что?


Когда я хочу работать с абстракцией этого типа, включающей в себя только те поля, которые нужны для конкретной ситуации

Такая абстракция называется "интерфейс".

И что?
Вы сами же и ответили на свой вопрос
Такая абстракция называется «интерфейс»

Писать интерфейсы для каждого среза, безусловно, можно, когда класс реализует 2-3 интерфейса. Только вот, если понадобиться делать срез по каждому свойству отдельно, вы будете в классе с 5 полями, реализовывать 5 отдельных интерфейсов?
Вы сами же и ответили на свой вопрос

Не, не понимаю. Не понимаю, зачем.


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

Если мне понадобится делать "срез" по каждой выполняемой объектом задаче, то да. Но возможно, если у вас каждый объект реализует так много задач, что вам утомительно интерфейсы поднимать, у вас что-то с SRP не так?

Но возможно, если у вас каждый объект реализует так много задач, что вам утомительно интерфейсы поднимать, у вас что-то с SRP не так?
SRP вещь хорошая, но в небольшом ряде случаев объекты, так или иначе, могут иметь много свойств. Попытка их вынести в отдельные классы закончиться тем, что у вас образуется переизбыток однотипного кода, вместо одного, конкретного механизма.
SRP вещь хорошая, но в небольшом ряде случаев объекты, так или иначе, могут иметь много свойств.

Ну да. Ну имеют они много свойств. Предположительно, связанных друг с другом, потому что иначе зачем они в одном объекте. И что с того?


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

Это какого же? Обрабатывающему коду все равно, в одном классе оно реализовано, или в разных, он через интерфейс ходит.

Это какого же? Обрабатывающему коду все равно, в одном классе оно реализовано, или в разных, он через интерфейс ходит.
Однотипных интерфейсов, когда будет необходимо делать срезы по разным группам данных, вместо одного, общего инструмента для доступа к вариативным данным, которые, в свою очередь, нужны для каких-то определенных ситуаций.

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

«Один общий инструмент для доступа к вариативным данным» — это словарь


Мы выносим ряд опциональных типов в массив(словарь)


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

Вот я и писал, что я не вижу, для какой задачи это оправдано.

Вот я и писал, что я не вижу, для какой задачи это оправдано
еще в самом начале статьи я написал, что для ситуаций, когда условия обработки объекта зависят не только от самого объекта, но и от среды, где он обрабатывается, и когда эти условия постоянно меняются. Описать все возможные комбинации выйдет дольше, чем отделить все опциональные данные(количество которых тоже может меняться) в отдельное хранилище, с которым работать отдельно от основного объекта.

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

Ну, это верно для очень большой доли разработки ПО.


Описать все возможные комбинации выйдет дольше, чем отделить все опциональные данные

Все еще не понятно, зачем отделять "опциональные данные".


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

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

Все еще не понятно, зачем отделять «опциональные данные»
для чистоты целевых объектов. То самое SRP о котором вы писали, для типов с большим количеством свойств, работа с которыми зависит не только от непосредственных вычислений, но и от внешних факторов(при условии что они часто меняются)

Если добавление "опциональных данных" делает целевые объекты "нечистыми", их там не должно было быть изначально, у вас ошибка в дизайне.


Покажите пример.

class Apple
{
public:

void methodwithColor() { }
void methodwithWeight() { }
void methodWithSize() { }

void methodwithFresh() { }
void methodWithInsects() { }
void methodWithWhole() { }

private:

int _weight;
int _color;

struct State
{
int size;

bool isFresh;
bool isInsects;
bool isWhole;
} _state;
};


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

А это точно объект, т.е. сущность, а не DTO?..


Какие операции над ним возможны?

А это точно объект, т.е. сущность, а не DTO?..
вооот, я как раз об этом же и писал вам. Это не целевой объект, целевым от него будет %30, а остальное лишь хранение неких данных, которые, тем не менее, привязаны именно к этому классу
Это не целевой объект, целевым от него будет %30

Я пока не вижу никакого объекта-как-сущности, я вижу DTO. У вас точно есть сущность с операциями?

class Apple
{
public:

bool methodwithColor()
{
return _color != 0;
}

bool methodWithWeight()
{
return _weight != 0;
}

protected:

void decayPerTick()
{
_weight--;
_color--;
}

private:

int _weight;
int _color;
};

хорошо, возможно, получилось слишком абстрактно

… а вот теперь внезапно выясняется, что на третьем тике decay яблоко должно перестать быть fresh и стать rotten. А у вас это свойство вынесено отдельно от объекта, и все его процессят самостоятельно.


Занятный у вас ОО-дизайн, ничего не скажешь. Смысл вроде был в том, что объект — это единственное, что отвечает за его состояние...

… а вот теперь внезапно выясняется, что на третьем тике decay яблоко должно перестать быть fresh и стать rotten

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

Проблема, однако же, в том, что не важно, когда оно имеет значение. Важно, что вы не можете его поменять, кроме как изнутри объекта.

это решается иммутабельностью исходных данных
пардон, я ошибся, написав про иммутабельность. Это решается передачей копии исходных данных*

Что такое "исходные данные"?

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

Я пока не понимаю, где у вас работа с объектом. То, что вы показываете — это какая-то БД в памяти.

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


Что мне не нравится в этом подходе:


  • список ключ значение откровенно говоря не оптимален
  • не понятно эти параметры это подобие саморефлексии или же реальные контейнеры данных, в любом случае это подход скорее из скриптовых языков программирования, но то что в условном питоне легко прячется за языковом сахаром в плюсах будет мозолить бойлерплейт кодом. В текущей реализации может быть так, что одни классы реально хранят данные в этих полях, а другие — нет.
  • когда мы выбрали срез, нам нужно или работать только с данными доступными через эту рефлексию, или всё ещё решать проблему обобщения типов

Как я понимаю задача организовать возможность выполнения функции над срезом контейнера разрозненных объектов где срез выполняется по некоторому формальному критерию.
Тут встаёт вопрос какой тип представлен в контейнере.
Если контейнер состоит из указателей на некий обобщённый класс то логично было бы ввести необходимый интерфейс, расширить все типы реализацией этого интерфейса и тогда целевая функция выполняет работу над интерфейсом а срез обеспечивается механизмом фильтрации по интерфейсу (различные техники, например расширить обобщённый класс методом для даункаста в желаемый интерфейс).
На практике часто встречается ситуация, когда в контейнере уже хранятся разрозненные объекты (например std::vector<std::variant<...>>) тогда самым правильным было бы реализовывать визитёр. Эта техника позволит вам отфильтровывать типы по формальным признакам содержащимся в системе типов, при этом условием может быть как присутствие полей/методов участвующих в обработке, так и любые другие формальные критерии (например наличие определённого метода).

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

Как я понимаю задача организовать возможность выполнения функции над срезом контейнера разрозненных объектов где срез выполняется по некоторому формальному критерию
именно

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

Вы, кстати, один из них и описали. Однако, я сосредоточил статью на идеи отделения опциональных данных от класса, который ими пользуется, и зачем это нужно. Если брать в расчет еще и реализации хранения разнородных объектов, то статья разрастется до неприличных размеров, хотя у меня и есть в планах написать расширенный вариант, когда сам закончу с этим.

Я как и lair выше не очень понимаю чем плохи интерфейсы. Но у меня есть вопросы к вашей реализации:


  • Если у меня есть только IVariable& я могу работать только с "опциональными" данными. Что делать в этом случае? Объявлять все поля опциональными? Это рационально? Что делать с методами?
  • Как мне кажется в представленной реализации объявляющий {{int{1},"vasya"}, {double{0},"petya"}} не равен объекту {{double{0},"petya"}, {int{1},"vasya"}}
  • Сама концепция "опциональных" данных мне кажется не правильной абстракцией для над проблемой гетерогенного хранилища.
  • Статье категорически не хватает примера иллюстрирующего проблему и решение (я имею ввиду компилирующийся пример или ссылку на godbolt.org)
Если у меня есть только IVariable& я могу работать только с «опциональными» данными. Что делать в этом случае?
это больше идеологический вопрос. Использовать только IVariable когда нужно работать с объектом — не будет рациональным никак. Само название, «опциональные» данные подразумевает то, что часть объекта мы обрабатываем отдельно, потому как эта часть не обязательна во всех возможных случаях, а является что-то вроде дополнения к основному объекту. С самим же объектом работа идет как обычно.

Как мне кажется в представленной реализации объявляющий {{int{1},«vasya»}, {double{0},«petya»}} не равен объекту {{double{0},«petya»}, {int{1},«vasya»}}
почему же, привязка идет к ключу, и в данном примере ключом выступало бы поле «name», порядок объявления не имеет значения.

Сама концепция «опциональных» данных мне кажется не правильной абстракцией для над проблемой гетерогенного хранилища
что, по вашем, было бы правильным решением?

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

Ну например так:
https://godbolt.org/z/qWdKfx
И конечно С++20 для всего этого не требуется, это всё можно сделать на С++98 с бустом.

НЛО прилетело и опубликовало эту надпись здесь
В целом, вы правы. У меня, в планах, написать более расширенный вариант данной статьи, с уже реализованными механизмами.
ИМХО, если свойств немного, то это чересчур. А если и свойств, и объектов много, то я взял бы в качестве «корзины» embedded in-memory database. И получал бы «срезы» на любой вкус. Ну а если надо экономить память — без индивидуально заточенного решения все равно не обойтись…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории