Вебинар Delphi and Firebird, by Marco Cantu

27 апреля, в 15 GMT (17 CET или 19 MSK) состоится вебинар «Delphi and Firebird» при участии Marco Cantu, известного эксперта по Delphi.

Тема вебинара — работа в Delphi с СУБД Firebird.
Вебинар проводится в рамках кампании MindTheBird (www.mindthebird.com), приуроченной к выходу СУБД Firebird 2.5, а также празднованию 10-летия этой СУБД.
Вебинар проводится на английском языке.
Подключиться к вебинару можно будет за 30 минут до его начала, по ссылке:
https://www.livemeeting.com/cc/mtbfb/join?id=5ZQRT8&role=attend&pw=r%3A9ztTR%7DM

Можно использовать как ПО LiveMeeting (будет предложено скачать по
ссылке, если не установлено, рекомендуется), так и веб-браузер.

Читать на сайте автора.

Поломки БД и магнитные бури

В начале этой недели к нам неожиданно поперли запросы на ремонты БД (Firebird и InterBase). То есть, конечно, запросы и так периодически приходят, но тут был какой-то шквал, даже подумали, не объявить-ли мораторий 🙂
А потом я увидел в новостях, что «Сильнейшая за полтора года магнитная буря пришла на Землю во второй половине дня 5 апреля.«. И сразу стало все понятно.

Раньше, как-то, на такие вещи не обращали внимания. Но тут совсем явная корреляция 🙂

Не забывайте, сегодня в 18:00 по Москве у нас состоится, можно сказать, эпический вебинар, с участием Анны Харрисон. Если кто не в курсе, она совместно с Джимом Старки разрабатывала InterBase.

Линк для подключения к вебинару через клиента LM:
https://www.livemeeting.com/cc/ibse/join?id=93K3J8&role=attend&pw=JHJNt%5DS%3C6
Веб-линк:

https://www.livemeeting.com/cc/ibse/webjoin?id=93K3J8&role=attend&pw=JHJNt%5DS%3C6
(LM лучше, чем через веб).

Читать на сайте автора.

Firebird 2.5 вебинар — по новой архитектуре SuperClassic

6 апреля в 19:00 (Мск) состоится вебинар по новой архитектуре СУБД Firebird 2.5 — SuperClassic.
Вебинар проводится в рамках кампании MindTheBird.
Основной докладчик на вебинаре — Дмитрий Еманов, ведущий архитектор проекта Firebird.
Планируемая длительность вебинара — не более часа.

Подключиться к вебинару можно при помощи клиента LiveMeeting, открыв в любом браузере ссылку:
https://www.livemeeting.com/cc/ibse/join?id=2WB4JT&role=attend&pw=7pHfh%21_%29r

Вход на вебинар разрешен за 30 минут до его начала.

Читать на сайте автора.

Firebird 2.5 — еще один вебинар, 23 марта

Продолжаем серию вебинаров по Firebird 2.5. В этот раз в вебинаре будут участвовать разработчики Firebird.
Тематика вебинара — отчасти по мотивам предыдущего:

  • MindTheBird! — запуск Firebird 2.5 в России
  • Мониторинг ресурсоемких запросов и превентивные действия
  • обзор аудита и trace api

Планируется все это на 30-40 минут.

www.ibase.ru/webinar.html — здесь доступна информация по подключению, а также записи предыдущих вебинаров.

Читать на сайте автора.

Firebird 2.5 — что нового?

Сегодня, 2 марта, с 14 до 15-ти проводим вебинар по новой функциональности Firebird 2.5.
http://www.ibase.ru/webinar.html

Вебинар обще-обзорный, особых подробностей там не будет, так что он для тех, кто пока не имел возможности поиграться с 2.5 или почитать про новые фичи, или для тех, кто уже использует FB 2.5 RC2, но тоже не успел прочитать Release Notes.

Читать на сайте автора.

По результатам предыдущего поста (про программку с MySQL). Предыстория:
размещают на моем форуме спам. Я реагирую, и смотрю, кто такие. Думал, программа написана на Firebird. Ан нет, включает в себя MySQL. Тогда я пошел на форум, и спросил — либо исходники открывайте, либо покажите что купили коммерческую лицензию на MySQL.
Весь диалог тут:

http://clientbase.ru/forum/index.php?showtopic=1620

Общее резюме — «Мы не считаем себя нарушителями лицензии, пока обратное нам не докажет правообладатель либо законодательство.»

Просто замечательно, как мне кажется.

Читать на сайте автора.

ООП и паттерны проектирования: практическое применение.

