Недавно добавленные исходники

•  TDictionary Custom Sort  3 227

•  Fast Watermark Sources  2 992

•  3D Designer  4 751

•  Sik Screen Capture  3 259

•  Patch Maker  3 467

•  Айболит (remote control)  3 528

•  ListBox Drag & Drop  2 904

•  Доска для игры Реверси  80 792

•  Графические эффекты  3 843

•  Рисование по маске  3 172

•  Перетаскивание изображений  2 544

•  Canvas Drawing  2 672

•  Рисование Луны  2 500

•  Поворот изображения  2 094

•  Рисование стержней  2 121

•  Paint on Shape  1 525

•  Генератор кроссвордов  2 183

•  Головоломка Paletto  1 730

•  Теорема Монжа об окружностях  2 158

•  Пазл Numbrix  1 649

 

 

Архив исходников

   
  Базы данных
  Графика & Мультимедиа
  Сети & Интернет
  Система
  Разное
   

Ссылки и Баннеры ...

 

Delphi Sources

Delphi Sources

СТАТЬИ

 

. : Библиотека AcedUtils : .

 

Расширение набора стандартных функций
и классов Borland Delphi
 

Описание библиотеки AcedUtils для Borland Delphi на платформе Win32

 

Страницы | 1 | 2 | 3 |


Модуль AcedCrypto

Модуль объединяет функции для шифрования и верификации данных, а также для генерации псевдослучайных чисел.

Функции G_RC4… предназначены для шифрования данных методом RC4. Это широко распространенный поточный шифр. Возможно, по стойкости он уступает блочным шифрам, типа Rijndael или CAST, но, зато, значительно превосходит их по производительности. Не так давно в этом шифре было обнаружено несколько уязвимостей, которые касаются алгоритма заполнения внутреннего массива ключей (key scheduling algorithm). Чтобы избежать связанной с этим гипотетической опасности взлома зашифрованных данных, компания RCA, разработчик данного шифра, рекомендует пропускать первые 256 байт выходной псевдослучайной последовательности. Функции из модуля AcedCrypto написаны с учетом этого замечания. При правильном использовании метод RC4 является вполне надежным. Надо только помнить основную особенность поточных шифров – один и тот же ключ не должен применяться для шифрования различных данных. Например, ключ может вычисляться с помощью побитовой операции xor двух чисел, одно из которых является постоянным и рассчитывается как цифровая сигнатура пароля, вводимого пользователем с клавиатуры, а второе число является изменяемым и создается генератором псевдослучайных чисел перед каждым сеансом шифрования данных. Изменяемое число может сохраняться в первых байтах зашифрованного потока данных.

Следующий набор функций из AcedCrypto G_RC6… предназначен для шифрования данных методом RC6. Это блочный шифр, разработанный компанией RCA. Нормальная длина ключа при шифровании методом RC6 составляет 256 бит (32 байта). В AcedCrypto имеются функции для инициализации процесса шифрования, задания начального вектора, шифрования и дешифрования 128-битного блока данных в режиме ECB (электронная кодовая книга), шифрования и дешифрования произвольного массива байт в режимах CFB (обратная загрузка шифротекста) и OFB (обратная загрузка выходных данных) с размером блока 128 бит. В режимах CFB и OFB размер шифруемых и дешифруемых фрагментов данных должен совпадать или размер этих фрагментов должен быть кратен 16 байт. При использовании в качестве ключа шифра пароля, вводимого пользователем с клавиатуры, нужно передавать в функцию G_RC6Init в качестве ключа не саму строку, содержащую введенный пароль, а цифровую сигнатуру SHA-256, рассчитанную для строки, введенной пользователем.

Далее в модуле AcedCrypto находятся функции для расчета значения односторонней хеш-функции SHA-256 массива байт. Цифровая сигнатура SHA-256 представляет собой массив из 32 байт, который вычисляется по данным исходного массива байт таким образом, чтобы, во-первых, невозможно было подобрать другой массив байт, для которого значение сигнатуры совпало бы с данной сигнатурой, и, во-вторых, чтобы по значению цифровой сигнатуры невозможно было восстановить какие-либо значения из исходного массива байт. Алгоритм расчета SHA-256 реализован в соответствии с документом FIPS180-2. Имеются функции для расчета цифровой сигнатуры массива байт и строки символов. Кроме того, можно рассчитать сигнатуру для массива байт, представленного в виде нескольких фрагментов. Это удобно, например, при работе с данными, организованными в виде потока типа TStream.

