Russian Qt Forum
Ноябрь 22, 2024, 09:03 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Инкапсуляция флажков  (Прочитано 8494 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Февраль 21, 2018, 07:59 »

Добрый день

Задача выглядит очень "хрестоматийной". Есть класс (Container) элементы которого (Element) имеют флажок selected. Нередко требуется узнать а есть ли в контейнере элементы с этим флажком = true (взведенным). Др словами выбрал ли юзер чего-нибудь. Пробегать всякий раз по всему контейнеру... Не, ну оно конечно просто и надежно, но уж очень коряво, да и тормоза возможны. Я пошел по очевидному пути: сделал член флажок private и объявил Container friend'ом. Ну и нарисовал (псевдокод)
Код
C++ (Qt)
void Container::SetSelectFlag( Element * elem, bool on )
{
if (elem->mSelected != on) {
 m_SelectedCount += on ? 1 : -1;
 elem->m_Selected = on;
}
}
И вроде все "по уму" - флажок надежно засисен! Однако вскоре я получил немало хлопот. У котейнера масса методов удаления, вставки и (самое противное) перетасовки эл-тов. И на всех них забота об этом гребаном флажке повисает неприятным грузом. Может я что-то не так "трактую" и/или есть лучший способ?

Спасибо
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #1 : Февраль 21, 2018, 10:16 »

Это такие накладные расходы).
Если хочется иметь счетчик/кеш каких либо свойств, например флажка selected, то придется все вручную отслеживать.
При этом нужно учитывать как изменения свойств самих элементов, так и изменение состава элементов в контейнере.

Вариант 1. Перебирать все элементы и кешировать значение. Это можно делать периодически или в момент запроса.
Вариант 2. Менять свойства элементов только через методы контейнера.
Вариант 3. Организовывать обратную связь между элементами и контейнером. Ассоциация, как в примере, сигнал-слот, паттерн Observer и т.п.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1177


Просмотр профиля
« Ответ #2 : Февраль 21, 2018, 12:52 »

если работает быстро, то лучше пробежаться по всем
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #3 : Февраль 21, 2018, 23:46 »

На хрестоматийный вопрос - хрестоматийный ответ)
Сделать еще один флажок для контейнера - bool m_isChanged.
Флажок ставим в тру внутри методов вставки-удаления-перетасовки-выделения.
Функцию "пробегания" по выбранным элементам делаем так:

int getSelectedCount()
{
if (m_isChanged)
{
  m_isChanged = false;
  m_count = // тут пробегаемся, а шо делать, и считаем выделенное...
}
return m_count;
}
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Февраль 22, 2018, 10:06 »

На хрестоматийный вопрос - хрестоматийный ответ)
Сделать еще один флажок для контейнера - bool m_isChanged.
Флажок ставим в тру внутри методов вставки-удаления-перетасовки-выделения.
Функцию "пробегания" по выбранным элементам делаем так:
Да, "lazy" вполне хрестоматийно, доп флажок часто называют типа m_dirty. Но ничего особо хорошего не видно. Добавили еще член данных. Связались с mutable (метод getSelectedCount наверняка потребуется константным).  И.. чего добились? Да особо ничего - все равно "нужно быть (предельно) внимательным", установка dirty  по-прежнему "на ручняке", компилятор никак не поможет. Ну разве что dirty = true легче воткнуть первой строкой во всех методах - не густо. Я сделал метод CalcSelectedAll и вызываю его если возможны неясности - тоже не блеск.

если работает быстро, то лучше пробежаться по всем
А так тонем в подробностях "если". Да, если эл-тов всего 10 - не проблема. Но сотни тоже возможны. И какова частота вызова HasSelected()? Выяснить это непросто даже в среднем проекте.

Вариант 1. Перебирать все элементы и кешировать значение. Это можно делать периодически или в момент запроса.
Вариант 2. Менять свойства элементов только через методы контейнера.
Вариант 3. Организовывать обратную связь между элементами и контейнером. Ассоциация, как в примере, сигнал-слот, паттерн Observer и т.п.
Насколько я понял, 1 - что предложил Racheengel, 2 - стартовый пост, 3 - не понял (ох уж этот обстервер - так я его ни разу и не применил Улыбающийся).