ООП и паттерны проектирования: практическое применение.

В Интернете выложено достаточно большое количество статей на тему применения паттернов (шаблонов) проектирования. К сожалению, примеров кода на Delphi немного, да и сами примеры достаточно упрощенные, и не дают возможность оценить всю мощь использования этой технологии. Предлагаемый цикл статей – попытка показать начинающим программистам пользу применения паттернов проектирования на реальном коммерческом проекте, пошагово решая часто возникающие практические задачи.
Это не обзор паттернов, и не учебник по их применению, это – практические занятия. Надеюсь, их получится несколько.

Занятие 1. Чтение настроек программы.
Постановка задачи: программа при начальном запуске должна прочитать настройки подключения к базе данных и соединиться с ней.
Такую задачу решал, пожалуй, каждый программист, и в любом учебнике программирования Вы легко найдете решение:

procedure ConnectDataBase;
var
IniFile : TIniFile;
Section, FileName, Password, UserName : String;
Begin
IniFile := TIniFile.Create(IniFilePath);
Section := 'Main';
with IniFile do begin
FileName:= ReadString(Section,'FileName','Application.gdb');
Password:= ReadString(Section,'Password','masterkey');
UserName:= ReadString(Section,'UserName','SYSDBA');
end;
MainDatabase.Params.Clear;
MainDatabase.Params.Add('password='+ Password);
MainDatabase.Params.Add('user_name='+'UserName')
MainDatabase.FileName := FileName;
MainDatabase.Connected := true;
end;

Вроде все легко и просто?
Тогда зададим себе несколько вопросов…

А если у нас несколько подключаемых БД, да еще и с разными вариантами подключения?

А если мы хотим добавить новые параметры настройки без перекомпиляции кода?

А если мы решим использовать для сохранения настроек не Ini-файл, а реестр, или cookie?

И мы видим, что предлагаемое решение нарушает несколько принципов объектно-ориентированного программирования!

Первое нарушение – решение не приспособлено к изменениям требований (а требования к программе меняются всегда!).

Второе нарушение — нарушение принципа «разделения ответственностей» – процедура выполняет одновременно несколько обязанностей: хранит параметры настройки (FileName, Password, UserName), читает их из файла и производит подключение к БД.
Рассмотрим эти вопросы по очереди. Если у нас несколько БД – необходимо хранить отдельно настройки для каждой.Можно в одном ini-файле в разных секциях, но лучше – для каждой БД создать свой файл.

В моей программе – две базы данных, БД проекта, и БД приложения. Соответственно, будет четыре файла – ApplicationLocalConnection.ini и ApplicationRemoteConnection.ini для приложения, ProjectLocalConnection.ini и ProjectRemoteConnection.ini для проекта.
А чтобы при запуске узнать, какие соединения использовать – еще и главный ini-файл для приложения, тут удобнее использовать название ini-файла, совпадающее с названием приложения. А поскольку одни и те же настройки могут использовать разные программы, для каждой надо создать свой каталог для хранения настроек:
Bin (тут лежат сами exe-файлы)
Application1.LocalSettings (тут лежат настроечные файлы приложения 1)
Application2.LocalSettings (тут лежат настроечные файлы приложения 2)

Второй вопрос – параметры настройки… Чтобы менять настройки без перекомпиляции кода, параметры надо хранить отдельно в виде списка строк, и регистрировать при создании экземпляра класса. Тут есть два пути – можно создать один общий класс хранения настроек, и параметризовать его списком переменных, либо использовать полиморфизм, и создать несколько классов по типам настроек.

Третий вопрос — сохранения настроек различными способами (в Ini-файле, в реестр, cookie или где-то еще).

Вот тут мы используем один из главных принципов объектно-ориентированного программирования – «инкапсулируй то, что может измениться». У нас может измениться способ чтения-сохранения настроек, так «спрячем» возможные изменения за «стеной» абстракции – создадим абстрактный класс TSettingsReader – «читатель» настроек. Он описывает интерфейс для чтения и сохранения настроек без конкретизации — как это делается.

Второй принцип, который мы применим – принцип «разделения ответственностей». Каждый класс должен выполнять только то, что нужно, и ничего лишнего. И для выполнения разных обязанностей нам необходимо создать 3 класса:

— TSettingsHolder. Класс для хранения параметров настройки. Он знает — что хранить.

— TSettingsReader. Класс для чтения и сохранения их. Он знает — как хранить.

— Класс для подключения к БД. Он знает — как использовать настройки.

Как видим, каждый класс занимается своим делом и не зависит от других. Более того, все эти классы – абстрактные, а использовать мы будем конкретных наследников!

