Интерфейсы и плагины
Интерфейсы и плагины ВведениеПожалуй, каждому программисту со временем приходит в голову мысль, что его замечательной программе не хватает гибкости. И действительно, допустим, есть большая программа, и в нее понадобилось внести небольшие изменения. Не важно, по какой причине. Но для этого ее надо откомпилировать, проверить, а потом еще и поставить на рабочие места. Нет нужды говорить, что это достаточно долгое занятие. В другом случае может возникнуть желание, чтобы другие программисты могли расширять функциональность программы, а когда программа - единое целое, то им надо полностью поставить исходный код, еще и с описанием, где что находится, какие библиотеки нужны и так далее.И программист приходит к выводу, что очень желательно, чтобы его программа состояла из набора отдельных модулей (плагинов), каждый из которых был бы независим от других. Тогда при необходимости можно изменить и отладить только один модуль, а сторонним программистам дать шаблон интерфейса. Программ, построенных таким образом, множество, и остается только завидовать. Чаще всего модульность реализуется на основе механизмов dll, динамических библиотек. Вот только со стандартной dll возникают трудности: экспортируются только процедуры, да и ansistring не так просто передать. А хочется ведь передавать в модули и объекты. Конечно, это возможно, но... С этого момента плагины можно писать только на Delphi, и, мало того, именно на той версии, с которой начиналась разработка, никто ведь не гарантирует, что внутреннее представление объекта не поменяется с изменением версии среды разработки. И, как ни странно, мало кто обращает внимание на механизм, который есть в любой современной версии Windows, и который предназначен прежде всего именно для взаимодействия модулей и программ. Я имею в виду COM. И если учесть, что в Delphi работа с COM является достаточно простым делом, очень странным кажется тот факт, что в большинстве случаев применение этой технологии ограничивается, например, выдачей в Excel отчетов или работой с TWebBrowser.Но эта технология позволяет легко создавать модульные приложения, и с достаточно низкими затратами. Все, что нужно - реализовать интерфейсы к объектам, и обмениваться этими интерфейсами. И совершенно не важно, на какой версии Delphi написан плагин, фактически, он может быть написан вообще на другом языке программирования: COM не зависит от языка. При этом СОМ - объектная технология, тесно интегрированная в Delphi.Я хочу показать, как можно с помощью COM решить задачу создания плагинов, маленьких частей программы, которые могут быть изменены независимо от основной программы. В частности, здесь я рассматриваю, как создать несколько библиотек, по-разному реализующих одну и ту же функциональность. Итак, что же такое интерфейсы и с чем их едят? Говоря простым языком, интерфейс COM - это всего лишь структура, которая содержит объявления методов. И не более того. Ближайший аналог в Delphi - объявление класса.Для создания плагинов нужно понимать, что у интерфейса нет экземпляра, это просто декларация, которая может быть понята на любом языке программирования, в котором есть возможность работы с COM. Собственно код и данные должны содержаться в объекте, который реализует этот интерфейс, для него даже есть специальное название, кокласс (coclass). И может быть сколько угодно коклассов, реализующих один и тот же интерфейс. Единый интерфейс с различным наполнением - как раз то, что нужно.То есть, нужно просто создать ту реализацию интерфейса, которая нужна, а дальше просто общаться с ней через интерфейс, который везде одинаков.Давайте рассмотрим конкретный пример. Для этого я взял три метода сортировки массива, которые нагло утянул из проекта в demos\threads. Это BubbleSort, SelectionSort и QuickSort. Потоки и визуализацию рассматривать не будем, по крайней мере, сейчас. Задача стоит в том, чтобы создать три плагина для сортировки массива, реализующих каждый свой метод, и подключать один из них по мере надобности. Разумеется, каждая реализация должна находиться в своей библиотеке (dll).Объекты СОМСначала рассмотрим простейший способ.Как можно догадаться, прежде всего нам понадобится интерфейс. Затем - библиотека, содержащая его реализацию. Я предлагаю начать с библиотеки, так удобнее. При этом я не буду выдавать подробные инструкции вроде "мееедленно подведите указатель мышки к пункту меню File...", я просто покажу, что нужно: В технологиии COM используются библиотеки специального вида, и при выборе ActiveX Library как раз создается шаблон такой библиотеки. Далее, сразу создаем COM Object с этой же вкладки, при этом среда спрашивает, что именно нужно. Вот это: Можно догадаться, что первым будет реализован метод пузырька. Обратите внимание, что Type Library не нужна. В библиотеку добавляется модуль, в котором уже объявлен класс, потомок TComObject. Не хватает только интерфейса. И, поскольку библиотеки типов нет, нужно объявить его вручную, и, желательно, в отдельном модуле для дальнейшего использования. Но предварительно надо сохранить то, что получилось. Я дал название библиотеке BubbleLib, а модулю кокласса - CBubble. Теперь добавим модуль с интерфейсом:
interface
type
ISortIntf = interface
['{BE229ED0-BA7B-11DA-B665-00508B0973BE}']
procedure Sort(var A: Variant); stdcall;
end;
implementation
end.
TBubbleSort = class(TComObject, ISortIntf)
protected
procedure Sort(var A: Variant); stdcall;
end;
{ TBubbleSort }
procedure TBubbleSort.Sort(var A: Variant);
var
I, J, T: Integer;
HighBound, LowBound: integer;
begin
HighBound := VarArrayHighBound(A, 1);
LowBound := VarArrayLowBound(A, 1);
for I := HighBound downto LowBound do
for J := LowBound to HighBound - 1 do
if A[J] > A[J + 1] then
begin
T := A[J];
A[J] := A[J + 1];
A[J + 1] := T;
end;
end;
Class_BubbleSort: TGUID = '{CFB8F671-BA45-11DA-B665-00508B0973BE}';
Class_SelectionSort: TGUID = '{BE229ED3-BA7B-11DA-B665-00508B0973BE}';
Class_QuickSort: TGUID = '{BE229ED4-BA7B-11DA-B665-00508B0973BE}';
implementation
uses COMObj, SortIntf;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Bubble: ISortIntf;
i: integer;
A: Variant;
HighBound, LowBound: integer;
Class_ID: TGUID;
ElemCount: integer;
begin
//Получаем нужный CLSID
Class_ID := StringToGUID(ListBox1.Items[ListBox1.ItemIndex]);
//Создаем класс по этому CLSID и сразу просим у него интерфейс ISortIntf
Bubble := CreateCOMObject(Class_ID) as ISortIntf;
ElemCount := SpinEdit1.Value;
//Массив создаем и записываем в мемо
A := VarArrayCreate([0,ElemCount-1], varInteger);
Memo1.Lines.Clear;
HighBound := VarArrayHighBound(A, 1);
LowBound := VarArrayLowBound(A, 1);
for i := LowBound to HighBound do
begin
A[i] := random(300);
Memo1.Lines.Add(IntToStr(A[i]));
end;
//А вот, собственно, и вызов метода плагина
Bubble.Sort(A);
//Остается показать результат
Memo1.Lines.Add('Sorted');
HighBound := VarArrayHighBound(A, 1);
LowBound := VarArrayLowBound(A, 1);
for i := LowBound to HighBound do
begin
Memo1.Lines.Add(IntToStr(A[i]));
end;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
with ListBox1.Items do
begin
Clear;
Add(GUIDToString(Class_BubbleSort));
Add(GUIDToString(Class_SelectionSort));
Add(GUIDToString(Class_QuickSort));
end;
ListBox1.ItemIndex := 0;
end;
Отправить комментарий