Упростите свои Delphi-приложения - Части 3 и 4
В качестве основы нам потребуется класс/объект, который мы сможем использовать для чтения и записи настроек приложения из и в реестр Windows. Звучит вполне просто. но, как вы помните, мы подумали предусмотреть расширение функциональности в дальнейшем.
2. Требования к коду 2.1. Совместимость с Delphi 7Хотя в последние годы язык пополнился некоторыми новыми элементами, мы же пока использовать их не будем. Наша цель - компиляция кода в Delphi 7.
Вы можете задаться вопросом: "Кто еще работает в этой старой Delphi?". Я заметил, что даже сегодня, некоторые мои клиенты используют Delphi 7 для компиляции своих проектов. В дальнейших статьях я может быть покажу Вам, как сделать то, что мы делаем, с использованием современных методов, но сейчас давайте остановимся на этом.
2.2. Отсутствие "привязки" к реестру WindowsНесмотря на то, что мы будем писать код для чтения и записи данных в реестр Windows, хочется легко адаптировать его для других хранилищ, например XML или INI-файла. Да и кто знает, что будет завтра. Не исключено, что мы получим возможность писать приложения для Windows Mobile, Mac, iPhone или даже iPad (было бы неплохо), и реестра Windows на этих платформах может не оказаться.
Сейчас сфокусируемся на реестре Windows, но, как Вы уже поняли, иметь ввиду другие хранилища - хорошая идея. Главное, о чем должна болеть наша голова в данный момент - возможность хранения/загрузки настроек. Как или где они будут хранится не так важно, сделаем то, что требуется.
2.3. Еще ньюансыПока мы знаем, что нам нужно что-то, что сможет хранить наши настройки. Нам понадобится загружать и сохранять их, а также, возможно, наличие имени для каждой настройки или даже значения по умолчанию или ее описание. Мы будем хранить Целые числа, Строки, а может быть и Пароли, Даты, .
3. Время кодинга! 3.1. . ну почти .Так, . вообще, до того, как начать писать код, стоит посмотреть, как подобные вещи реализованы в VCL. Конечно, мы все можем сделать и сами, но позволить новым классам наследоваться от существующих было бы неплохой идеей. Т.к. нам нужен список, Вы можете взять, например, класс TList.
В моем случае, я знал, что хочу иметь настройки, в которых будут храниться строки, целые числа и булевы значения. После я решил, что нужны настройки и для хранения значений типа DateTime, а также некоторых других типов. В конце концов я пришел к чему-то похожему на TField и TIntegerField, TStringField, .
Итак, зная, что я буду использовать различные типы данных в настройках и хочу хранить список этих настроек, я решил, что неплохо было бы подключить к работе TObjectList.
3.2. Создание класса TdvSetting 3.2.1. Преамбула- Идентификатор (или Имя, Заголовок)
- Описание (или Подсказка)
- Значение
Необходимо иметь возможность чтения и записи значения конкретного идентификатора в реестр. Кроме того, при чтении значения, я хочу проверять, если ли уже значение у данного идентификатора, а в случае его отсутствия использовать значение по умолчанию.
Я хотел бы получать значения TdvSetting в виде String или Variant (подобно TField и TStringField), поэтому реализовал эту возможность в коде. Плюс, я хочу устанавливать значение TdvSetting. И наконец, как и с TField в VCL, я добавил код, вызывающий исключение, если потомок не реализует какой-либо метод.
Это может показаться несколько сложным, но давайте сравним TdvSetting с TField и TStringField еще раз. С TStringField Вы можете присвоить значение, используя
Оба варианта присваивания верны, но, если aField - экземпляр TField, а не TStringField - возникнет исключение. Ту же функциональность сделал и я.
Сейчас я сосредоточусь на реестре Windows, но как Вы знаете неплохо было бы иметь и другие возможности хранения. В конце концов, единственная вещь, на которой мы сейчас акцентируем внимание - хранение/загрузка некоторых настроек. Как или где они будут храниться не так важно, сначала сделаем то, что хотели.
3.2.2. Код 3.2.3. Что он делает?На самом деле, этот кусок кода практически ничего не делает. Он просто приводит создание базового класса, которые мы сможем использовать в качестве родительского для остальных классов. Попросту, мы имеет некоторую обработку ошибок и скелет для наших классов настроек.
3.3. Класс TdvStringSetting 3.3.1. ПреамбулаВ общем, класс TdvSetting предоставляет нам скелет, который мы можем использовать для создания настроек-потомков. Я уже завершил написание TdvStringSetting, TdvIntegerSetting, TdvBooleanSetting и некоторых других, но давайте начнем с TdvStringSetting.
3.3.2. Код 3.3.3. Что он делает?Т.к. у нас уже имеется скелет от TdvSetting, нам остается лишь переопределить некоторые методы и реализовать свои собственные функции. Как видите, мы добавили примерно такой же код, который имеет TStringField в VCL.
Дополнительно реализованы лишь методы SaveToRegIni и LoadFromRegIni. Они позволяют загрузить и сохранить значение настройки в реестр. Кроме того, при загрузке мы используем значение по умолчанию, если настройка не найдена в реестре. Секция реестра, откуда/куда мы будем загружать/сохранять значение, имеет такое же имя, как и идентификатор настройки.
3.4. Создание класса TdvSetting 3.4.1. ПреамбулаТеперь, когда мы спроектировали различные типы настроек, нам понадобится какой-либо контейнер для их хранения. Например, приложение может иметь несколько настроек: PrintInColor (Цветная печать), CheckForUpdates (Проверять обновления), AutoConnect (Автоподключение), . и мы должны иметь к ним доступ. Как уже было сказано ранее, я создал TdvSettings на основе TObjectList. Так мы сможем хранить ссылку на экземпляр каждого TdvSetting-объекта и иметь к ним доступ.
3.4.2. Код 3.4.3. Что он делает?Итак, это простой контейнер для нескольких объектов класса TdvSetting. Он предоставляет доступ к каждой настройке по ее индексу в списке или идентификатору (имени). Мы также сможем устанавливать корневой узел (RootKey) TdvSettings-объекта, задающий общий путь в реестре ко всем настройкам. Когда-нибудь мне захочется иметь возможность создавать настройки из базового класса и загружать их значения.
Вы заметите, что я добавил пустой метод CreateSettings. Цель - реализовать его в дочерних классах. На уровне TdvSettings мы не знаем, какие настройки у нас есть, как они называются и какой тип они имеют. В будущем же я хочу иметь возможность создавать настройки из базового класса и загружать их значения.
В TdvMyApplicationSettings я переопределю этот метод и добавлю необходимый код для настройки конкретных TdvSetting-объектов, которые мне нужны в приложении.
4. Что дальше?На данный момент у нас есть скелет, от которого мы можем оттолкнуться. Если Вы готовы повозиться, можете создать свой собственный TdvSetting и его потомков. Я уже говорил, что мне нужны были Integer, DataTime и Boolean - их и попробуйте реализовать.
Я уже неоднократно отмечал, что не существует единственного подхода к решению нашей проблемы, и старался привести и другие возможные способы. Это позволило мне прийти к тому, что есть сейчас. Например, начальные версии содержали код для TRegistryIniFile непосредственно внутри класса TdvSetting.
Наличие одного и того же кода для LoadFromRegIni и SaveToRegIni во всех потомках TdvSetting, заставило меня в скором времени призадуматься. Я решил, что не стоит создавать и уничтожать TRegistryIniFile для каждой из 20 настроек - так код пришел к своему текущему состоянию.