Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Май 28, 2016, 10:18



Название: Выделить класс
Отправлено: Igors от Май 28, 2016, 10:18
Добрый день

Есть такая структура (псевокод)
Код
C++ (Qt)
struct CNode {
...  
 int GetChildCount( void ) const;
 CNode * GetChild( int index );
 
// data
int mRow, mColumn;
...
};
И вот набегает набор методов посвященных манипуляциям с mRow, mColumn по чилдренам. Напр пересчитать все значения чтобы вставить новый чайлд в заданную строку/столбец. И наоборот, обеспечить нулевые минимальные строку/столбец после удаления чайлда. И другие. Это слабо связано с сущностью CNode и вполне может использоваться повторно (хотя пока такой необходимости нет).

Стоит ли сразу же выделить управление mRow/mColumn в отдельный класс? Если да то как это лучше сделать?

Спасибо


Название: Re: Выделить класс
Отправлено: Racheengel от Май 28, 2016, 10:30
а как mRow, mColumn связаны с index ?
что собой структура "физически" представляет?

если у Вас есть что то типа CNode::insertChild(int index, int row, int col), то по хорошему этот метод должен заботиться об обновлении всех зависимых значений после вставки (если я правильно идею понимаю). То же самое при удалении. А значит, по идее должен быть какой-либо внешний контроллер, который бы хранил список всех нодов и их "связи" и обновлял их при необходимости.
Но для этого надо знать структуру объектов.


Название: Re: Выделить класс
Отправлено: Igors от Май 28, 2016, 11:07
а как mRow, mColumn связаны с index ?
что собой структура "физически" представляет?
То что и нарисовано вверху. По индексу получаем указатель на чайлда который имеет поля mRow, mColumn

если у Вас есть что то типа CNode::insertChild(int index, int row, int col), то по хорошему этот метод должен заботиться об обновлении всех зависимых значений после вставки (если я правильно идею понимаю). То же самое при удалении.
Понимаете правильно, но сейчас пересчеты строки/столбца - ф-ционал конкретного класса CNode (его методы).

А значит, по идее должен быть какой-либо внешний контроллер, который бы хранил список всех нодов и их "связи" и обновлял их при необходимости.
"Хранилище" - сам CNode, он предоставляет интерфейс (см выше) для получения числа чайлдов (айтемов) и доступа к их полям строка/столбец. Напрашивается класс использующий этот интерфейс - но не завязанный жестко на CNode

Но для этого надо знать структуру объектов.
Ну вот, опять проблемы с абстрактным мЫшлением :)


Название: Re: Выделить класс
Отправлено: Racheengel от Май 29, 2016, 00:38
Ну вот, опять проблемы с абстрактным мЫшлением :)

Но Вы же ожидаете конкретного ответа, а не абстрактного? :)

Что такое row и column для айтема? по какому принципу они должны меняться, если куда-то вдруг вставляется еще один чайлд?
row и column это расположение айтема в какой-либо таблице, либо какие-либо внешние индексы, либо ... ?
Ничего этого из приведенного описания класса не видно.
О какой системе координат идет речь? Можно данные структуры предствить в "графическом" виде?


Название: Re: Выделить класс
Отправлено: Igors от Май 29, 2016, 07:03
row и column это расположение айтема в какой-либо таблице,
Да, в QGridLayout (юзер может таскать из одной ячейки в другую)
по какому принципу они должны меняться, если куда-то вдруг вставляется еще один чайлд?
Напр юзер захотел вставить айтем слева и/или сверху, для текущего QGridLayout это будет ячейка напр (-2, -5). Но такого QGridLayout не понимает, вот и надо пересчитать все row, column. Также есть требование: слева, справа (сверху, снизу) вокруг ячейки с айтемом должны быть хотя бы 1 пустой столбец (строка). И.т.д. На первый взгляд это кажется совсем простым, но мне почему-то пришлось повозюкаться с этим не один день  :) Не то чтобы "огромный" код - но ощутимый. И вот я смотрю на него и думаю: ну а CNode здесь причем? А значит...  ???


Название: Re: Выделить класс
Отправлено: Racheengel от Май 29, 2016, 09:44
Я бы наверное row и column вынес из CNode в какой-нибудь CNodeManager, в котором бы имплементировалась вся логика переносов и пересчётов.
Почему так? Потому что row и column - это "внешние" атрибуты по отношению к ноде и могут меняться независимо от ее контента.

А в менеджере будет что то типа

typedef QPair<int,int> NodeIndex;
QMap<NodeIndex, CNode*> mNode;

ну и дальше делаем как то так:

myManager->insertNode(-2, -5, new CNode(..)); // ну а тут все координаты всех нодов переколбашиваем

читаем ноду:

CNode* node = myManager->getNode(-2, -5);

и т.д. "Усе через менежер".

Что касается чайлдов, я так понимаю - это просто ссылки на связанные ноды?