Функции G_Random… реализуют собой генератор псевдослучайных чисел Mersenne Twister, период которого можно считать равным бесконечности. Функция G_RandomNext возвращает число типа LongWord, равномерно распределенное в интервале от 0 до $FFFFFFFF; функция G_RandomUniform генерирует псевдослучайную величину, равномерно распределенную в интервале [0.0, 1.0]; G_RandomGauss используется для моделирования непрерывной случайной величины с нормальным законом распределения. Функция G_RandomFill позволяет заполнить область памяти псевдослучайной последовательностью байт. Функция G_RandomEncryptVector шифрует внутренний вектор, используемый при генерации псевдослучайных чисел, методом RC6. Это соответствует переводу генератора на новую числовую последовательность. Следует отметить, что сама по себе выходная числовая последовательность данного генератора не является криптографически стойкой. Чтобы превратить ее в криптографически стойкую, можно воспользоваться, например, функцией G_SHA256Transform.

Модуль AcedLists

Содержит классы для работы со списками. Кроме перечисленных ниже классов, в AcedLists определены функции G_SortAscending и G_SortDescending для потокобезопасной сортировки, соответственно, по возрастанию и убыванию массива указателей. Элементы сортируемого массива сравниваются с помощью функции, адрес которой передается в качестве параметра в функции G_SortAscending и G_SortDescending. Вместо этих двух функций можно было бы оставить одну, а порядок сортировки изменять путем задания различных функций сравнения элементов массива. Однако, при изменении структуры данных пришлось бы вносить согласованные изменения во все функции сравнения. Этот путь легко может привести к ошибкам. Гораздо удобнее и надежнее иметь одну функцию сравнения элементов массива и, в зависимости от желаемого порядка сортировки, вызывать либо G_SortAscending, либо G_SortDescending.

Битовая строка TBitList

Предоставляет методы и свойства для работы со строкой бит, которую можно представить как упакованный массив элементов типа Boolean. Изначально, при создании экземпляра класса TBitList, все биты строки устанавливаются в 0, т.е. в значение False. Метод Load используется для загрузки битовой строки из бинарного потока типа TBinaryReader, произвольной области памяти или другого экземпляра класса TBitList. Кроме того, данные могут быть загружены из ключа реестра Windows. Для сохранения данных в бинарном потоке типа TBinaryWriter или в реестре Windows предназначен метод Save. К отдельным элементам битовой строки можно обратиться с помощью свойства Items. Общее число элементов возвращается и устанавливается свойством Count. Методы IndexOf, LastIndexOf используются для поиска, соответственно, вперед и назад первого установленного или сброшенного бита, начиная с указанного индекса. Функция CountOf подсчитывает общее количество установленных или сброшенных бит в массиве. Метод SetAll устанавливает все биты в значение 0 или 1. Если два экземпляра класса TBitList содержат одинаковое число элементов, к ним можно применить различные логические операции. Например, метод AndSet выполняет операцию логического умножения элементов двух списков. В результате, установленными окажутся только биты, которые были установлены в обоих списках, а все остальные биты будут установлены в 0. Аналогичные методы предусмотрены для выполнения операций: OR, XOR, NOT, AND NOT. Функция Equals используется для проверки равенства содержимого двух экземпляров класса TBitList. Функция Clone возвращает копию экземпляра TBitList.

Классы TIntegerList, TWordList

Эти классы предназначены для работы со списком значений типа Integer или Word, соответственно. Классы полностью аналогичны друг другу, поэтому рассмотрим только первый из них – класс TIntegerList. Список значений может быть сортированным или несортированным. Это зависит от свойства MaintainSorted. Если оно равно True, при выполнении операций добавления и удаления элементы списка гарантированно располагаются в порядке возрастания значений. Если свойство MaintainSorted равно False, новые элементы добавляются в конец списка, независимо от их значений. В этом случае допустимо использовать метод Insert для вставки элемента в произвольное место списка. Кроме того, можно использовать методы UnorderedRemove и UnorderedRemoveAt для быстрого удаления элементов списка. При вызове данного метода удаляемый элемент замещается последним элементом списка, после чего свойство Count уменьшается на 1. Таким образом, удаление элемента списка происходит без смещения следующих за ним элементов. Когда MaintainSorted равно True, при вызове методов Insert и UnorderedRemove/UnorderedRemoveAt возникает исключение, т.к. произвольная вставка и unordered-удаление могут нарушить порядок сортировки элементов.

