Я уже приводил ссылку на чужую статью:
http://www.rsdn.ru/article/patterns/patterns.xml
Там описаны формы, модули и операции.
Также я писал про "логику форм и прецеденты".
Вот тут:
http://18delphi.blogspot.ru/2013/08/mvc.html
http://18delphi.blogspot.ru/2013/08/mvc_8.html
У нас есть ещё одно понятие - "реализация прецедента". Это программная сущность, которая выполняет собой роль контроллера форм входящих в неё. И, как следует из названия - эта сущность нужна для реализации логики пользовательского прецедента, выделенного в требованиях.
Исторически мы их назвали "фабриками сборок форм". Это был "период исканий". И тогда мы ещё не понимали, что это ИМЕННО - "реализации прецедентов". Поэтому в коде много упоминаний FormSetFactory. В коде реализации прецедентов (FormSetFactory) описываются так:
Методы вида XXXNeedMakeForm служат для определения необходимости создания формы представления и передачи этому представлению его бизнес-объекта (контроллера).
Сразу оговорюсь, что использование Supports - это наследие "времён RAD", когда модель описывалась в дизайнере форм, а не на "чертежах".
Сейчас - есть чёткое понимание - как избежать данного Supports. Но поскольку этого пока не было сделано - привожу код как есть.
Запись вида:
AddZone(vcm_ztParent, fm_cfSaveLoad)
-- служит для связывания формы представления с конкретной зоной главного окна, в которое будет встраиваться реализация прецедента.
Регистрируются в конечном приложении они примерно так:
-- понятное дело, что правильнее было сделать внедрение зависимостей (http://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8). Но оно - пока сделано не было.
Создаётся же экземпляр реализации прецедента таким образом:
Tfs_SituationSearch.Make - это фабричный метод, который создаёт экземпляр "реализации прецедента" и встраивает его в главную форму приложения, заменяя им предыдущую активную "реализацию прецедента".
При этом сохранение состояния всех составляющих предыдущей реализации прецедента в историю Back/Forward осуществляется "автоматически".
TsdsSituation.Make - фабричный метод, создающий бизнес-объект логики прецедента. Который является контроллером для бизнес-объектов форм.
aQuery - это источник данных из предметной области для инициализации данного прецедента. В данном случае это IQuery - т.е. пользовательский запрос. Например загруженный из папок.
В конечном итоге метод OpenSituationCard зовётся примерно так:
А уж OpenQuery, зовётся из "клиентского кода" так:
-- из обработчика OnClick различных контролов.
Или так:
-- из обработчика "операции" SavedQuery.Execute на одной из форм представления другого прецедента.
Не скрою. Выглядит вся эта "конструкция" - тяжеловесно. Особенно на ПЕРВЫЙ ВЗГЛЯД.
Но во-первых - всё это рождалось годами. В процессе множества дискуссий и недопониманий.
Во-вторых - очень долго нам мешало то, что мы пытались всё это делать "через призму RAD" и дизайнеров форм и компонентов. Пока не пришли к пониманию того, что мета-модель и "чертежи" - годятся для этого гораздо лучше.
В-третьих - было допущено множество архитектурных ошибок. Опять же продиктованных "периодом исканий". Какие-то ошибки были исправлены, какие-то - до сих пор существуют. Но зато есть понимание - как их исправлять. И какой должна быть "идеальная архитектура.
Зато ТЕПЕРЬ, при наличии мета-модели, "чертежей" и кодогенерации - мы реально можем можем собирать реализации конкретных пользовательских прецедентов и их логики из УЖЕ существующих "кирпичиков" на модели и "номенклатуры микросхем".
Процесс создания НОВЫХ прецедентов выглядит реально как "отвёрточная сборка". Разумеется до тех пор, пока готовые "кирпичики" удовлетворяют текущим потребностям. Новые "кирпичики" делать сложнее, но тоже - не "космически" сложно. И для этого тоже существует поддержка со стороны мета-модели.
http://www.rsdn.ru/article/patterns/patterns.xml
Там описаны формы, модули и операции.
Также я писал про "логику форм и прецеденты".
Вот тут:
http://18delphi.blogspot.ru/2013/08/mvc.html
http://18delphi.blogspot.ru/2013/08/mvc_8.html
У нас есть ещё одно понятие - "реализация прецедента". Это программная сущность, которая выполняет собой роль контроллера форм входящих в неё. И, как следует из названия - эта сущность нужна для реализации логики пользовательского прецедента, выделенного в требованиях.
Исторически мы их назвали "фабриками сборок форм". Это был "период исканий". И тогда мы ещё не понимали, что это ИМЕННО - "реализации прецедентов". Поэтому в коде много упоминаний FormSetFactory. В коде реализации прецедентов (FormSetFactory) описываются так:
unit fsSituationSearch; {$Include nsDefine.inc} interface uses Classes, vcmInterfaces, vcmFormSetFactory, vcmUserControls, QueryCardInterfaces, l3StringIDEx, PrimSaveLoadUserTypes_slqtKW_UserType, PrimAttributeSelect_utSingleSearch_UserType, PrimTreeAttributeSelect_astNone_UserType, PrimTreeAttributeFirstLevel_flSituation_UserType, FiltersUserTypes_utFilters_UserType, Common_FormDefinitions_Controls, Search_FormDefinitions_Controls, PrimSelectedAttributes_utSelectedAttributes_UserType, SearchLite_FormDefinitions_Controls, vcmFormSetFormsCollectionItemPrim, SimpleListInterfaces {a}, SearchInterfaces {a}, l3TreeInterfaces, vcmFormSetFactoryPrim ; type Tfs_SituationSearch = {final fsf} class(TvcmFormSetFactory) {* ППС 6.х } protected // overridden protected methods procedure InitFields; override; class function GetInstance: TvcmFormSetFactoryPrim; override; public // public methods function SaveLoadParentSlqtKWNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для SaveLoad } function AttributeSelectParentUtSingleSearchNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для AttributeSelect } function TreeAttributeSelectParentAstNoneNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для TreeAttributeSelect } function SelectedAttributesChildUtSelectedAttributesNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для SelectedAttributes } function FiltersNavigatorUtFiltersNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для Filters } function TreeAttributeFirstLevelNavigatorFlSituationNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; {* Обработчик OnNeedMakeForm для TreeAttributeFirstLevel } public // singleton factory method class function Instance: Tfs_SituationSearch; {- возвращает экземпляр синглетона. } end;//Tfs_SituationSearch implementation uses l3Base {a}, l3MessageID, SysUtils {a} ; // start class Tfs_SituationSearch var g_Tfs_SituationSearch : Tfs_SituationSearch = nil; procedure Tfs_SituationSearchFree; begin FreeAndNil(g_Tfs_SituationSearch); end; class function Tfs_SituationSearch.Instance: Tfs_SituationSearch; begin if (g_Tfs_SituationSearch = nil) then begin l3System.AddExitProc(Tfs_SituationSearchFree); g_Tfs_SituationSearch := Create; end; Result := g_Tfs_SituationSearch; end; var { Локализуемые строки SituationSearchCaptionLocalConstants } str_fsSituationSearchCaption : Tl3StringIDEx = (rS : -1; rLocalized : false; rKey : 'fsSituationSearchCaption'; rValue : 'ППС 6.х'); { Заголовок фабрики сборки форм "SituationSearch" } // start class Tfs_SituationSearch function Tfs_SituationSearch.SaveLoadParentSlqtKWNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CC1002D6NeedMake_impl* aNew := l_UseCase.dsSaveLoad; //#UC END# *4D80CC1002D6NeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.SaveLoadParentSlqtKWNeedMakeForm function Tfs_SituationSearch.AttributeSelectParentUtSingleSearchNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CC820003NeedMake_impl* aNew := l_UseCase.dsAttributeSelect; //#UC END# *4D80CC820003NeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.AttributeSelectParentUtSingleSearchNeedMakeForm function Tfs_SituationSearch.TreeAttributeSelectParentAstNoneNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CCDD0117NeedMake_impl* aNew := l_UseCase.dsTreeAttributeSelect; //#UC END# *4D80CCDD0117NeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.TreeAttributeSelectParentAstNoneNeedMakeForm function Tfs_SituationSearch.SelectedAttributesChildUtSelectedAttributesNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CCF40025NeedMake_impl* aNew := l_UseCase.dsSelectedAttributes; //#UC END# *4D80CCF40025NeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.SelectedAttributesChildUtSelectedAttributesNeedMakeForm function Tfs_SituationSearch.FiltersNavigatorUtFiltersNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CC20008BNeedMake_impl* aNew := l_UseCase.dsFilters; //#UC END# *4D80CC20008BNeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.FiltersNavigatorUtFiltersNeedMakeForm function Tfs_SituationSearch.TreeAttributeFirstLevelNavigatorFlSituationNeedMakeForm(const aDataSource: IvcmFormSetDataSource; out aNew: IvcmFormDataSource; aSubUserType: TvcmUserType): Boolean; var l_UseCase : IsdsSituation; begin if Supports(aDataSource, IsdsSituation, l_UseCase) then try //#UC START# *4D80CC2E03BCNeedMake_impl* aNew := l_UseCase.dsTreeAttributeFirstLevel; //#UC END# *4D80CC2E03BCNeedMake_impl* finally l_UseCase := nil; end;//try..finally Result := (aNew <> nil); end;//Tfs_SituationSearch.TreeAttributeFirstLevelNavigatorFlSituationNeedMakeForm procedure Tfs_SituationSearch.InitFields; {-} begin inherited; with AddZone(vcm_ztParent, fm_cfSaveLoad) do begin UserType := slqtKW; with AddZone(vcm_ztParent, fm_cfAttributeSelect) do begin UserType := utSingleSearch; with AddZone(vcm_ztParent, fm_efTreeAttributeSelect) do begin UserType := astNone; OnNeedMakeForm := TreeAttributeSelectParentAstNoneNeedMakeForm; end; with AddZone(vcm_ztChild, fm_enSelectedAttributes) do begin UserType := utSelectedAttributes; OnNeedMakeForm := SelectedAttributesChildUtSelectedAttributesNeedMakeForm; end; OnNeedMakeForm := AttributeSelectParentUtSingleSearchNeedMakeForm; end; OnNeedMakeForm := SaveLoadParentSlqtKWNeedMakeForm; end; with AddZone(vcm_ztNavigator, fm_enFilters) do begin UserType := utFilters; OnNeedMakeForm := FiltersNavigatorUtFiltersNeedMakeForm; end; with AddZone(vcm_ztNavigator, fm_efTreeAttributeFirstLevel) do begin UserType := flSituation; ActivateIfUpdate := wafAlways; OnNeedMakeForm := TreeAttributeFirstLevelNavigatorFlSituationNeedMakeForm; end; Caption := str_fsSituationSearchCaption.AsCStr; end;//Tfs_SituationSearch.InitFields class function Tfs_SituationSearch.GetInstance: TvcmFormSetFactoryPrim; {-} begin Result := Self.Instance; end;//Tfs_SituationSearch.GetInstance initialization str_fsSituationSearchCaption.Init; end.
Методы вида XXXNeedMakeForm служат для определения необходимости создания формы представления и передачи этому представлению его бизнес-объекта (контроллера).
Сразу оговорюсь, что использование Supports - это наследие "времён RAD", когда модель описывалась в дизайнере форм, а не на "чертежах".
Сейчас - есть чёткое понимание - как избежать данного Supports. Но поскольку этого пока не было сделано - привожу код как есть.
Запись вида:
AddZone(vcm_ztParent, fm_cfSaveLoad)
-- служит для связывания формы представления с конкретной зоной главного окна, в которое будет встраиваться реализация прецедента.
Регистрируются в конечном приложении они примерно так:
procedure TAppRes.RegisterFormSetFactories; begin inherited; RegisterFormSetFactory(Tfs_CompareEditions); RegisterFormSetFactory(Tfs_InternetAgent); RegisterFormSetFactory(Tfs_Folders); RegisterFormSetFactory(Tfs_Autoreferat); RegisterFormSetFactory(Tfs_AutoreferatAfterSearch); RegisterFormSetFactory(Tfs_Document); RegisterFormSetFactory(Tfs_DocumentWithFlash); RegisterFormSetFactory(Tfs_List); RegisterFormSetFactory(Tfs_Diction); RegisterFormSetFactory(Tfs_Tips); RegisterFormSetFactory(Tfs_MedicDiction); RegisterFormSetFactory(Tfs_MedicFirmDocument); RegisterFormSetFactory(Tfs_DrugDocument); RegisterFormSetFactory(Tfs_DrugList); RegisterFormSetFactory(Tfs_MedicFirmList); RegisterFormSetFactory(Tfs_SendConsultation); RegisterFormSetFactory(Tfs_Consultation); RegisterFormSetFactory(Tfs_ViewChangedFragments); RegisterFormSetFactory(Tfs_SituationSearch); RegisterFormSetFactory(Tfs_SituationFilter); RegisterFormSetFactory(Tfs_AACContents); RegisterFormSetFactory(Tfs_AAC); end;
-- понятное дело, что правильнее было сделать внедрение зависимостей (http://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8). Но оно - пока сделано не было.
Создаётся же экземпляр реализации прецедента таким образом:
class procedure TSearchModule.OpenSituationCard(const aQuery: IQuery); var __WasEnter : Boolean; //#UC START# *4F27EA7D0011_4AA641A3036C_var* //#UC END# *4F27EA7D0011_4AA641A3036C_var* begin __WasEnter := vcmEnterFactory; try //#UC START# *4F27EA7D0011_4AA641A3036C_impl* Tfs_SituationSearch.Make(TsdsSituation.Make(TdeSearch.Make(nsCStr(AT_KW), aQuery))); //#UC END# *4F27EA7D0011_4AA641A3036C_impl* finally if __WasEnter then vcmLeaveFactory; end;//try..finally end;//TSearchModule.OpenSituationCard
Tfs_SituationSearch.Make - это фабричный метод, который создаёт экземпляр "реализации прецедента" и встраивает его в главную форму приложения, заменяя им предыдущую активную "реализацию прецедента".
При этом сохранение состояния всех составляющих предыдущей реализации прецедента в историю Back/Forward осуществляется "автоматически".
TsdsSituation.Make - фабричный метод, создающий бизнес-объект логики прецедента. Который является контроллером для бизнес-объектов форм.
aQuery - это источник данных из предметной области для инициализации данного прецедента. В данном случае это IQuery - т.е. пользовательский запрос. Например загруженный из папок.
В конечном итоге метод OpenSituationCard зовётся примерно так:
class procedure TAppRes.OpenQuery(aQueryType: TlgQueryType; const aQuery: IQuery); //#UC START# *4AC4A69D03B7_4A925AFF01BA_var* //#UC END# *4AC4A69D03B7_4A925AFF01BA_var* begin //#UC START# *4AC4A69D03B7_4A925AFF01BA_impl* case aQueryType of lg_qtKeyWord: TdmStdRes.OpenSituationCard(aQuery); lg_qtAttribute: TdmStdRes.AttributeSearch(aQuery, nil); lg_qtPublishedSource: TdmStdRes.PublishSourceSearch(aQuery, nil); lg_qtLegislationReview: TdmStdRes.OpenLegislationReview(aQuery); lg_qtSendConsultation: vcmDispatcher.ModuleOperation(TdmStdRes.mod_opcode_Search_OpenConsult); lg_qtBaseSearch: TdmStdRes.OpenBaseSearch(ns_bsokGlobal, aQuery); lg_qtInpharmSearch: TdmStdRes.InpharmSearch(aQuery, nil); else inherited; end;//case aQueryType //#UC END# *4AC4A69D03B7_4A925AFF01BA_impl* end;//TPrimNemesisRes.OpenQuery
А уж OpenQuery, зовётся из "клиентского кода" так:
procedure TPrimMainMenuNewForm.SearchClick(aSender: TObject); //#UC START# *4ACB8B7B0192_4958E1F700C0_var* //#UC END# *4ACB8B7B0192_4958E1F700C0_var* begin //#UC START# *4ACB8B7B0192_4958E1F700C0_impl* if (aSender = flAttributeSearch) then TdmStdRes.OpenQuery(lg_qtAttribute, nil) else if (aSender = flSitiationSearch) then TdmStdRes.OpenQuery(lg_qtKeyWord, nil) else if (aSender = flPublishedSourceSearch) then TdmStdRes.OpenQuery(lg_qtPublishedSource, nil) else if (aSender = flDictionSearch) then TdmStdRes.OpenDictionary(nil, NativeMainForm) else Assert(false); //#UC END# *4ACB8B7B0192_4958E1F700C0_impl* end;//TPrimMainMenuNewForm.SearchClick
-- из обработчика OnClick различных контролов.
Или так:
procedure TPrimWorkJournalForm.SavedQuery_Execute_Execute(const aParams: IvcmExecuteParamsPrim); //#UC START# *4C3F342E02AF_4BD6D6EA0075exec_var* var l_AdapterNode : INodeBase; l_BaseEntity : IUnknown; //#UC END# *4C3F342E02AF_4BD6D6EA0075exec_var* begin //#UC START# *4C3F342E02AF_4BD6D6EA0075exec_impl* if Supports(JournalTree.TreeView.CurrentNode, INodeBase, l_AdapterNode) then try try l_AdapterNode.GetEntity(l_BaseEntity); except on ECanNotFindData do Exit; //TODO: нода "пропала" что делать? end; try TdmStdRes.OpenQuery(l_BaseEntity As IQuery); finally l_BaseEntity := nil; end;//try..finally finally l_AdapterNode := nil; end;//try..finally //#UC END# *4C3F342E02AF_4BD6D6EA0075exec_impl* end;//TPrimWorkJournalForm.SavedQuery_ExecuteQuery_Execute
-- из обработчика "операции" SavedQuery.Execute на одной из форм представления другого прецедента.
Не скрою. Выглядит вся эта "конструкция" - тяжеловесно. Особенно на ПЕРВЫЙ ВЗГЛЯД.
Но во-первых - всё это рождалось годами. В процессе множества дискуссий и недопониманий.
Во-вторых - очень долго нам мешало то, что мы пытались всё это делать "через призму RAD" и дизайнеров форм и компонентов. Пока не пришли к пониманию того, что мета-модель и "чертежи" - годятся для этого гораздо лучше.
В-третьих - было допущено множество архитектурных ошибок. Опять же продиктованных "периодом исканий". Какие-то ошибки были исправлены, какие-то - до сих пор существуют. Но зато есть понимание - как их исправлять. И какой должна быть "идеальная архитектура.
Зато ТЕПЕРЬ, при наличии мета-модели, "чертежей" и кодогенерации - мы реально можем можем собирать реализации конкретных пользовательских прецедентов и их логики из УЖЕ существующих "кирпичиков" на модели и "номенклатуры микросхем".
Процесс создания НОВЫХ прецедентов выглядит реально как "отвёрточная сборка". Разумеется до тех пор, пока готовые "кирпичики" удовлетворяют текущим потребностям. Новые "кирпичики" делать сложнее, но тоже - не "космически" сложно. И для этого тоже существует поддержка со стороны мета-модели.
Комментариев нет:
Отправить комментарий