Внимательный читатель вправе задать вопрос: а где же паттерны проектирования? А они – в реализации использования наших абстрактных классов! Обратимся к классикам – к «банде четырех» (Э.Гамма, Р.Хелм, Р.Джонсон, Дж.Влиссидес. Приемы объектно-ориентированного проектирования)!
Рассмотрим описание паттерна проектирования «Мост» (Bridge).
Применимость: используйте паттерн «Мост», когда:
— хотите избежать постоянной привязки абстракции к реализации. Так, например, бывает, когда реализацию необходимо выбирать во время выполнения программы.
— и абстракции, и реализации должны расширяться новыми подклассами. В таком случае паттерн «Мост» позволяет комбинировать разные абстракции и реализации и изменять их независимо.
— изменения в реализации абстракции не должны сказываться на клиентах, то есть клиентский код не должен перекомпилироваться. — вы хотите скрыть от клиентов реализацию абстракции.

А это как раз наш случай! И вот как выглядит диаграмма для нашей конкретной реализации паттерна:

Реализация паттерна Bridge

Каковы основные результаты применения паттерна?
— отделение реализации от интерфейса. Реализации больше не имеет постоянной привязки к интерфейсу. Реализацию абстракции можно конфигурировать во время выполнения. Разделение классов TSettingsHolder и TSettingsReader устраняет зависимость от их реализации.
— повышение степени расширяемости. Можно расширять независимо иерархии классов TSettingsHolder и TSettingsReader.

Вот теперь – первый пример кода:

Класс TSettingsReader – «читатель» настроек. Описывает абстрактный интерфейс для чтения и сохранения настроек.

Для возможности добавлять параметры настройки без перекомпиляции кода они создаются динамически при инициализации,и названия параметров хранятся в отдельном IniKeys : TStringList и регистрируются в нем при создании конкретного класса.

type TSettingsReader = class
private
fIniSection: String;
fOwnerName: String;
fIsReadOnly: Boolean;
fSourcePath: String;
fDriveType : Word;
fDriveTypeDescription : String;
fIniKeys: TStringList;
procedure SetSourcePath(const Value: String);
function CheckKeyName(const KeyName: string): Boolean;
 
protected
property IniKeys : TStringList read fIniKeys;
public
class function GetStorageType: String;virtual;abstract; // В наследниках выдает способ сохранения параметров
function CheckExists: Boolean; virtual;abstract; // Проверка наличия файла или ветки реестра
procedure RegisterIniKey(const KeyName, DefaulValue : String);// Процедура регистрации параметра настройки
function SectionExists(const Section: string): Boolean; virtual;abstract;// Проверка наличия секции
property IniSection: String read fIniSection write fIniSection; // Секция хранения
property SourcePath: String read fSourcePath write SetSourcePath; // Путь к ini-файлу или ветке реестра
property IsReadOnly: Boolean read fIsReadOnly; // Доступ к записи файла (программа может быть на CD-Rom)
property DriveType : Word read fDriveType; // Тип устройства хранения
property DriveTypeDescription: String read fDriveTypeDescription; // Описание устройства хранения
property OwnerName: String read fOwnerName write fOwnerName; // Имя владельца - хранителя
constructor Create;
destructor Destroy;override;
 
function Load:Boolean;virtual; // Функция загрузки параметров из файла в IniKeys
function Update:Boolean; virtual; abstract; // Функция сохранения параметров в файле
 
function GetIniValue(const KeyName : string): string;
procedure SetIniValue(const KeyName, Value : string);
end;
 
implementation

constructor TSettingsReader.Create;
begin
inherited;
fIniKeys := TStringList.Create;
fIniKeys.CaseSensitive := False;
end;
 
destructor TSettingsReader.Destroy;
begin
IniKeys.Free;
inherited;
end;
 
function TSettingsReader.CheckKeyName(const KeyName: string): Boolean;
begin
Result := (IniKeys.IndexOfName(KeyName)>=0);
if not Result then
MsgError('['+Self.IniSection+'] '+KeyName+' not registered!', Self.OwnerName+'-GetIniValue');
end;
 
function TSettingsReader.GetIniValue(const KeyName: string): string;
begin
if CheckKeyName(KeyName) then
Result := Trim(IniKeys.Values[KeyName])
end;
 
function TSettingsReader.Load: Boolean;
begin
Result := CheckExists; // В базовом классе – только проверка. В наследниках будет добавлена сама процедура чтения.
end;
 
