Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Февраль 21, 2018, 07:59



Название: Инкапсуляция флажков
Отправлено: Igors от Февраль 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;
}
}
И вроде все "по уму" - флажок надежно засисен! Однако вскоре я получил немало хлопот. У котейнера масса методов удаления, вставки и (самое противное) перетасовки эл-тов. И на всех них забота об этом гребаном флажке повисает неприятным грузом. Может я что-то не так "трактую" и/или есть лучший способ?

Спасибо


Название: Re: Инкапсуляция флажков
Отправлено: ssoft от Февраль 21, 2018, 10:16
Это такие накладные расходы).
Если хочется иметь счетчик/кеш каких либо свойств, например флажка selected, то придется все вручную отслеживать.
При этом нужно учитывать как изменения свойств самих элементов, так и изменение состава элементов в контейнере.

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


Название: Re: Инкапсуляция флажков
Отправлено: qate от Февраль 21, 2018, 12:52
если работает быстро, то лучше пробежаться по всем


Название: Re: Инкапсуляция флажков
Отправлено: Racheengel от Февраль 21, 2018, 23:46
На хрестоматийный вопрос - хрестоматийный ответ)
Сделать еще один флажок для контейнера - bool m_isChanged.
Флажок ставим в тру внутри методов вставки-удаления-перетасовки-выделения.
Функцию "пробегания" по выбранным элементам делаем так:

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


Название: Re: Инкапсуляция флажков
Отправлено: Igors от Февраль 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 перекрывать. В общем довольно много обычных методов контейнера (что пишутся интуитивно) оказываются под ударом.


Название: Re: Инкапсуляция флажков
Отправлено: qate от Февраль 22, 2018, 12:35
Да, если эл-тов всего 10 - не проблема. Но сотни тоже возможны. И какова частота вызова HasSelected()? Выяснить это непросто даже в среднем проекте.

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


Название: Re: Инкапсуляция флажков
Отправлено: m_ax от Февраль 22, 2018, 21:43
Я бы посмотрел на концепт/паттерн так называемого trackable, который используется в boost::signals.. Т.е. по сути заменить флажёк на отдельный класс, который регестрируется менеджером флажков, и который сам сбрасывает своё состояние при удалении, перемещении и т.д.. и сообщает САМ своему менеджеру при своём изменении..

Но я сейчас подозреваю, что отморозил страшную скверну - знания, принятые на вооружения в boost'е  :)


Название: Re: Инкапсуляция флажков
Отправлено: Igors от Февраль 23, 2018, 13:59
сотни разве проблема ?
и почему сложно выяснить частоту - неясно назначение контейнера ?
вон в субд пробегают по таблице в поисках и все ок
Вы чувствуете что разговор как-то "уходит в сторону" от программирования? :)
Цитировать
А может все-таки можно не делать? И вообще, если у юзверя тормоза - пусть покупает железо помощнее (скоро докатимся и до этого)
Т.е. кто-то (дядя? начальник?) должен взять на себя ответственность и заявить (или утвердить) что brute force здесь приемлема. Так вот, насколько я знаю проект - это не так.

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

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

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


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

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

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


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


Название: Re: Инкапсуляция флажков
Отправлено: Авварон от Февраль 24, 2018, 13:53

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

Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?


Название: Re: Инкапсуляция флажков
Отправлено: Igors от Февраль 24, 2018, 14:47
Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?
Ждал этого ответа (на мой взгляд самое разумное). Правда тогда неясно кто должен быть friend'ом эл-та?


Название: Re: Инкапсуляция флажков
Отправлено: Авварон от Февраль 24, 2018, 21:29
Что мешает завернуть вектор в "контейнер2" только с функционалом счетчика флажков?
Ждал этого ответа (на мой взгляд самое разумное). Правда тогда неясно кто должен быть friend'ом эл-та?

а надо?)


Название: Re: Инкапсуляция флажков
Отправлено: ViTech от Февраль 25, 2018, 13:52
сотни разве проблема ?
и почему сложно выяснить частоту - неясно назначение контейнера ?
вон в субд пробегают по таблице в поисках и все ок
Вы чувствуете что разговор как-то "уходит в сторону" от программирования? :)

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

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

а надо?)

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


Название: Re: Инкапсуляция флажков
Отправлено: qate от Февраль 26, 2018, 08:44
Вы чувствуете что разговор как-то "уходит в сторону" от программирования? :)

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