Собсно после нескольких сеансов отладки все работает вполне прилично. Попробуем присмотреться где обычно недосмотры. Ну конечно удаление эл-та "извне" я сразу учел
Код
C++ (Qt)
void Container::DeleteElement( Element * elem )
{
m_SelectedCount -= elem->m_Selected ? 1 : 0;
Q_ASSERT(m_SelectedCount >= 0);
// и удаляем эл-т
}
Но вот банальное clear() - прощелкал. Также есть неск ситуаций "take", т.е. извлекаем, потом возможно вставляем, но может и нет, удаляем. Приходится и take/insert перекрывать. В общем довольно много обычных методов контейнера (что пишутся интуитивно) оказываются под ударом.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1177


Просмотр профиля
« Ответ #5 : Февраль 22, 2018, 12:35 »

Да, если эл-тов всего 10 - не проблема. Но сотни тоже возможны. И какова частота вызова HasSelected()? Выяснить это непросто даже в среднем проекте.

сотни разве проблема ?
и почему сложно выяснить частоту - неясно назначение контейнера ?
вон в субд пробегают по таблице в поисках и все ок
Записан
m_ax
Джедай : наставник для всех
*******
Online Online

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Февраль 22, 2018, 21:43 »

Я бы посмотрел на концепт/паттерн так называемого trackable, который используется в boost::signals.. Т.е. по сути заменить флажёк на отдельный класс, который регестрируется менеджером флажков, и который сам сбрасывает своё состояние при удалении, перемещении и т.д.. и сообщает САМ своему менеджеру при своём изменении..

Но я сейчас подозреваю, что отморозил страшную скверну - знания, принятые на вооружения в boost'е  Улыбающийся
« Последнее редактирование: Февраль 22, 2018, 21:56 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Февраль 23, 2018, 13:59 »

сотни разве проблема ?
и почему сложно выяснить частоту - неясно назначение контейнера ?
вон в субд пробегают по таблице в поисках и все ок
Вы чувствуете что разговор как-то "уходит в сторону" от программирования? Улыбающийся
Цитировать
А может все-таки можно не делать? И вообще, если у юзверя тормоза - пусть покупает железо помощнее (скоро докатимся и до этого)
Т.е. кто-то (дядя? начальник?) должен взять на себя ответственность и заявить (или утвердить) что brute force здесь приемлема. Так вот, насколько я знаю проект - это не так.

Я бы посмотрел на концепт/паттерн так называемого trackable, который используется в boost::signals.. Т.е. по сути заменить флажёк на отдельный класс, который регестрируется менеджером флажков, и который сам сбрасывает своё состояние при удалении, перемещении и т.д.. и сообщает САМ своему менеджеру при своём изменении..

Но я сейчас подозреваю, что отморозил страшную скверну - знания, принятые на вооружения в boost'е  Улыбающийся
Ну организовывать сигналы/оповещения для флажка/счетчика - слишком жирно будет. И почему "сброс состояния"? (установка dirty). Это для случаев когда операция трудоемка (напр пересчет айтемов в лайауте) и приходится делать ее "по запросу" (а не на всякое изменение). Изменить счетчик флажков такой операцией не является. Кстати, а что за паттерн, навскидку гугл ничего о нем не выдает.

Сейчас контейнер сделан так (псевдокод)
Код
C++ (Qt)
struct Container {
...
 QVector<Element *> mElements;
};
 
При этом внешние попытки нарушить корректность счетчика флажков легко пресекаются. Но конечно ф-ционал льется в класс контейнер (нормальное желание работать с классом его методами). Поэтому внутри Container'а вызов mElements.clear(), erase() и.т.п. носит массовый характер, уследить тяжело. Вот если бы как-то запретить вызов этих методов, тем самым заставить вызывать другие (что учитывают флажок). Но пока не вижу как это удобно сделать
Записан
m_ax
Джедай : наставник для всех
*******
Online Online

Сообщений: 2095