procedure TSettingsReader.RegisterIniKey(const KeyName, DefaulValue: String);
begin
IniKeys.Add(KeyName+'='+DefaulValue);
end;
 
procedure TSettingsReader.SetIniValue(const KeyName, Value: string);
begin
if CheckKeyName(KeyName) then
IniKeys.Values[KeyName] := Value;
end;
 
procedure TSettingsReader.SetSourcePath(const Value: String);
begin
fSourcePath := Value;
fDriveType := CheckDriveType(Value);
case fDriveType of
DRIVE_FIXED : fDriveTypeDescription := 'Fixed drive';
DRIVE_REMOTE : fDriveTypeDescription := 'Remote (network) drive';
DRIVE_CDROM : fDriveTypeDescription := 'CD-ROM drive';
DRIVE_RAMDISK : fDriveTypeDescription := 'RAM disk';
DRIVE_REMOVABLE : fDriveTypeDescription := 'Removable drive (USB)';
end;
fIsReadOnly := (fDriveType = DRIVE_CDROM); //Надо определить, не является-ли диск CD-диском?
end;
 
function CheckDriveType(const FilePath: String): Word;
var
FileDrive : string;
begin
FileDrive := ExtractFileDrive(FilePath);
Result := GetDriveType(PChar(FileDrive));
end;
 

Как видим, класс  TSettingsReader содержит две виртуальные функции Load и Update, которые, будучи перекрыты в наследниках, определяют способ чтения и сохранения настроек. В то же время класс имеет всю базовую функциональность для хранения настроек.

Класс TSettingsReader  – абстрактный, и конкретная реализация методов перекрыта в наследниках:
— TFBIniFileReader — «читатель» ini-файла

— TFBRegistryReader — «читатель» реестра

— TFBCookieReader — «читатель» файла cookie

Соответствующий экземпляр класса будет создаваться в процессе загрузки программы в зависимости от настроек.

Вот пример реализации класса для чтения из ini-файла (я намеренно убрал все проверки и реакцию на ошибки чтения/записи, чтобы не загромождать листинг, полный код можно получить по ссылке):


type TFBIniFileReader = class (TSettingsReader)
private
fIniFile: TIniFile;
function GetIniFile: TIniFile; // "Отложенное" создание Ini-File - когда понадобится!
public
class function GetStorageType: String; override;
constructor Create;
destructor Destroy; override;
function CheckExists: Boolean; override;
function SectionExists(const Section: string): Boolean; override;
function Load:Boolean; override;
function Update:Boolean; override;
property IniFile: TIniFile read GetIniFile;
end;
 
implementation;
class function TFBIniFileReader.GetStorageType: String;
begin
Result := 'IniFile';
end;
 
function TFBIniFileReader.CheckExists: Boolean;
begin
Result := FileExists(SourcePath);
end;
 
constructor TFBIniFileReader.Create;
begin
inherited;
end;
 
destructor TFBIniFileReader.Destroy;
begin
inherited;
fIniFile.Free;
end;
 
function TFBIniFileReader.Load: Boolean;
//При чтении из ini-файла при отсутствии значений им присваиваются значения по-умолчанию!
var
i: Integer;
KeyName , KeyValue, DefaultValue : string;
begin
Result := inherited Load;
if not Result then
Exit;
try
with IniFile do begin // Мы обращаемся к свойству IniFile, сам файл будет открыт в момент обращения!
for i := 0 to IniKeys.Count - 1 do begin
KeyName := IniKeys.Names[i];
DefaultValue := IniKeys.ValueFromIndex[i];
KeyValue := ReadString(IniSection, KeyName, DefaultValue);
if Trim(KeyValue) = '' then
KeyValue := DefaultValue;
IniKeys.ValueFromIndex[i] := KeyValue;
end;
Result := true;
end;
except // … реакция на ошибки чтения/записи
end;
end;
 
 
function TFBIniFileReader.Update: Boolean;
var
i: Integer;
KeyName : string;
ActualValue : string;
begin
Result := True;
if IsReadOnly then begin
MsgError('Путь: '+Self.SourcePath+#10+'Ошибка - '+ExtractFileDrive(Self.SourcePath)+
' ('+Self.DriveTypeDescription+') - только для чтения!', 'Невозможно записать ini-файл');
Exit;
end;
BackUpFile(Self.SourcePath);
try
with IniFile do begin
for i := 0 to IniKeys.Count - 1 do begin
KeyName := IniKeys.Names[i];
ActualValue := IniKeys.ValueFromIndex[i];
WriteString(IniSection, KeyName, ActualValue);
end;
UpdateFile;
end;
except //… реакция на ошибки чтения/записи
end;
end;
 