Для поиска значений в списке применяется метод IndexOf, который возвращает индекс найденного элемента или -1, если искомый элемент отсутствует. Когда элементы списка отсортированы, используется быстрый алгоритм бинарного поиска, в противном случае – линейный поиск с помощью функции G_Scan_Integer из модуля AcedBinary. Иногда бывает удобнее не поддерживать список все время в отсортированном виде, так как это замедляет процесс вставки и удаления элементов. Можно просто перед началом поиска вызвать метод EnsureSorted. Тогда, если список не был отсортирован, он сортируется, а затем выполняется быстрый бинарный поиск. Естественно, это имеет смысл делать, только если поиск выполняется многократно, т.к. сама сортировка занимает больше времени, чем линейный поиск.

Аналогично классу TBitList, в TIntegerList есть методы для загрузки списка из бинарного потока типа TBinaryReader, из произвольной области памяти, из реестра Windows или из другого экземпляра класса TIntegerList. Список может быть сохранен в бинарном потоке TBinaryWriter или в реестре Windows. Кроме того, список может быть загружен из строки, содержащей данные в кодировке Base64, и может быть сохранен в виде строки в кодировке Base64. Новые элементы обычно добавляются в список методами Add или AddIfNotExists, когда необходимо избежать дублирования значений в списке. Чтобы удалить элемент из списка по индексу, вызывается метод RemoveAt, а чтобы удалить элемент с определенным значением – метод Remove. При удалении значений методами Remove и RemoveAt порядок остающихся в списке элементов не меняется. Список полностью очищается при вызове метода Clear. Функция Equals позволяет поэлементно сравнить два списка, а функция Clone возвращает копию данного экземпляра класса TIntegerList.

Когда свойство MaintainSorted равно False, экземпляр TIntegerList можно использовать в качестве стека. Значения помещаются в стек (в конец списка) методом Add, а извлекаются из стека (с конца списка) методом Pop в соответствии с правилом: "первым вошел – последним вышел". Получить последнее значение, помещенное в стек, без его удаления можно методом Peek. При вызове методов Peek и Pop количество элементов в списке не проверяется. Ответственность за то, что список не является пустым, возлагается на пользователя класса.

Обратиться к отдельным элементам списка можно через свойство ItemList, которое возвращает указатель на массив элементов типа Integer. Число используемых элементов в этом массиве определяется свойством Count. Количество элементов, под которое распределена память во внутреннем массиве, считывается и устанавливается свойством Capacity. Начальное значение этого свойства передается как параметр в конструктор класса TIntegerList.

При описании модуля AcedBinary были рассмотрены функции, позволяющие работать с массивами чисел типа Integer как с множествами, в частности, применять к ним различные логические операции. Эти операции можно производить и с экземплярами класса TIntegerList. Например, пусть имеется два отсортированных списка значений типа Integer List1 и List2, из которых необходимо получить третий список List3, содержащий значения, присутствующие в первом списке, но отсутствующие во втором. Это соответствует операции вычитания множеств. Можно создать новый экземпляр класса TIntegerList для хранения списка-результата List3, передав в его конструктор число элементов первого списка, т.к. это число соответствует максимальной длине выходного массива для случая, когда два исходных множества не имеют пересечения. Операция вычитания множеств и заполнения списка List3 выполняется одной строкой кода:

List3.Count := G_AndNotSet_Integer(List1.ItemList, List1.Count, List2.ItemList, List2.Count, List3.ItemList).

Классы ассоциированных списков

Экземпляры классов: TIntegerAssociatedList, TIntegerHashTable, TStringAssociatedList, TStringHashTable представляют собой набор ключей типа Integer или String, с каждым из которых связано значение типа TObject. Они предназначены для быстрого поиска значений в списке по ключу. Классы типа AssociatedList хранят ключи в виде сортированного списка. Классы типа HashTable реализуют ассоциативный массив (словарь) с использованием хеширования ключей. Вероятно, сортированный список лучше подходит для небольших наборов данных в пределах одной-двух сотен элементов, когда накладные расходы на хеширование не оправданны. Хеш-таблицы можно применять для больших наборов данных, т.к. при использовании хеш-функции, дающей хорошее распределение, скоростные характеристики хеш-таблицы близки к O(1) (константны).

