Порой возникает необходимость из низкоуровневой библиотеки передавать данные в высокоуровневую. У меня такая нужда возникла, когда надо было зарегистрировать операции (наша реализация дельфовских TAction-ов в скриптовой машине).
Изначально это было сделано напрямую:
Однако такой подход является неправильным с точки зрения архитектуры проекта: операциям совсем необязательно знать о скриптовой машине.
Даже наоборот - не нужно знать.
Чтобы обойти эту проблему можно использовать следующий подход: в библиотеке операций создаём утилитный класс-менеджер, который "выставляет" наружу интерфейс для передачи информации выше:
Также на нём реализован транзитный метод регистрации:
Этот класс создаём синглтоном (http://ru.wikipedia.org/wiki/%CE%E4%E8%ED%EE%F7%EA%E0_(%F8%E0%E1%EB%EE%ED_%EF%F0%EE%E5%EA%F2%E8%F0%EE%E2%E0%ED%E8%FF)#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.80_.D0.BD.D0.B0_Delphi)
Также создаём класс-регистратор в скриптовой библиотеке, реализующий описанный выше интерфейс, который будет подписан к менеджеру как получатель информации о регистрируемый операциях:
Осталось организовать подписку.
Это можно сделать во многих местах, но мне было удобнее это сделать непосредственно в секции инициализации модуля kwOperationsRegistrar:
Теперь оба синглтона созданы и путь для регистрации операций проложен.
Операции регистрируются через "третьи руки", целостность проекта не нарушена, зато понижена его связность:
В модуле vcmBaseMenuManager вместо строки
надо вписать
Естественно, я не открыл Америку, и этот подход давно известен:
http://ru.wikipedia.org/wiki/%C2%ED%E5%E4%F0%E5%ED%E8%E5_%E7%E0%E2%E8%F1%E8%EC%EE%F1%F2%E8
Вывод один - надо больше читать полезной литературы и тогда не придётся ломать голову, как избавиться от лишнего uses-а.
----
Другой коллега вот дал такую ссылку - http://martinfowler.com/articles/injection.html
Изначально это было сделано напрямую:
unit vcmBaseMenuManager;
uses
kwEntityOperation;
procedure TvcmBaseMenuManager.RegisterKeywords;
var
l_I, l_J : Integer;
l_En : TvcmBaseEntitiesCollectionItem;
begin
if not f_KeywordsRegistered then
begin
f_KeywordsRegistered := True;
for l_I := 0 to Pred(Entities.Count) do
begin
l_En := Entities.Items[l_I] as TvcmBaseEntitiesCollectionItem;
for l_J := 0 to Pred(l_En.Operations.Count) do
TkwEntityOperation.Register(l_En, l_En.Operations.Items[l_J] as TvcmBaseOperationsCollectionItem);
end;//for l_I
end;//not f_KeywordsRegistered
end;//TvcmBaseMenuManagerPrim.RegisterKeywords
Однако такой подход является неправильным с точки зрения архитектуры проекта: операциям совсем необязательно знать о скриптовой машине.
Даже наоборот - не нужно знать.
Чтобы обойти эту проблему можно использовать следующий подход: в библиотеке операций создаём утилитный класс-менеджер, который "выставляет" наружу интерфейс для передачи информации выше:
unit vcmOperationsManager;
type
IvcmOperationsRegistrar = interface(IUnknown)
['{3E98A7F7-A01F-4D5A-8AB2-2C731A6E8CB5}']
procedure Register(anEn: TvcmBaseEntitiesCollectionItem;
anOp: TvcmBaseOperationsCollectionItem);
end;//IvcmOperationsRegistrar
TvcmOperationsManager = class
private
f_Registrar : IvcmOperationsRegistrar;
public
procedure Register(anEn: TvcmBaseEntitiesCollectionItem;
anOp: TvcmBaseOperationsCollectionItem);
public
property Registrar: IvcmOperationsRegistrar
read f_Registrar
write f_Registrar;
public
class function Instance: TvcmOperationsManager;
end;//TvcmOperationsManager
Также на нём реализован транзитный метод регистрации:
procedure TvcmOperationsManager.Register(anEn: TvcmBaseEntitiesCollectionItem; anOp: TvcmBaseOperationsCollectionItem); begin if f_Registrar <> nil then // или так: Assert(f_Registrar <> nil); - если мы уверены, что подписка происходит всегда f_Registrar.Register(anEn, anOp); end;//TvcmOperationsManager.Register
Этот класс создаём синглтоном (http://ru.wikipedia.org/wiki/%CE%E4%E8%ED%EE%F7%EA%E0_(%F8%E0%E1%EB%EE%ED_%EF%F0%EE%E5%EA%F2%E8%F0%EE%E2%E0%ED%E8%FF)#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.80_.D0.BD.D0.B0_Delphi)
Также создаём класс-регистратор в скриптовой библиотеке, реализующий описанный выше интерфейс, который будет подписан к менеджеру как получатель информации о регистрируемый операциях:
unit kwOperationsRegistrar;
uses
kwEntityOperation,
vcmOperationsManager;
type
TkwOperationsRegistrar = class(TObject, IvcmOperationsRegistrar)
protected
procedure Register(anEn: TvcmBaseEntitiesCollectionItem;
anOp: TvcmBaseOperationsCollectionItem);
public
class function Instance: TkwOperationsRegistrar;
end;//TkwOperationsRegistrar
procedure TkwOperationsRegistrar.Register(anEn: TvcmBaseEntitiesCollectionItem;
anOp: TvcmBaseOperationsCollectionItem);
begin
TkwEntityOperation.Register(anEn, anOp);
end;//TkwOperationsRegistrar.Register
Осталось организовать подписку.
Это можно сделать во многих местах, но мне было удобнее это сделать непосредственно в секции инициализации модуля kwOperationsRegistrar:
initialization TvcmOperationsManager.Instance.Registrar := TkwOperationsRegistrar.Instance; end.
Теперь оба синглтона созданы и путь для регистрации операций проложен.
Операции регистрируются через "третьи руки", целостность проекта не нарушена, зато понижена его связность:
В модуле vcmBaseMenuManager вместо строки
TkwEntityOperation.Register(l_En, l_En.Operations.Items[l_J] as TvcmBaseOperationsCollectionItem);
надо вписать
TvcmOperationsManager.Instance.Register(l_En, l_En.Operations.Items[l_J] as TvcmBaseOperationsCollectionItem);
Естественно, я не открыл Америку, и этот подход давно известен:
http://ru.wikipedia.org/wiki/%C2%ED%E5%E4%F0%E5%ED%E8%E5_%E7%E0%E2%E8%F1%E8%EC%EE%F1%F2%E8
Вывод один - надо больше читать полезной литературы и тогда не придётся ломать голову, как избавиться от лишнего uses-а.
----
Другой коллега вот дал такую ссылку - http://martinfowler.com/articles/injection.html
Комментариев нет:
Отправить комментарий