function TFBIniFileReader.GetIniFile: TIniFile; // "Отложенное" создание Ini-File - когда понадобится!
begin
if not Assigned(fIniFile) then
try
fIniFile := TIniFile.Create(Self.SourcePath);
except //… реакция на ошибки чтения/записи
end;
Result:=fIniFile;
end;
 
 
 
Опишем класс TCustomSettingsHolder, который содержит абстрактный интерфейс для хранителя настроек.
 
type TCustomSettingsHolder = class
private
fSettingsReader: TSettingsReader;
procedure SetSettingsReader(const Value: TSettingsReader);
public
class function GetConnectionType: String;virtual;abstract;//Метод класса, выдает описание типа соединения c БД
class function GetDescription : String;virtual;abstract;//Метод класса, выдает описание подключенной БД
property SettingsReader: TSettingsReader read fSettingsReader write SetSettingsReader; //Читатель настроек
end;
 
implementation
procedure TCustomSettingsHolder.SetSettingsReader (const Value: TSettingsReader);
//Как видим, хранитель настроек ссылается на абстрактный класс читателя настроек, что позволяет при инициализации
//приложения назначить конкретного наследника класса!
begin
fSettingsReader := Value;
fSettingsReader.OwnerName := Self.GetDescription;//Чтобы хранитель мог при ошибке сообщить, из какого соединения он вызван
end;


Класс TFBSettingsHolder (наследник TCustomSettingsHolder)реализует функциональность по чтению и сохранению настроек.
 
type TFBSettingsHolder = class(TCustomSettingsHolder)
public
// Начальная инициализация - создание параметров настроек.Функция должна вызываться после установки SettingsReader!
procedure Initialize; virtual; abstract;
// Чтение настроек из файла.
function ReadParameters:Boolean;virtual;
end;

От него унаследованы классы TMainConnection, TApplicationLocalConnection, TApplicationRemoteConnection,
TProjectLocalConnection, TProjectRemoteConnection, которые отличаются реализацией процедуры инициализации.
Для примера приводятся только процедуры для настроек самого приложения и локального подключения к БД:
 
procedure TMainConnection.Initialize;
begin
with Self.SettingsReader do begin
IniSection:='Main';
RegisterIniKey('SettingsStorage', 'ini'); // Способ хранения настроек
RegisterIniKey('Application', 'ApplicationLocalConnection'); // Способ подключения к БД приложения
RegisterIniKey('Project', 'ProjectLocalConnection'); // Способ подключения к БД проекта
RegisterIniKey('RootDir', 'C:Program FilesFastBase'); // Корневой каталог программы
RegisterIniKey('TempDir', 'C:Program FilesFastBaseTMP');
RegisterIniKey('LogDir', 'C:Program FilesFastBaseLog');
RegisterIniKey('WebServer', 'WebServerLocalConnection');
RegisterIniKey('RememberUser', 'no');
RegisterIniKey('Login', 'no');
RegisterIniKey('Category', 'no');
end;
end;
 
procedure TApplicationLocalConnection.Initialize;
begin
with Self.SettingsReader do begin
IniSection:='ApplicationLocalConnection';
RegisterIniKey('WorkDirectory', 'c:Program FilesFastBaseDB');
RegisterIniKey('FileName', 'Application.gdb');
RegisterIniKey('Password', 'masterkey');
RegisterIniKey('UserName', 'SYSDBA');
RegisterIniKey('ServerName', 'localhost');
end;
end;

Как видим, параметры настроек для разных классов соединения различаются, причем легко добавить либо изменить их без внесения правок в отлаженный код. Чтение настроек производится вызовом функции GetIniValue(IniKey).
На следующем занятии мы рассмотрим применение паттерна «Абстрактная фабрика».

Некие негодяи залезли на мой форум и разместили там спам-сообщение.
Я, как водится, полез по ссылке, чтобы посмотреть, что рекламируют. И, о ужас! Не удержусь от публикации ссылки — http://www.clientbase.ru/download/ — здесь можно скачать программу CRM с MySQL! А программа-то без исходников! Вот я им завтра-то позвоню, и спрошу, где исходники, или оплачивали-ли они лицензию. Контора, похоже, большая, т.к. даже номер с 8-800 зарегистрирован.

upd: по признакам «большой» контора вроде проходит — спам и телефон 8-800…, но наименование организации: ИП Гарифуллин Марат Мидхатович, ИНН 165700291820, Адрес: 420024, Казань, Ямашева, 36

Читать на сайте автора.