Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 18.08.2013, 00:00   #1
DrunkPacifist
 
Регистрация: 05.03.2011
Сообщений: 5
По умолчанию Вопрос по ООП

Здравствуйте. Пожалуйста, помогите с оформлением класса, обеспечивающего доступ к БД. Дело в том, что у меня не очень богатый опыт работы с ООП в Delphi.

Есть программа, которая должна работать с базами данных через UniDAC (или ADO, не суть важно), при этом сами базы могут отличаться - это может быть как SQL Server, так и Oracle, так и любая другая. Само собой, это подразумевает наличие специфических для каждой БД особенностей и запросов - например, для получения списка всех таблиц в базе данных SQL Server предполагается использовать, например, 'EXEC sp_databases', который естественно не будет работать с Oracle.

На мой взгляд хорошим решением было бы создание класса, в котором хранятся все методы работы с базой данных, а при его создании в конструкторе Create указывать тип БД (SQLServer или Oracle)
Цитата:
constructor TDBEngine.Create(AProvider : TDBProviderName);
и чтобы в зависимости от этого параметра при последующем выполнении, скажем, функции, которая бы отображала список всех баз данных на сервере
Цитата:
Function TDBEngine.GetServerStructure : TTreeNode;
выполнялась функция, соответствующая текущему типу БД, т.е.
GetServerStructure заменялась бы на функции GetOracleStructure или GetSQLServerStructure, содержащиеся в том же классе.

Вопрос - можно ли это реализовать и как именно? Заранее благодарен!

P.S. Возможно стоить сделать класс-наследник базового TDBEngine для каждой базы данных (например TDBOracleEngine) с заменой всех специфических для БД методов своими, и при создании экземпляра из главной формы выбирать тот, который нужен?
Цитата:
//что-то вроде этого
Case ProviderName of
dbSQLServer : DB := TDBSQLEngine.Create;
dbOracle : DB := TDBOracleEngine.Create;
End;
P.P.S. Надеюсь я изложил проблему не слишком сумбурно, если что, то поправьте меня

Последний раз редактировалось DrunkPacifist; 18.08.2013 в 00:10.
DrunkPacifist вне форума Ответить с цитированием
Старый 18.08.2013, 00:47   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Тут проблема не в компоненте, а вообще в возможности программного адаптирования запросов любой сложности под тип СУБД. Диалекты языков и возможности СУБД достаточно различаются и очень сомневаюсь, что реально можно это осуществить. Разве что принудительно ограничиться возможностями стандарта SQL/89, и то проблемно. ADO-провайдеры не зря делают под каждую СУБД
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 18.08.2013, 01:48   #3
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,434
По умолчанию

