Порой возникает необходимость из низкоуровневой библиотеки передавать данные в высокоуровневую. У меня такая нужда возникла, когда надо было зарегистрировать операции (наша реализация дельфовских 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
Комментариев нет:
Отправить комментарий