Название: Re: Выделить класс
Отправлено: Igors от Май 29, 2016, 11:34
Что касается чайлдов, я так понимаю - это просто ссылки на связанные ноды?
Да

Я бы наверное row и column вынес из CNode в какой-нибудь CNodeManager, в котором бы имплементировалась вся логика переносов и пересчётов.
Такой класс уже есть, но он занимается содержательной частью CNode (там еще масса всего). Поля mRow, mColumn в эту часть не входят - они как бы сами по себе, нет зависимостей от др членов CNode
typedef QPair<int,int> NodeIndex;
QMap<NodeIndex, CNode*> mNode;
Это не вносит никакой общности, по-прежнему завязано на CNode. Зато получаем новые мелкие заботы - напр при удалении CNode, сериализации и.т.п. Не вижу ничего плохого что CNode имеет поля строка/столбец


Название: Re: Выделить класс
Отправлено: Racheengel от Май 29, 2016, 13:30
А какое преимущество дает то, что mRow, mColumn находятся в ноде, а не в менеджере?
По вашему получается, что наоборот - сама по себе нода абсолютно не зависит от ее координат.
И когда на поле появляется новая нода - то в текущем варианте Вам надо проапдейтить все объекты-ноды.
А если бы их координаты жили  только в менеджере - в сами ноды лезть бы не пришлось, достаточно обновить индексы в менеджере, и все.
Да и сериализировать и удалять проще через менеджер, имхо. Проблем я не вижу,  только преимущества.


Название: Re: Выделить класс
Отправлено: Igors от Май 30, 2016, 08:11
Да и сериализировать и удалять проще через менеджер, имхо. Проблем я не вижу,  только преимущества.
Тогда расскажите как сериализовать это

typedef QPair<int,int> NodeIndex;
QMap<NodeIndex, CNode*> mNode;

Что будем писать/читать в поток вместо указателя на CNode ?


Название: Re: Выделить класс
Отправлено: Igors от Май 30, 2016, 08:22
И когда на поле появляется новая нода - то в текущем варианте Вам надо проапдейтить все объекты-ноды.
А если бы их координаты жили  только в менеджере - в сами ноды лезть бы не пришлось, достаточно обновить индексы в менеджере, и все.
А чем это лучше? Так я пробегаюсь по нодам, а так по контейнеру манагера - так его надо иметь и хранить (где?). И нод теряет самодостаточность, нуждается в помощи менеджера.

Как-то уперлись в один вариант (связка по мапе). Ну а почему не простейшее наследование, напр
Код
C++ (Qt)
struct CNode : public GridMan {
...  
 int GetChildCount( void ) const;
 CNode * GetChild( int index );
 
// from GridMan
 virtual int GetCellCount( void ) const { return GetChildCount(); }
 virtual int & RowAt( int index )  { return GetChild(index)->mRow; }
 virtual int & ColumnAt( int index )  { return GetChild(index)->mColumn; }
 
// data
int mRow, mColumn;
...
};
Теперь GridMan - законно общий класс. Что Вы об этом думаете?


Название: Re: Выделить класс
Отправлено: Racheengel от Май 30, 2016, 10:07
По сериализации: вместо поинтера пишем id ноды.
Собсвтвенно, при десериализации восстанавливаем.
Без этого все равно никуда, как-то ж в любом случае надо связи восстанавливать.

По общему классу: а кто будет обновлять в этом случае "соседние" ноды, если текущая изменилась (перетащили там или удалили-вставили)? Откуда GridMan будет знать, кого обновлять, кого нет?


Название: Re: Выделить класс
Отправлено: Igors от Май 30, 2016, 12:07
По сериализации: вместо поинтера пишем id ноды.
Собсвтвенно, при десериализации восстанавливаем.
Без этого все равно никуда, как-то ж в любом случае надо связи восстанавливать.
Значит чтобы пользоваться такой конструкцией - надо обеспечить уникальное ID. Как-то это не очень "общо"  :)

По общему классу: а кто будет обновлять в этом случае "соседние" ноды, если текущая изменилась (перетащили там или удалили-вставили)? Откуда GridMan будет знать, кого обновлять, кого нет?
Методы делающие содержательную работу (вставки/удаления) неизбежно есть, здесь они удачно ложатся в GridMan, напр
Код
C++ (Qt)
void GridMan::SetNodePos( int index, int row, int column );
В классе CNode я его не писал т.к. он не чисто виртуальный.


Название: Re: Выделить класс
Отправлено: Racheengel от Май 30, 2016, 13:01
Ну а как у Вас сейчас связь между нодами восстанавливается?
"Общий" ID можно тупо генерировать из поинтеров на ноды, тогда они гарантированно будут уникальными.

