четверг, 8 августа 2013 г.

Про "MVC". Будем реализовывать дальше прецедент "работа с документом"

Предыдущая серия была тут - http://18delphi.blogspot.com/2013/08/mvc.html

Теперь будем продолжать реализацию прецедента "работа с документом".

У нас остались нереализованные требования:
6. Печать и предварительный просмотр всего документа.
7. Печать и предварительны просмотр выделенных участков документа.
8. Переход по ссылкам. Ссылки бывают - внутренние (внутри данного прецедента) и внешние (между различными прецедентами).
9. Логирование функций работы с документом.
10. Экспорт всего документа в определённые форматы.
11. Экспорт выделенного участка текста в определённые форматы.

Начнём с "Печать и предварительный просмотр всего документа".

Пусть собственно ФУНКЦИОНАЛ печати и предварительного просмотра - УЖЕ "как-то" реализован. А именно ОТДЕЛЬНЫМ классом:

TDocumentPrintMachine = class
 procedure Preview;
 procedure Print;
end;//TDocumentPrintMachine

- это пока только "набросок".

Теперь посмотрим - как этот ПРОЕКТНЫЙ класс связан с прецедентом "работа с документом".

Напомню как выглядит прецедент "просмотр документа":

С точки зрения визуализации:

type
 TDocumentForm = class(TForm)
 
  TDocumentTextViewArea = class(TFrame)
   Text : TDocumentView;
  end;//TDocumentTextViewArea
 
  DocumentTextViewArea : TDocumentTextViewArea; 
 
  TListViewArea = class(TFrame)
   List : TListView;
  end;//TListViewArea 
 
  DocumentContents: DocumentContentsViewArea: TDocumentContentsViewArea = class(TFrame)
 
   DocumentStructureViewArea: TDocumentStructureViewArea = class(TFrame)
    Tree : TTreeView;
   end;//TDocumentStructureViewArea 
 
   DocumentPicturesViewArea: TListViewArea;
 
  end;//TDocumentContentsViewArea 
 
  DocumentIncomingLinksViewArea: TListViewArea;
 
  DocumentOutgoingLinksViewArea: TListViewArea;
 
  DocumentAnnotationViewArea: TDocumentTextViewArea;
 
end;//TDocumentForm

С точки зрения данных:

type
 TDocumentUseCaseData = class(TUseCaseData)
 
  TDocumentTextViewAreaData = class(TViewAreaData)
   property Text: TDocument;
  end;//TDocumentTextViewAreaData
 
  property DocumentText: TDocumentTextViewAreaData;
 
  TListViewAreaData = class(TViewAreaData)
   property List : TList;
  end;//TListViewAreaData
 
  property DocumentContents: TDocumentContentsViewAreaData = class(TViewAreaData)
 
   property DocumentStructure: TDocumentStructureViewAreaData = class(TViewAreaData)
    property DocumentStructure : TTree;
   end;//TDocumentStructureViewAreaData
 
   property DocumentPictures: TListViewAreaData;
 
  end;//TDocumentContentsViewAreaData
 
  property DocumentIncomingLinks: TListViewAreaData;
 
  property DocumentOutgoingLinks: TListViewAreaData;
 
  property DocumentAnnotation: TDocumentTextViewAreaData;
 
 end;//TDocumentUseCaseData

Куда "всунуть" TDocumentPrintMachine?

Стоп!
Тут я забыл про один вопрос - "как данные связываются с визуализацией?"
Сейчас мы рассмотрим этот вопрос.

Пока почитайте вот это - http://www.delphinotes.ru/2013/01/delphi.html#more

Там - ОЧЕНЬ правильные вещи про "базовую форму" и "базовый фрейм" написаны.
СВОЯ "базовая форма" и "базовый фрейм". Николай - там - ОЧЕНЬ верно это описал.

Будем считать, что TForm => TvcmForm, TFrame => TvcmViewArea.

А теперь посмотрим, как преображается наша модель:
type
 TDocumentForm = class(TvcmForm)
 
  f_DocumentData : TDocumentUseCaseData;
 
  TDocumentTextViewArea = class(TvcmViewArea)
   f_TextData : TDocumentTextViewAreaData;
   Text : TDocumentView;
  end;//TDocumentTextViewArea
 
  DocumentTextViewArea : TDocumentTextViewArea; 
 
  TListViewArea = class(TvcmViewArea)
   f_List : TListViewAreaData;
   List : TListView;
  end;//TListViewArea 
 
  DocumentContents: DocumentContentsViewArea: TDocumentContentsViewArea = class(TvcmViewArea)
 
   f_Contents : TDocumentContentsViewAreaData;
 
   DocumentStructureViewArea: TDocumentStructureViewArea = class(TvcmViewArea)
    f_Structure : TDocumentStructureViewAreaData;
    Tree : TTreeView;
   end;//TDocumentStructureViewArea 
 
   DocumentPicturesViewArea: TListViewArea;
 
  end;//TDocumentContentsViewArea 
 
  DocumentIncomingLinksViewArea: TListViewArea;
 
  DocumentOutgoingLinksViewArea: TListViewArea;
 
  DocumentAnnotationViewArea: TDocumentTextViewArea;
 
end;//TDocumentForm

- тут мы видим "данные" и "представление" ОДНОВРЕМЕННО.

(Ещё раз напоминаю! "Геометрию" и Owner/Parent - мы рассмотрим - позже. Потому, что ТУТ - не ВСЁ ТАК ПРОСТО. Например есть TPanel а есть TTabView. ;-) "Активные зоны" могут себя ПО-РАЗНОМУ вести :-))

А где они ("данные" и "представление") в итоге "встречаются"? Сейчас мы это - рассмотрим:

Модель (и фреймворк) дают нам следующее:
type
 TDocumentForm = class(TvcmForm)
 
  f_DocumentData : TDocumentUseCaseData;
 
  TDocumentTextViewArea = class(TvcmViewArea)
   f_TextData : TDocumentTextViewAreaData;
   Text : TDocumentView;
   procedure GotData(aData : TDocumentTextViewAreaData);
   begin
    f_TextData := aData;
    Text.Text := aData.Text;
   end;
  end;//TDocumentTextViewArea
 
  DocumentTextViewArea : TDocumentTextViewArea; 
 
  TListViewArea = class(TvcmViewArea)
   f_List : TListViewAreaData;
   List : TListView;
   procedure GotData(aData : TListViewAreaData);
   begin
    f_List := aData;
    List.List := aData.List;
   end;
  end;//TListViewArea 
 
  DocumentContents: DocumentContentsViewArea: TDocumentContentsViewArea = class(TvcmViewArea)
 
   f_Contents : TDocumentContentsViewAreaData;
 
   DocumentStructureViewArea: TDocumentStructureViewArea = class(TvcmViewArea)
    f_Structure : TDocumentStructureViewAreaData;
    Tree : TTreeView;
    procedure GotData(aData : TDocumentStructureViewAreaData);
    begin
     f_Structure := aData;
     Tree.Tree := aData.Tree;
    end; 
   end;//TDocumentStructureViewArea 
 
   DocumentPicturesViewArea: TListViewArea;
 
   procedure GotData(aData : TDocumentContentsViewAreaData);
   begin
    f_Contents := aData;
    DocumentStructureViewArea.GotData(aData.DocumentStructure);
    DocumentPicturesViewArea.GotData(aData.DocumentPictures);
   end;
 
  end;//TDocumentContentsViewArea 
 
  DocumentIncomingLinksViewArea: TListViewArea;
 
  DocumentOutgoingLinksViewArea: TListViewArea;
 
  DocumentAnnotationViewArea: TDocumentTextViewArea;
 
  procedure GotData(aData : TDocumentUseCaseData);
  begin
   f_DocumentData := aData;
   DocumentTextViewArea.GotData(aData.DocumentText);
   DocumentContentsViewArea.GotData(aData.DocumentContents);
   DocumentIncomingLinksViewArea.GotData(aData.DocumentIncomingLinks);
   DocumentOutgoingLinksViewArea.GotData(aData.DocumentOutgoingLinks);
   DocumentAnnotationViewArea.GotData(aData.DocumentAnnotation);
  end;
 
end;//TDocumentForm


Идея понятна? По-моему - она - ОЧЕВИДНА :-)
И ЭТО ВСЁ - генерируется с модели. Спросите меня - "где модель" и "как оно генерируется"? Я расскажу - чуть позже...

Теперь вернёмся к требованиям и ответственностям.
......

Для начала посмотрим вот сюда:
  TDocumentTextViewArea = class(TvcmViewArea)
   Text : TDocumentView;
  end;//TDocumentTextViewArea
 
  DocumentTextViewArea : TDocumentTextViewArea; 
...
  DocumentAnnotationViewArea: TDocumentTextViewArea;
...

Предположим, что ответственность "Печать и предварительный просмотр всего документа" реализует объект TDocumentTextViewArea. Ну то есть - печатать и просматривать документ позволяют ДВЕ "активных зоны" - DocumentTextViewArea и DocumentAnnotationViewArea.

Для начала - определим пользовательские "точки входа", а именно - операции "Печать" и "Предварительный просмотр".

Предположим, что операции определяет мета-директива operation.

Это аналог procedure, но с ФИКСИРОВАНОЙ сигнатурой и возможностью ПУБЛИКАЦИИ в toolbar'е и всевозможных меню. А также - operation может УПРАВЛЯТЬ видимостью и доступностью этих "пунктов меню". Но об этом - позже.

(<<operation>> это кстати один из СТЕРЕОТИПОВ нашей мета-модели UML, но про UML - тоже позже)

"Ноги" operation - растут из TAction на самом деле :-) (это для любителей Delphi).

продолжим наверное в следующей статье...

.................

to be continued....

4 комментария:

  1. Кстати, мы на практике очень часто такое используем - перед отображением формы, например, с табличными данными, заполняется объект, который определяет поведение формы. Например - возможность редактирование записей, либо выбор одной записи, либо выбор нескольких записей (результат выбора возвращается в объекте). Либо много чего ещё может быть (например, по какой таблице будет форма строится).
    И этот объект передаётся с конструктором формы (или фреймы).

    Я про это тоже хотел написать, но лето, дети, разленился ((

    ОтветитьУдалить
    Ответы
    1. "Кстати, мы на практике очень часто такое используем - перед отображением формы, например, с табличными данными, заполняется объект, который определяет поведение формы"

      ;-) ВЕРЮ!!!

      Я ведь САМ - IUnknown ПРИДУМАЛ :-) Я ведь САМ - НИЧЕГО не придумал :-)

      "Я про это тоже хотел написать, но лето, дети, разленился" - я САМ - многое ЧЕГО НЕ НАПИСАЛ :)

      Но я ВСЕХ - приглашаю в СОАВТОРЫ, :-) если тема "про MVC" - "прёт" :-)

      Самоё клёвое было бы - "я рассказываю на пальцах.. Вы это пишите :-) Все лавры - вам..." :-) А мне - удовлетворение :-)

      Удалить
    2. Поставьте кстати хотя бы "интересно"... А то все читают - и пишут "вау - круто"... А другие говорят - "у тебя, что ни одного читателя?" :-)

      И все - комплексуют быть первыми :-)

      Удалить