Просмотр профиля
« Ответ #8 : Февраль 23, 2018, 18:56 »

Цитировать
Ну организовывать сигналы/оповещения для флажка/счетчика - слишком жирно будет.
Я не говорю о сигналах в их привычном понимании..
Идея в том, чтобы переложить контроль над текущим состоянием флажка не на методы Вашего Container, а на функционал самого флажка (он, конечно, теперь должен быть классом, с деструктором, в котором при его уничтожении выставляется соответствующая информация - я самовыпилился и т.д. и другими методами).  И +  дружественный классу флажка менеджер, который будет регестрировать объекты флажков, если они создаются и кладутся в Container. Элемент контэйнера удалился, у флажка сработал деструктор, который сообщил менеджеру о том, что всё - меня нет)

У меня, к сожалению, сейчас нет возможности привести минимальный код, и, наверное не получится это сделать до след. воскресенья.. Но, надеюсь, идея более-менее ясна)

Цитировать
Кстати, а что за паттерн, навскидку гугл ничего о нем не выдает.
И не найдёте) Это класс такой в boost::signals) У меня здесь, на этом форуме, в теме про доморощенную реализацию механизма сигнал-слот  какие то слова на эту тему есть..) Где то)
« Последнее редактирование: Февраль 23, 2018, 19:05 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Февраль 24, 2018, 13:39 »

Я не говорю о сигналах в их привычном понимании..
Идея в том, чтобы переложить контроль над текущим состоянием флажка не на методы Вашего Container, а на функционал самого флажка (он, конечно, теперь должен быть классом, с деструктором, в котором при его уничтожении выставляется соответствующая информация - я самовыпилился и т.д. и другими методами).  И +  дружественный классу флажка менеджер, который будет регестрировать объекты флажков, если они создаются и кладутся в Container. Элемент контэйнера удалился, у флажка сработал деструктор, который сообщил менеджеру о том, что всё - меня нет)
Чтобы самовыпиливаться нужно создать у эл-та поле m_owner, это достаточно обязывающее решение/зависимость. Напр "take" - эл-т извлечен из контейнера (на не удален). С менеджером тоже как-то мутно, неясно на основании чего он будет что-то решать/делать
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #10 : Февраль 24, 2018, 13:53 »


При этом внешние попытки нарушить корректность счетчика флажков легко пресекаются. Но конечно ф-ционал льется в класс контейнер (нормальное желание работать с классом его методами). Поэтому внутри Container'а вызов mElements.clear(), erase() и.т.п. носит массовый характер, уследить тяжело. Вот если бы как-то запретить вызов этих методов, тем самым заставить вызывать другие (что учитывают флажок). Но пока не вижу как это удобно сделать

Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Февраль 24, 2018, 14:47 »

Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?
Ждал этого ответа (на мой взгляд самое разумное). Правда тогда неясно кто должен быть friend'ом эл-та?
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #12 : Февраль 24, 2018, 21:29 »

Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?
Ждал этого ответа (на мой взгляд самое разумное). Правда тогда неясно кто должен быть friend'ом эл-та?

а надо?)
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #13 : Февраль 25, 2018, 13:52 »

сотни разве проблема ?
и почему сложно выяснить частоту - неясно назначение контейнера ?
вон в субд пробегают по таблице в поисках и все ок
Вы чувствуете что разговор как-то "уходит в сторону" от программирования? Улыбающийся

Разговор как раз в сторону программирования и двигался, уходя от кодирования.

Ждал этого ответа (на мой взгляд самое разумное). Правда тогда неясно кто должен быть friend'ом эл-та?

а надо?)

Я бы ещё задумался над тем, нужно ли самому элементу вообще знать, что его кто-то где-то "выбрал" Улыбающийся.
Записан

Пока сам не сделаешь...
qate
Супер
******
Offline Offline

Сообщений: 1177


Просмотр профиля
« Ответ #14 : Февраль 26, 2018, 08:44 »

Вы чувствуете что разговор как-то "уходит в сторону" от программирования? Улыбающийся

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

Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.111 секунд. Запросов: 23.