Насчет SetNodePos не совсем понимаю.
У Вас есть ноды 1,2,3,4.
Вы меняете позицию ноды 2. Кто будет пересчитывать позиции для 1,3 и 4?
Как это устроено?


Название: Re: Выделить класс
Отправлено: Igors от Май 31, 2016, 13:30
Насчет SetNodePos не совсем понимаю.
У Вас есть ноды 1,2,3,4.
Вы меняете позицию ноды 2. Кто будет пересчитывать позиции для 1,3 и 4?
Этот общий класс (GridMan) и будет, ведь у него есть доступ к позициям всех нодов (см перекрытые виртуалы)

Все-таки наследование мне здесь не очень нравится


Название: Re: Выделить класс
Отправлено: Racheengel от Май 31, 2016, 14:14
То есть КАЖДЫЙ GridMan, от которого наследуюется КАЖДАЯ нода, имеет доступ ко всем остальным нодам?
Как то совсем не хорошо...
Вынесите GridMan в отдельный класс, пусть он будет "один для всех".
А наследование тут не нужно.


Название: Re: Выделить класс
Отправлено: Igors от Май 31, 2016, 14:43
То есть КАЖДЫЙ GridMan, от которого наследуюется КАЖДАЯ нода, имеет доступ ко всем остальным нодам?
Ко всем позициям чайлд-нодов данного нода. А про "собственно ноды" GridMan ничего не знает (что хорошо)

Вынесите GridMan в отдельный класс, пусть он будет "один для всех".
Синглтон что ли? Здесь явно не подходит

Да, туговато идет, у меня впечатление что Вы постоянно забываете условие :) Ладно, переформулируем чуть иначе

- Есть ф-ционал пересчета всех значений строк/столбцов при вставке/удалении в таблице. Ни с какой конкретной таблицей он не связан, поэтому и хочется сделать его отдельным классом (напр GridMan). Однако сами значения строк/столбцов удобнее хранить в самой таблице (или в др классе) где они непосредственно используются и сериализуются. Все что нужно GridMan - знать число ячеек и читать/писать значения строк/столбцов (не заботясь о том кому они принадлежат). Как должен выглядеть GridMan чтобы им удобно было пользоваться?


Название: Re: Выделить класс
Отправлено: Racheengel от Май 31, 2016, 15:25
Вот сейчас Вы описываете совершенно другую задачу, чем в начале...

Получается, что GridMan - это менеджер данных таблицы, который должен отслеживать вставку-удаление строк-столбцов таблицы и обновлять ее контент. То есть по сути это - некий контроллер, имеющий что-то типа OnRowInserted(MyTable* table, int row), OnColumnRemoved(MyTable* table, int column) и т.д.

А куда ноды теперь подевались?


Название: Re: Выделить класс
Отправлено: Igors от Июнь 01, 2016, 17:57
Получается, что GridMan - это менеджер данных таблицы, который должен отслеживать вставку-удаление строк-столбцов таблицы и обновлять ее контент. То есть по сути это - некий контроллер, имеющий что-то типа OnRowInserted(MyTable* table, int row), OnColumnRemoved(MyTable* table, int column) и т.д.
Хочется чтобы GridMan (и только он) отвечал за пересчет позиций всех ячеек. Ни о каком содержимом он не знает и понятия не имеет это таблица или еще кто. А обновлять контент будет тот кто его позовет (напр сама таблица). Ведь для собсно пересчетов строк/столбцов никакое UI не нужно. А вот читать пересчитанное могут многие - поэтому вариант с мапой не очень удобен.

А куда ноды теперь подевались?
Пока никуда  :), это я хочу чтобы GridMan ничего о них не знал


Название: Re: Выделить класс
Отправлено: Racheengel от Июнь 01, 2016, 21:28
Ну ок, GridMan пересчитает значения в таблице и т.д. Да, ему гуй не нужен, это контроллер и ничего больше.
Получается, для каждой таблицы свой GridMan, так?
А ноды то как обновляться будут?
И где они тогда живут то? :)


Название: Re: Выделить класс
Отправлено: Igors от Июнь 02, 2016, 08:32
Ну ок, GridMan пересчитает значения в таблице и т.д. Да, ему гуй не нужен, это контроллер и ничего больше.
Получается, для каждой таблицы свой GridMan, так?
А ноды то как обновляться будут?
И где они тогда живут то? :)
Живут они в др структуре (графе). Есть довольно обильный UI (класс окна и др) которые и будут обновлять, визуализировать и.т.п.

Вот "для каждой таблицы свой GridMan" мне не нравится. Обратите внимание что GridMan не имеет НИКАКИХ собственных данных/полей, он все берет из интерфейса "источника" (в данном случае CNode). В варианте с наследованием это недостаточно гибко - заточено на чайлды данного нода. В др ситуации напр никаких "нодов" нет, а есть просто виджеты (но опять-таки хранящие строку/столбец). Как тогда воспользоваться общим GridMan ?