Во всех классах реализованы операции для добавления и удаления элементов, извлечения значения по ключу. Свойство OwnValues управляет принадлежностью значений спискам. Например, если это свойство равно True, то при очистке списка методом Clear, для каждого значения типа TObject будет вызван метод Free. По умолчанию это свойство равно False. При вызове конструктора классов TStringAssociatedList, TStringHashedList можно указать, нужно ли различать регистр символов в значениях ключей типа String. Чтобы заранее распределить память под известное число элементов, добавляемых в ассоциированный список, можно передать соответствующее значение в конструктор класса или воспользоваться методом EnsureCapacity.

Связанный список TLinkedList

Список TLinkedList представляет собой упорядоченный набор элементов, каждый из которых содержит ссылку на предыдущий и последующий элементы. Связанный список удобен для последовательного перебора элементов вперед или назад, а также для добавления и удаления элементов в произвольном месте списка. Этот класс может использоваться, например, для организации очередей, работающих по принципу: "первым пришел – первым вышел", с возможностью добавления элементов в конец очереди и выборки их с начала очереди. Особенностью списка на основе класса TLinkedList является отсутствие возможности индексированного доступа к элементам. При необходимости обращения к элементам списка по индексу лучше воспользоваться другими классами, такими как TArrayList.

Свойство HeadNode экземпляра класса TLinkedList возвращает указатель на первый элемент, т.е. узел, списка. В противоположность этому свойству, TailNode возвращает указатель на последний элемент списка. С помощью этих свойств, а также полей NextNode и PrevNode узла списка TLinkedListNode, представляющих, соответственно, указатель на следующий и предыдущий элементы списка, можно перебрать все элементы списка. Значение типа TObject узла списка сохраняется в поле Value записи TLinkedListNode. Если свойство OwnValues данного экземпляра TLinkedList равно True, при очистке списка методом Clear или удалении отдельных элементов методами RemoveHead, RemoveTail или Remove для значения Value каждого удаляемого узла списка вызывается метод Free. По умолчанию свойство OwnValues равно False, и метод Free для значений не вызывается. Добавить значение в начало или в конец связанного списка можно, соответственно, функциями AddHead и AddTail, которые возвращают ссылку на добавленный узел. Чтобы вставить значение в произвольное место списка, необходимо воспользоваться функциями InsertBefore или InsertAfter, предназначенными для добавления элемента перед или после определенного узла списка. Функция IsEmpty возвращает True, если список пустой. Функции PopHeadValue и PopTailValue позволяют извлечь значение, соответственно, из первого и последнего узлов, а сами узлы удалить из связанного списка.

Список указателей TArrayList

TArrayList является аналогом класса ArrayList из .NET Framework. Он предназначен для хранения набора указателей. В конструктор класса передается предполагаемое число элементов, которое будет добавлено в список. Элементы можно загрузить методом Load из другого экземпляра класса TArrayList или из указанной области памяти. Отдельные элементы добавляются методом Add в конец списка или методом Insert в произвольное место списка. Для удаления элемента по индексу используется метод RemoveAt. Если элемент содержит ссылку на объект, производный от TObject, вызов метода RemoveAndFreeItemAt не только удалит элемент из списка, но и освободит занимаемую им память вызовом TObject.Free. Методы UnorderedRemoveAt и UnorderedRemoveAndFreeItemAt, аналогичны двум предыдущим. Они позволяют быстро удалить элемент из списка, заменив его последним элементом списка и уменьшив на 1 значение свойства Count. Эти методы могут применяться, когда порядок элементов не имеет значения. Для полной очистки списка предназначен метод Clear; для очистки списка с вызовом метода Free для каждого элемента используется метод ClearAndFreeItems. Аналогично классам TIntegerList, TWordList, в классе TArrayList имеются методы для работы со списком как со стеком. Метод Pop возвращает последний элемент списка и одновременно удаляет его, уменьшая на 1 значение свойства Count. Метод Peek возвращает последний элемент списка, не удаляя его. Поместить элемент в стек можно с помощью метода Add.

