12.04.2011, 21:34 | #41 |
Участник
|
Цитата:
Ок. Тогда попробую подвести итоги: 1. контейнер значений (в т.ч. контейнер контейнеров) плюсы: = очень просто инициализировать значения, легко добавлять/изменять значений = стандартный функционал работает с контейнерами, поэтому проблем с примером использования не будет = единственный способ сериализации, приемлемый для передачи параметров между клиентом и сервером (за таблиц, конечно) минусы: = контейнер - константа. ВСЕГДА. любая попытка изменить контейнер приводит к пересозданию контейнера. что чревато проблемами производительности для больших наборов = контейнер - содержит только константы. не содержит ссылки. поэтому хранить ссылку на класс внутри контейнера нельзя = никакой встроенной проверки типов. нужно писать самому = никакой встроенной проверки количества данных. нужно писать самому. или надеятся на добросовестность программиста пример инициализации: container parm = [['<html>','</html>'],['<body>','</body>']]; container parm = [[myTable1, myField1], [myTable2, myField2]]; пример использования: = pack/unpack = класс keySum 2. класс (set, list, map и т.п.) плюсы: = контроль типов присутствует (хоть и минимальный) = класс не пересоздается в памяти при изменении минусы: = писать нужно гораздо больше кода чем с контейнерами примеры использования: ??? 2.1. класс (set, list, map и т.п.), хранящий экземпляры специально написанного класса плюсы: = полный контроль над значениями минусы: = писать надо очень много пример инициализации: set mySet = new Set(Types:Class); mySet.add(new MyParm(myTable1, field1)); mySet.add(new MyParm(myTable2, field2)); пример использования: ??? 2.2. класс (set, list, map и т.п.), хранящий struct: плюсы: = не надо писать собственного класса минусы: = struct не делает контроля обязательности = очень многословная запись пример инициализации: set mySet = new Set(Types:Class); struct myStruct = new Struct(Types::Integer, "age", Types::String, "name"); myStruct.value("name","Jane Dow"); myStruct.value("age", 34); mySet.add(myStruct); myStruct.value("name","Tom Gun"); myStruct.value("age", 45); mySet.add(myStruct); пример использования: ??? 2.3. класс (set, list, map и т.п.), хранящий контейнеры: = даже не знаю минусы: = бу-э-э-э... способ объединяет минусы всех способов пример инициализации: set mySet = new Set(Types:Class) mySet.add([myTable1, field1]); mySet.add([myTable2, field2]); пример использования: FactureCalcBalances_RU 3. таблицы плюсы: = полный контроль = эффективная передача клиент/сервер = достаточно очевидно для любого програмиста минусы: = нужно писать кода не меньше, чем в случае 2.1 "класс собственных классов" пример использования: NumberSeqReference.loadModule() NumberSeqReference_Ledger.loadModule() 4. xml-файл в ресурсе плюсы: = удобно передавать вместе с приложением. особенно большие наборы данных минусы: = очень неочевидно для программистов = работа с xml ресурсоемка пример использования: \Data Dictionary\Tables\BITimePeriodsMDX\Methods\importFromAOT так? вроде ничего не забыл? |
|
12.04.2011, 22:38 | #42 |
Administrator
|
По п. 2.1 пример - разноска в ГК: класс LedgerVoucherList - обертка Map, состоящий из объектов LedgerVoucherObject. Здесь формируется список проводок в ГК по ваучеру. (см. класс LedgerVoucher и метод addVoucher)
Другой пример: Объекты List и Map, хранящие в себе список контролек диалога. List хранит упорядоченный список (см Dialog.addCtrlDialogField()), а Map - ссылки на объекты (Dialog.addFieldCon() и Dialog.addDialogClass()) Struct (согласно перекрестным ссылкам) используется для задания свойств объекта. В частности у контрольки в DialogField и в классе WebApplication (тут есть типизация, а не только String)
__________________
Возможно сделать все. Вопрос времени |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
12.04.2011, 23:27 | #43 |
MCTS
|
Если честно, не совсем понимаю смысл создания отдельного динамического хранилища для статичных начальных данных (данных по умолчанию).
Зачем придумывать и создавать коллекцию для этих данных, что бы потом тратить время (и свое и процессорное ) на:
Поясню на примере. Что подразумевает использование отдельно-выделенного хранилища данных? Использование как-минимум двух методов. 1. Метод, в котором коллекция заполняется данными. X++: void initDataCollection() { ; dataCollection = new DataCollection(Type); dataCollection.append(data1); dataCollection.append(data2); dataCollection.append(data3); dataCollection.append(data4); ..... } X++: void processDataCollection() { while(dataCollection.more()) { processData(dataCollection.getData()); dataCollection.next(); } } 3. Метод, в котором сразу обрабатываются статические данные без заморочек со всякими коллекциями-хранилищами. X++: void processDefaultData()
{
;
processData(data1);
processData(data2);
processData(data3);
processData(data4);
....
}
__________________
Dynamics AX Experience Последний раз редактировалось CDR; 12.04.2011 в 23:37. |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
12.04.2011, 23:48 | #44 |
Участник
|
Цитата:
Сообщение от CDR
X++: void processDefaultData()
{
;
processData(data1);
processData(data2);
processData(data3);
processData(data4);
....
} во-первых, я забыл записать этот способ. а он обсуждался. это будет способ 5. во-вторых, чтобы так написать нужно чтобы processData был реентерабельным. в принципе, не такое уж усложняющее требование. но все-таки. в-третьих, я не зря упомянул в теме про классы. при таком подходе, класс-потомок сможет только добавлять начальные данные, но не изменять начальные данные родителя. X++: class foo { void processDefaultData() { ; processData(data1); processData(data2); processData(data3); processData(data4); .... } .... } class fooBar extends foo { void processDefaultData() { ; // как в этом методе изменить/убрать из списка data3? super(); processData(data5); .... } } в общем, бывают случаи, когда совсем статический подход не работает. но согласен - это очень хороший подход в большинстве случаев. |
|
13.04.2011, 00:25 | #45 |
MCTS
|
Цитата:
Цитата:
Задача на грани невероятного для статического набора данных.
__________________
Dynamics AX Experience |
|
13.04.2011, 12:37 | #46 |
Участник
|
Да не в функции я уперся, а в то, что требование контроля самим же программистом введенных констант - бессмысленно.
Либо константы не являются константами в прямом смысле этого слова (tablenum() - это НЕ константа), либо никто и никогда не контролирует тип этих констант. В смысле, не пишет функционала по этому контролю Т.е. тот факт, что тот или иной способ инициализации статических данных контролирует еще и тип этих самых данных - никак не может служить критерием оценки при выборе способа. Ни основным, ни дополнительным критерием. Это вообще ни на что не влияет. Ни с какой стороны Цитата:
Сообщение от mazzy
код цвета, тег html, шрифт, размеры шрифта, GUID, MAC-адрес, ip-адрес, адрес сайта в интернете, параметры границы, размеры окон по умолчанию, avi-файл отображения в progressBar, фигуры для тетриса, список начальных вопросов-ответов для Элизы, а также любые другие константы, например из аксаптовских макросов.
тысячи их. Насколько я в курсе, никто и никогда не контролирует тип значения, записанного в макросе (константе). Их просто используют, предполагая, что макрос и так имеет нужный тип. PS: Ну, считайте, я "прицепился" к фразе Цитата:
Сообщение от mazzy
минусы:
= никакой встроенной проверки типов. нужно писать самому = никакой встроенной проверки количества данных. нужно писать самому |
|
13.04.2011, 13:20 | #47 |
Участник
|
Контрпример. Программист создаёт продукт (например фреймворк классов или просто базовый класс) который не является конечным звеном в цепочке разработки. Такой программист должен предоставить удобный интерфейс для других программистов, которые будут использовать и поддерживать его разработки. Конечно можно все параметры запихнуть в один большой контейнер и сказать: хотите воспользоваться моим чудо классом - читайте документацию. При таком подходе безошибочно ввести все константы сможет разве что сам автор чудо-фреймворка, да и тот через неделю уже не вспомнит всех нюансов.
|
|
13.04.2011, 14:39 | #48 |
Участник
|
Цитата:
Сообщение от S.Kuskov
Контрпример. Программист создаёт продукт (например фреймворк классов или просто базовый класс) который не является конечным звеном в цепочке разработки. Такой программист должен предоставить удобный интерфейс для других программистов, которые будут использовать и поддерживать его разработки. Конечно можно все параметры запихнуть в один большой контейнер и сказать: хотите воспользоваться моим чудо классом - читайте документацию. При таком подходе безошибочно ввести все константы сможет разве что сам автор чудо-фреймворка, да и тот через неделю уже не вспомнит всех нюансов.
Более того, Ваш пример вообще ни о чем, поскольку абсолютно тоже самое можно сказать о любом способе инициализации статических данных. Ну, например, создан вместо контейнера Struct, или MAP, или временная таблицы. И как Вы собираетесь угадывать "что есть что" в этом "чудо-хранилище"? Опираясь на имена? Или таки будете читать документацию? Вот когда Вы создаете новую номерную серию, каким образом Вы определяете что надо заполнить в loadModulе()? Неужели из интерфейса все понятно? |
|
13.04.2011, 14:55 | #49 |
Участник
|
Цитата:
Ну скажем так, игнорировать имена я уж точно не буду. Если я знаю что мне нужно указать пару "id столбца" - "значение", то Id поля я запишу в поле с именем FieldId а значение в поле с именем Value, а не наоборот. В случае с контейнером это совсем не очевидно. |
|
13.04.2011, 17:39 | #50 |
Участник
|
Цитата:
Получается, что если элементов структуры мало, то ориентироваться в них достаточно просто вне зависимости от того, есть имя или нет. А если элементов структуры много, то ориентироваться в них сложно, опять же, вне зависимости от того, есть имя или нет. |
|
13.04.2011, 18:43 | #51 |
Участник
|
Цитата:
Цитата:
P.S.: по-мойму оффтоп получается |
|
14.04.2011, 11:13 | #52 |
Участник
|
Цитата:
Для начала, небольшое отступление. Когда я начал программировать в среде X++ я считал "не правильным" создавать временные таблицы в AOT. Логика здесь была такая: данные временные, значит, и все объекты, которые их используют должны быть временными. В том смысле, что они должны быть удалены по окончании работы кода. А объект AOT остается! Т.е. "временный объект" оказывается "постоянным". В настоящее время, я рассматриваю временные таблицы, как программный код написанный визуальными средствами. Это позволяет избавится от психологического "затыка". Так вот, возвращаясь к задаче. Если сама задача заключается в создании/изменении query, то, почему-то вариант создания этого самого query напрямую в AOT не рассматривается в принципе. "По определению". Почему собственно? Думаю, по двум причинам: 1. Тот психологический "затык", который я описал чуть выше 2. В отличие от временной таблицы query можно создать программно Итого, предлагаю вариант прямого создания Query в AOT. Со всеми предопределенными Range. ============================================= Ну, хорошо, предположим, "ломка" настолько сильная и не преодолимая, что заставить себя создать объект в AOT - невозможно . Ладно. Тогда переходим к "программистским" трюкам Как правило, для построения query создается отдельный метод класса вроде BuildQuery. И вот тут еще один интересный вопрос. А зачем вообще надо предварительно "запихивать" поля в какое-то промежуточное хранилище, если можно прямо так и писать код создания с явным указанием имен полей? Ну, там queryBuildDataSource.addRange(fieldnum(...)) Зачем здесь "лишние" посредники? Хотите модифицировать query в классах-наследниках? Да пожалуйста! Есть куча методов для этого - clearRange(), findRange() и т.п. ============================== Ну, предположим, у нас стоит задача иметь возможность делать query с разными таблицами-источниками. Т.е. одного метода создания - недостаточно. Ну, так создайте несколько методов! Не в одном классе, разумеется, а иерархия классов-наследников, каждый из которых будет работать со своими данными Это как-раз стандартный подход в Axapta. =============================== Другими словами, все предложенные решения сводятся к тому, что просто не создается временное хранилище для статических данных. Статические данные сразу используются. Без промежуточных хранилищ Вот именно про это я и говорил, что бессмысленно рассматривать подобную задачу "вообще". Нужна конкретная постановка задачи в смысле конечной цели использования статического набора. |
|
14.04.2011, 11:17 | #53 |
Участник
|
Цитата:
Тема называется: Как правильно хранить статичный набор начальных данных в классах? |
|
|
За это сообщение автора поблагодарили: S.Kuskov (-1). |
14.04.2011, 11:22 | #54 |
Участник
|
Чем объект AOT отличается от статического набора?
|
|
|
За это сообщение автора поблагодарили: S.Kuskov (0). |
14.04.2011, 11:26 | #55 |
Участник
|
Цитата:
Сообщение от Владимир Максимов
А зачем вообще надо предварительно "запихивать" поля в какое-то промежуточное хранилище, если можно прямо так и писать код создания с явным указанием имен полей? Ну, там
queryBuildDataSource.addRange(fieldnum(...)) Зачем здесь "лишние" посредники? Хотите модифицировать query в классах-наследниках? Да пожалуйста! Есть куча методов для этого - clearRange(), findRange() и т.п. |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
14.04.2011, 11:28 | #56 |
Участник
|
можно и в AOT.
об этом был способ №4 "xml-файл в ресурсе" и способ №5 "Метод, в котором сразу обрабатываются статические данные без заморочек со всякими коллекциями-хранилищами" "query в AOT" - частный случай для инициализации с query. мало того, именно такой способ я и рекомендовал всегда для работы с кверями. ищите по ключевому слову "querystr" просто тема не о кверях. |
|
14.04.2011, 11:50 | #57 |
Участник
|
Цитата:
Исходная постановка задачи после всех уточнений, замечаний, разных "но" и "если" выглядит довольно странно. Примерно так: Необходимо обеспечить запись некой "матрицы" (таблицы) значений с возможностью их последующей модификации. При этом предполагается, что возможно абсолютно произвольное изменение любой характеристики этой матрицы. Может измениться количество строк, количество столбцов, типы и расположение отдельных элементов и т.д. и т.п. И что здесь можно ответить? Да любой объект, который может хранить списки подходит для этого! Хоть обычные массивы. Другой вопрос, а подходит ли выбранное решение для конкретной ситуации? Другими словми, в исходной постановке задача решения не имеет, поскольку сводится к перечню всех возможных инструментов Axapta. Нужно четко обозначить границы задачи. Для каких целей будет использован статический набор? |
|
14.04.2011, 11:59 | #58 |
Участник
|
Цитата:
На ваш взгляд: 1. В Аксапте нет универсального способа хранения статических данных 2. для Query лучше хранить сам query в AOT. Владимир Максимов, я отключюсь в этой ветке от общения с вами. Извините. Другие мнения/дополнения будут? |
|
|
За это сообщение автора поблагодарили: Владимир Максимов (0). |
14.04.2011, 12:10 | #59 |
Участник
|
Вот, кстати, вспомнилось. Не совсем начальные данные, правда, но всё равно показательно.
Пример использования контейнеров в системе когда не надо использовать контейнеры, а надо таблицы: RTax25RegisterTrans.RegsiterValues. там хранится около дюжины самых разнотипных полей. См. например LedgerJournalCreate_Tax25AmountDiff_RU Код: ledgerJournalTrans.DocumentNum = conpeek(registerTrans.RegisterValues, #Value3); ledgerJournalTrans.DocumentDate = conpeek(registerTrans.RegisterValues, #Value4); Код: taxPay += (this.round(conpeek(_trans.RegisterValues, #Value10)) - this.round(conpeek(_trans.RegisterValues, #Value13))); Надеюсь, когда нибудь проверка BP научится бить по рукам за #define.Value13(13) от которого толку - ноль. Ещё один плюс таблиц перед containerами: фильтры. Помню была доработка по этим RTax25* где надо было выбирать определенные RegisterValues. Так как делать joinы и фильтры на container'ы внутри таблиц нельзя, то приходилось перебирать все RTax25RegisterTrans принадлежащие RTax25RegisterTable'у. Последний раз редактировалось mayk; 14.04.2011 в 12:13. |
|
|
За это сообщение автора поблагодарили: mazzy (2). |