В реализации не шибко сложно.
Пусть будет объект TMyDB.
У него есть поле fCase(fCase: type TDBCase =(cAccess,cSQLServer,cPostgree,cOra cle,...), которое указывает тип базы данных, на основе этого типа, в конструкторе вы специфически подготавливаетесь к подключению.
Далее общие методы БД:
TMyDB.Select(aTpl:TDBTemplate; aArgs:array of variant);
TMyDB.Insert(aTpl:TDBTemplate; aArgs:array of variant);
TMyDB.иТ.Д.(aTpl:TDBTemplate; aArgs:array of variant);

Вот тут самое интересное:
Объект TDBTemplate(его вы тоже реализуете), является шаблоном нужного вам запроса,
содержит массив из строк, и метод подстановки параметров aArgs.

Размера массива строк равен размеру(кол-во вариантов) TMyDB.fCase.
Т.е. каждая строка это отдельный вариант запроса для какой-то БД fCase.
и далее ваш TDBTemplate в зависимости от TMyDB.fCase выбирает нужны вариант и подставляет в него параметры из массива aArgs, затем передает SQL запрос все в тот же общий метод обращения к БД вашего TMyDB, где в зависимости от fCase вы специфично отсылаете запрос и так же получаете ответ.

Получается что-то вроде шаблонов. И эти шаблоны надо где-то хранить.

Пример привести не могу т.к. знаю пока MS Access да и то через пень-колоду.

Цитата:
P.S. Возможно стоить сделать класс-наследник базового TDBEngine для каждой базы данных (например TDBOracleEngine) с заменой всех специфических для БД методов своими, и при создании экземпляра из главной формы выбирать тот, который нужен?
Ну вы же в Delphi программируете.
Начиная с TObject и заканчивая отображением IDE базовый предок TObject оброс overMilliard свойств, параметров, методов, функций.


Да, можно.
НО лучше не обьекты а интерфейсы и вот почему:
в случае с объектами можно только так:
Код:
type Доски = class(Дерево)
type Гвозди = class(Сталь)

type Забор = Class(Доски, Гвозди) //Обломчик, наследование 2 классов низя
Но забор должен обладать и гвоздями и досками, как же быть?

Пользовать интерфейсы!

В случае с интерфейсами же:
Код:
type
  //Опишим интерфейсы
  {
  Наследование интерфейсов, такое же как в ООП
 IUnknown - базовый интерфейс для всех
  }
  IDerevo = interface(IUnknown)
    ['{B463C695-03C5-4024-9D9E-EF0E8BC2F7C7}']
    //Описывает только дерево
  end;

  IDoski = interface(IUnknown)
    ['{048DB602-4277-44D3-AB3F-AA813A1ABE78}']
    //описывает только  доски, не имеет ничего общего с деревом
  end;

  IGVozdi = interface(IUnknown)
    ['{4A8760C9-12F6-4A8E-BF39-5B620CC71A8B}']
    //Описывает только гвозди
  end;

  IZAbor = interface(IUnknown)
    ['{9ED3AF2A-F842-4F72-913B-123956433F09}']
    //Вообще не относится к доскам и гвоздям
  end;

  {
  Опишем интерфейсные объекты, которые реализуют и совмещают
   в себе функционал отдельных интерфейсов
  }

  TDerevo = class(TInterfacedObject, IDerevo)
    //Реализует IDerevo и его возможности
  end;

  TDoski = class(TInterfacedObject, IDerevo, IDoski)
    // реализует IDoski, наследует IDerevo и его возможности
  end;

  TGvozdi = class(TInterfacedObject, IGVozdi)
    //Ну это гвозди, они изначально из ниоткуда :d
  end;

  TZabor = class(TInterfacedObject, IZAbor, IDoski, IGVozdi)
  //Забор тоже сам по себе изначально
  //Но теперь реализует IZabor и обладает методами и свойствами досок и гвоздей
  end;
Наследование интерфейсов такое же же, как в ООП, а вот интерфейсные объекты могут залпом хавать пачки вообще левых интерфейсов и юзать их.

Надеюсь вам это как-то пригодится.

Последний раз редактировалось Человек_Борща; 18.08.2013 в 02:22.
Человек_Борща вне форума Ответить с цитированием
Старый 18.08.2013, 11:16   #4
DrunkPacifist
 
Регистрация: 05.03.2011
Сообщений: 5
По умолчанию

Цитата:
Вот тут самое интересное:
Объект TDBTemplate(его вы тоже реализуете), является шаблоном нужного вам запроса,
содержит массив из строк, и метод подстановки параметров aArgs.

Размера массива строк равен размеру(кол-во вариантов) TMyDB.fCase.
Т.е. каждая строка это отдельный вариант запроса для какой-то БД fCase.
и далее ваш TDBTemplate в зависимости от TMyDB.fCase выбирает нужны вариант и подставляет в него параметры из массива aArgs, затем передает SQL запрос все в тот же общий метод обращения к БД вашего TMyDB, где в зависимости от fCase вы специфично отсылаете запрос и так же получаете ответ.
В том то и дело, что отдельным вариантом запроса все не ограничивается, требуется отдельный метод для их обработки для каждой БД вместо одного общего в TMyDB. Вопрос: как сделать чтобы в зависимости от TMyDB.fCase вызывался специфический для текущей БД метод?
DrunkPacifist вне форума Ответить с цитированием
Старый 18.08.2013, 14:54   #5
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,434
По умолчанию

Цитата:
В том то и дело, что отдельным вариантом запроса все не ограничивается, требуется отдельный метод для их обработки для каждой БД вместо одного общего в TMyDB. Вопрос: как сделать чтобы в зависимости от TMyDB.fCase вызывался специфический для текущей БД метод?
Ну опять вы все с ног на голову перевернули

А вы сделаете 1 общий для всех.
Допустим та же выборка из БД SELECT.
Код:
 procedure TMyDB.Select(aTpl:TDBQuertyTemplate; aArgs:array of variant);
 var
   sQuerty:string;
 begin
  aQuerty:=aTpl.ConstructSQL(fCase,aArgs); //Указанный шаблон тупо отдает готовый SQL код
  case fCase of //Тут мы сами рулим правилами отправки SQL для каждого типа БД.
    cAccess:
      begin
        fADOQuerty.Close; //И не надо для каждой БД руками все писать.
        // Берите в оборот существующие библиотеки
        fADOQuerty.SQL.Clear;
        fADOQuerty.SQL.Add(sQuerty);
        fADOQuerty.Open; 
      end;

    cMSSQL:
      begin
        //Как для MS SQL я не знаю, но думаю, что метод будет отличаться от ADO.
      end;
  end;
 end;
Человек_Борща вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Вопрос по ООП frem-dag Общие вопросы C/C++ 3 17.10.2010 17:45
Вопрос про ООП Unconnected Общие вопросы Delphi 15 13.02.2010 20:22
Вопрос по ООП rocky7 Общие вопросы C/C++ 5 21.08.2009 11:34
Вопрос по программированию в C++(ООП) Katya Melody Помощь студентам 2 24.04.2009 01:08