Обращение к отдельным элементам списка осуществляется через свойство ItemList, которое возвращает ссылку на массив указателей типа Pointer. Длина списка возвращается свойством Count. Общее число элементов, под которое распределена память во внутреннем массиве, определяется свойством Capacity. Свойство Count может произвольным образом изменяться. Никаких проверок при этом не выполняется. Capacity обычно превышает Count, что позволяет добавлять новые элементы в список без немедленного выделения нового блока памяти. Чтобы предельно уменьшить объем памяти, занимаемый списком, можно вызвать метод TrimToSize, который распределяет под внутренний массив область памяти, достаточную для хранения Count элементов, но не более того. После вызова этого метода свойство Capacity становится равно свойству Count. Метод Clone возвращает копию экземпляра класса TArrayList. Метод Equals поэлементно сравнивает текущий список с заданным списком и возвращает True, если все элементы, т.е. указатели, обоих списков равны, или False, если между списками есть какие-либо различия.

Чтобы найти элемент в списке TArrayList можно воспользоваться функцией IndexOf, которая сканирует внутренний массив в поисках нужного указателя. Если надо найти не просто указатель, а элемент с определенным значением признака, придется перебрать в цикле все элементы массива ItemList. Для более эффективного поиска, а также просто для упорядочивания элементов списка, можно отсортировать список методом Sort. Данный метод принимает в качестве параметра адрес функции, сравнивающей два элемента списка. Элементы сортируются по возрастанию. Когда список отсортирован, для нахождения элемента методом бинарного поиска можно воспользоваться функцией Search, которая принимает два параметра: указатель на искомое значение признака и функцию для сопоставления элемента списка с этим значением. Имеется также модификация этого метода, которая принимает два указателя на искомые значения, например, для поиска улицы в определенном городе, если эти признаки находятся в разных полях записи, описывающей адрес.

В классе TArrayList предусмотрена возможность группирования элементов. При этом элементы с одинаковым значением признака объединяются в одну группу. Группирование выполняется методом EnumerateGroups, который принимает в качестве параметра адрес функции, сравнивающей два элемента списка. При выполнении этой операции список сортируется в порядке возрастания значения признака. Функция EnumerateGroups возвращает коллекцию групп – экземпляр класса TGroupEnumerator. Число групп в этой коллекции определяется свойством GroupCount. Указатель на массив, содержащий группы, возвращается свойством GroupList класса TGroupEnumerator. Каждая группа представляется списком TArrayReadOnlyList – аналог класса TArrayList, который не позволяет добавлять и удалять элементы. Свойство ItemList этого класса возвращает указатель на массив, содержащий элементы группы. Количество элементов можно определить с помощью свойства Count. В классе TArrayReadOnlyList предусмотрены методы для линейного поиска указателя (IndexOf), для сортировки элементов группы (Sort), для бинарного поиска (Search), для выделения подгрупп (EnumerateGroups). Следует обратить внимание на то, что в экземпляре класса TArrayReadOnlyList нет собственного внутреннего массива для хранения элементов списка. Вместо этого он пользуется внутренним массивом экземпляра класса TArrayList, для которого был вызван метод EnumerateGroups. Поэтому в процессе работы с группами нельзя вносить изменения в основной экземпляр TArrayList.

Модуль AcedStorage

Содержит типы и классы, предназначенные для организации объектного хранилища данных на локальном компьютере или на файловом сервере с возможностью многопользовательского доступа. Хранение данных на файловом сервере широко использовалось до появления серверов баз данных. В настоящее время для хранения информации почти всегда используется какой-либо сервер БД, контролирующий соблюдение ограничений и целостность данных. Несмотря на очевидные достоинства такого подхода, в некоторых случаях имеет смысл пожертвовать этими возможностями в обмен на компактный формат хранения данных, высокую производительность при выборке и изменении данных, отсутствие необходимости установки серверной части приложения, объектно-ориентированное представление данных. Возложение ответственности за целостность данных на клиентское приложение приводит к некоторому усложнению кода. Тем не менее, наличие готового механизма разрешения коллизий на файловом сервере в значительной мере облегчает эту задачу.

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

В реляционных базах данных язык SQL можно рассматривать в качестве посредника между потребителем данных (клиентом) и СУБД (сервером). При подготовке и выполнении сложных запросов возникают две проблемы. Во-первых, клиент должен сгенерировать текст запроса в виде предложения на языке SQL. Если структура базы данных включает сложные связи между таблицами, конструирование SQL-запросов представляет собой нетривиальную задачу. Во-вторых, на стороне сервера должен быть произведен разбор полученного SQL-предложения, а затем, с учетом метаданных, оптимизация запроса и выполнение кода для фактической манипуляции данными. Эффективное выполнение SQL-запросов представляет собой научную проблему. С другой стороны, можно изначально отказаться от посредника в виде языка SQL при взаимодействии потребителя с хранилищем данных. Безусловно, такой подход может быть применен только ограниченно, в рамках одного приложения или группы проектов. Однако, при правильной организации структуры данных прямое обращение клиентов к данным хранилища может не только повысить производительность, но и, в конечном счете, уменьшить сложность кода, сократить затраты на разработку и сопровождение приложения, чему способствует, в частности, наличие механизма контроля версий данных.

При использовании модуля AcedStorage все данные представляются в виде объектов: таблицы – в виде экземпляров класса TSerializableCollection, записи в таблицах – экземплярами класса TSerializableObject, точнее производных от него классов. Для сортировки и поиска записей применяются индексы – объекты типа TDataIndex. К библиотеке AcedUtils прилагается демонстрационный проект – приложение, использующее для хранения данных и одновременного доступа к данным нескольких пользователей классы из модуля AcedStorage. Пример кратко описывается далее в этой статье. Рассмотрим все эти классы подробнее.

Класс TSerializableObject

Абстрактный базовый класс TSerializableObject используется для создания на его основе класса, описывающего записи таблицы данных. Конструктор этого класса является виртуальным. Он может перекрываться в классах-потомках. Каждый экземпляр TSerializableObject содержит идентификатор типа Integer (свойство ID), значение которого уникально в пределах данной коллекции. При загрузке данных объекта из бинарного потока вызывается метод Load, принимающий два параметра: ссылку на экземпляр класса TBinaryReader, из которого нужно прочитать данные, и число, соответствующее сохраненной версии данных. Понятие версии данных введено для того, чтобы была возможность изменять хранимый формат данных в процессе доработки приложения без необходимости создания специального конвертера данных под новый формат. Каждый объект должен уметь загружать в методе Load любую из своих версий до самой последней, включительно. Число, соответствующее последней, т.е. сохраняемой, версии объекта передается в качестве параметра в конструктор коллекции TSerializableCollection. В методе Load при попытке загрузить данные для версии, которая не поддерживается объектом, следует вызвать процедуру RaiseVersionNotSupported из модуля AcedConsts, в которую передаются параметры: Self и числовое значение версии, которая не может быть загружена. Для сохранения данных объекта в бинарном потоке вызывается метод Save класса TSerializableObject, в который передается ссылка на экземпляр класса TBinaryWriter. В методе Save объект должен записать свои данные в бинарный поток. В каждом классе, унаследованном от TSerializableObject, кроме методов Load и Save должны перекрываться методы Equals для проверки равенства двух экземпляров класса и Clone для создания точной копии данного экземпляра класса.

Объекты типа TSerializableObject обычно не создаются сами по себе конcтруктором Create и свойство ID в них не назначается произвольным образом. Все это делается методами коллекции TSerializableCollection, которая полностью управляет временем жизни объектов, загрузкой и сохранением их в бинарном потоке, назначением уникальных идентификаторов, согласованием изменений и прочими аспектами работы с объектами данных.

Класс TSerializableCollection

Является центральным элементом концепции объектного хранилища данных. Коллекция содержит набор объектов типа, производного от TSerializableObject, который определяется параметром ItemClassType при вызове конструктора класса TSerializableCollection. Таким образом, все элементы коллекции имеют один и тот же тип. Полиморфизм здесь не используется, т.к. возможность присутствия в коллекции объектов различного типа приводит к неоправданному усложнению механизмов работы с данными. Внутренний массив элементов, к которому можно обратиться через свойство ItemList, упорядочен по возрастанию значений первичного ключа, т.е. ID. Количество элементов в коллекции возвращается свойством Count. Для ускорения поиска элементы коллекции могут хешироваться по первичному ключу. Чтобы включить хеширование, надо установить в True значение свойства MaintainHash коллекции. По умолчанию для экономии памяти хеширование отключено. Найти элемент коллекции по значению первичного ключа или просто убедиться в том, что элемент с таким ключом присутствует в коллекции, можно с помощью метода SearchObject. Если нужен не сам объект, а его индекс во внутреннем массиве элементов, следует воспользоваться методом IndexOf. Если в коллекции мало элементов и нужно определить индекс одного из них во внутреннем массиве, можно вызвать метод ScanPointer для линейного поиска указателя. Если элементов много, функция IndexOf быстрее найдет нужный элемент методом бинарного поиска.

Коллекция может быть загружена из бинарного потока типа TBinaryReader методом Load и сохранена в бинарном потоке типа TBinaryWriter методом Save. Метод Equals поэлементно сравнивает данную коллекцию с другой коллекцией и возвращает True, если все соответствующие элементы обеих коллекций равны. Функция Clone возвращает копию коллекции, содержащую копии всех элементов и каждого из индексов. Если предполагается добавить в коллекцию большое число элементов, можно вызвать метод EnsureCapacity для резервирования места во внутренних массивах коллекции. Для полной очистки коллекции используется метод Clear. При вызове этого метода свойство Changed устанавливается в False, т.к. эта операция не предполагает фактического удаления данных на диске. Например, метод Clear вызывается перед загрузкой с диска обновленных данных коллекции. Чтобы по-настоящему удалить из коллекции все элементы, нужно вызвать метод Delete или DeleteDirect для каждого элемента коллекции. Однако, при монопольном доступе к данным и отсутствии необходимости вызова события OnItemDeleted для каждого удаляемого элемента коллекции, можно очистить ее методом Clear, а затем вручную установить свойство Changed в значение True. Это самый быстрый способ удаления всех данных.

Новые элементы коллекции создаются вызовом функции NewItem класса TSerializableCollection. При этом элемент сразу не добавляется в коллекцию. Сначала он должен быть заполнен данными. Затем, для подтверждения изменений вызывается метод EndEdit, который помещает информацию о добавлении новой записи в кэш изменений. Если новая запись не нужна, например, если пользователь нажал кнопку "Отмена" в окне добавления записи, нужно вызвать метод CancelEdit для освобождения памяти, занятой новым элементом. При необходимости корректировки данных нельзя вносить изменения непосредственно в элементы списка ItemList коллекции. Вместо этого, должен быть вызван метод BeginEdit, в который передан идентификатор изменяемого элемента коллекции. Метод BeginEdit возвращает ссылку на копию элемента, в которую можно вносить изменения. Затем эта копия передается в методы EndEdit для подтверждения изменений или CancelEdit для отмены изменений. Удалить элемент коллекции можно вызовом метода Delete с передачей в него идентификатора удаляемого элемента.

Все описанные выше манипуляции с объектами не затрагивают основной список элементов ItemList, т.е. фактический набор данных коллекции. Изменения кэшируются во внутренних массивах: InsertedItemList и DeletedItemList с числом элементов, соответственно, InsertedCount и DeletedCount. В первом из этих массивов находятся элементы, добавленные в коллекцию, а также исправленные, т.е. новые, версии измененных объектов. Во втором массиве находятся удаленные объекты и исходные, т.е. старые, версии измененных объектов. Эти массивы отсортированы в порядке убывания идентификаторов элементов. Проверить наличие кэшированных изменений вообще или изменений для элемента с конкретным идентификатором можно вызовом функции HasChanges. Чтобы применить кэшированные изменения к основному набору элементов используется метод ApplyChanges. Обычно перед вызовом этого метода проверяется наличие обновленных данных на файловом сервере. Если версия файла данных на сервере изменилась, коллекция перечитывается с диска и изменения применяются к новым данным. Управление версиями файлов будет рассмотрено далее в этом разделе. В классе TSerializableCollection предусмотрены специальные события: OnItemInserted, OnItemChanged, OnItemDeleted, которые инициируются, соответственно, при добавлении, изменении и удалении элемента основного набора данных коллекции. Метод ApplyChanges может вернуть одно из следующих значений: appChangesOk (изменения применены успешно), appChangesOriginalObjectChanged (произошла ошибка, связанная с тем, что изменяемый элемент коллекции был одновременно изменен другим пользователем), appChangesUniqueIndexViolation (ошибка, возникающая при попытке вставить значение, нарушающее уникальность одного из индексов коллекции, который не допускает дублирования значений). Чтобы очистить кэш изменений без фактической модификации данных используется метод RejectChanges.

Поясним работу с первичными ключами элементов коллекции. При создании нового элемента методом NewItem свойству ID этого элемента присваивается временное отрицательное значение, которое последовательно уменьшается для каждого следующего создаваемого элемента. При вызове ApplyChanges для добавляемого элемента временное значение ID заменяется настоящим идентификатором, который создается функцией GenerateID коллекции. При изменении идентификатора объекта в коллекции инициируется событие OnItemIDChanged, чтобы можно было обновить внешние ключи в элементах других коллекций, ссылающихся на добавленный элемент. В экземпляре класса TSerializableCollection предполагается, что элементы сохраняют значение свойства ID в виде числа типа Integer. Если заранее известно, что число элементов коллекции не превысит 65535, можно хранить уникальные идентификаторы как 2-байтные значения типа Word. Тогда вместо класса TSerializableCollection надо использовать производный от него класс TWordPrimaryKeyCollection, в котором перекрывается метод GenerateID, чтобы значения первичного ключа не превышали 65535. Если в коллекции может быть не более 255 элементов, имеет смысл хранить ID как один байт. Тогда в качестве коллекции нужно использовать класс TBytePrimaryKeyCollection. Возможна также ситуация, когда вообще нет смысла сохранять уникальный идентификатор вместе с данными объекта. Значение ID может динамически назначаться в момент загрузки коллекции из бинарного потока. Для реализации такой возможности предусмотрен класс TFakePrimaryKeyCollection. При отказе от хранения идентификатора возникает проблема удаления элементов в режиме многопользовательского доступа, т.к. после удаления элемента и повторной загрузки коллекции динамические идентификаторы следующих за ним элементов изменятся. Таким образом, при работе с TFakePrimaryKeyCollection в режиме многопользовательского доступа удаление отдельных элементов коллекции должно быть запрещено.

В классе TSerializableCollection есть еще пара методов для работы с данными в обход кэша изменений. Метод EndEditDirect подтверждает изменение или добавление нового элемента аналогично методу EndEdit, а затем сразу применяет это изменение к основному набору данных, что эквивалентно вызову метода ApplyChanges с идентификатором только что измененного или добавленного объекта. Метод DeleteDirect удаляет элемент с указанным идентификатором из основного набора данных. Методы EndEditDirect и DeleteDirect подходят для работы с данными в режиме монопольного доступа, когда другие пользователи не имеют возможности изменить файл данных одновременно с текущими изменениями.

Каждая коллекция, не являющаяся частью какого-либо объекта, хранится на диске в виде отдельного файла. Для загрузки коллекции из файла предназначен метод LoadFile класса TSerializableCollection, для ее сохранения в файле – метод SaveFileDirect. Второй метод не предназначен для работы с данными в режиме многопользовательского доступа, т.к. он перезаписывает любые изменения, сделанные другими пользователями. При одновременной работе нескольких пользователей для сохранения изменений необходимо открыть файл данных методом OpenFile, затем применить кэшированные изменения к данным методом ApplyChanges. Если изменения применены успешно, можно сохранить коллекцию на диске вызовом метода SaveIfChanged. Если во время применения изменений к данным произошла ошибка, необходимо восстановить исходное состояние набора данных в памяти. Для этого используется метод UndoIfChanged коллекции при открытом файле данных. В конце операции файл данных должен быть закрыт методом CloseFile.

Данные коллекции могут сохраняться на диске в упакованном виде. Для этого в методы OpenFile и SaveFileDirect передается параметр CompressionMode, который, если он отличен от dcmNoCompression, задает режим сжатия записываемого файла данных (одна из констант, перечисленных в описании модуля AcedCompression). Не следует, однако, применять сжатие к файлам, которые содержат часто изменяемые данные, так как это может значительно понизить общую производительность системы, особенно в режиме многопользовательского доступа к данным. В упакованном виде лучше хранить справочники, коды и прочие данные, которые меняются редко. В методы LoadFile, OpenFile и SaveFileDirect передается также параметр EncryptionKey. Он позволяет указать ключ для шифрования файла данных методом RC6. При использовании шифрования, данные, кроме всего прочего, защищаются цифровой сигнатурой SHA-256. Так же как и сжатие, шифрование не стоит использовать для часто изменяемых данных без крайней на то необходимости.
 

 

Страницы | 1 | 2 | 3 |

 

Дата: 10.08.2005, Автор: Андрей Дрязгов, Сайт: http://acedutils.narod.ru.



Назад

 

Скачать статью с исходником (300 Кб)

   

 






























































































































































































































































































































































 

© 2004-2024 "DS"

Соглашение пользователя / Реклама / Карта сайта             Created by BrokenByte Software