четверг, 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....

Вопрос про UseCase'ы

Задали тут ХОРОШИЙ вопрос, цитирую:

"смотрите

есть "актор" (могу попутать)

мы пишем его с себя, выставляя use case-ы

и все эти use case-ы растут из моего состояния (как юзера)

в момент, когда я системой ПОКА НЕ ОБЛАДАЮ

потом появилась система

и я - уже не "человек умелый", а "человек разумный"

т.е. я как "я с системой" отличаюсь от "меня без системы"

и мое видение системы тоже изменяется

и как тогда насчет ЭВОЛЮЦИИ прецедентов?

как пишут (блин) "классики"

система устаревает в момент её создания

как только она завершена, она тут же устарела

т.к. "пользователь с системой" - уже отличается от "пользователя без системы""

-- я в будущем постараюсь на него ответить. Но может быть кто-нибудь из читателей сам постарается ответить?

"Шаблон" работы с ТЗ

 "проблема пользователя" -> "свойство системы" -> "ответственность программной сущности"

Иногда "проблема" - опускается...

приходит Пользователь и говорит - "хочу красную ленточку" - это СРАЗУ - "свойство системы"
а проблема - "ну так хочет Пользователь"

Читаю всякие разные комментарии и думаю, что многие путают СЛОЖНОСТЬ системы с другими характеристиками

Читаю всякие разные комментарии и думаю, что многие путают СЛОЖНОСТЬ системы с другими характеристиками. Например с ОТКАЗОУСТОЙЧИВОСТЬЮ ил МАСШТАБИРУЕМОСТЬЮ. Или ВРЕМЕНЕМ ОТКЛИКА.

Вот скажем Wikipedia - сложная система?

P.S. если из инструментов есть лишь "молоток", то все проблемы кажутся "гвоздями" :-)

вторник, 6 августа 2013 г.

Ссылка. В поисках самого востребованного языка программирования. Не мог пройти мимо. Сам - прочитаю позже

http://blogerator.ru/page/samij-vostrebovannij-populjarnij-jazyka-programmirovanija-rejting-tiobe-pypl-redmonk-statistika

Не мог пройти мимо. Сам - прочитаю позже. Может быть - выскажу своё мнение. Или воздержусь.

"Синтез" логики форм приложения на тему "MVC". Или "анализ прецедентов сверху вниз". А также - "рефакторинг при проектировании". Пока БЕЗ UML

Предварительная вводная: Говоря о классах "реализации" - будем придерживаться нотации Delphi и будем иметь в виду архитектуру библиотеки VCL. Это необязательно.  Но это - для определённости.

Пусть есть несколько "похожих" прецедентов (http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D1%86%D0%B5%D0%B4%D0%B5%D0%BD%D1%82_(UML)):

1. Работа с документом
2. Работа со словарной статьёй
3. Работа со статьёй энциклопедии
4. Обзор изменений документа
5. Сравнение соседних редакций документа

Прецеденты взяты из реальной системы. Надеюсь - этому никто не расстроится. Просто качественно можно говорить об известном предмете, а не о "коне вакууме".

Надеюсь, что с одной стороны - никто не узрит в этом "выдачу технологических секретов", а с другой - "скрытую рекламу". Если бы не эти два обстоятельства - писать было бы сильно проще.

Этап сбора и описания требований - опускаю. Этой теме можно посвятить отдельную статью (если интересно). Пока предположим, что требования собраны и описаны. И частично - детализированы.

Прецеденты - РАЗНЫЕ, но по функциональности - ПЕРЕСЕКАЮТСЯ.

Что у них общего? Это - "работа с документом" в том или ином виде.

Понятное дело, что в реализации этих прецедентов - есть много общего, но и есть различия.

Понятное дело, все эти прецеденты визуализуются РАЗЛИЧНЫМИ формами отображения.

Понятно, что эти формы - родственны, но при этом - они и различаются.

Что делать? Как будем проектировать функционал этих прецедентов?

Наследование? Визуальное наследование? Агрегация? Выделение бизнес-логики в отдельный слой/слои?

Хочется немножко рассказать на эту тему.

Для начала надо наверное выделить ОБЩИЕ ответственности для данных прецедентов:
1. Отображение документа.
2. Скроллирование документа.
3. Навигация курсором в рамках текста документа.
4. Выделение участков текста документа.
5. Копирование выделенных участков в буфер обмена.
6. Печать и предварительный просмотр всего документа.
7. Печать и предварительны просмотр выделенных участков документа.
8. Переход по ссылкам. Ссылки бывают - внутренние (внутри данного прецедента) и внешние (между различными прецедентами).
9. Логирование функций работы с документом.
10. Экспорт всего документа в определённые форматы.
11. Экспорт выделенного участка текста в определённые форматы.

пока - ограничимся...

Теперь попытаемся выделить функциональные различия данных прецедентов.

Для начала поймём - как эти прецеденты - визуализируются.

На самом деле каждый прецедент (отображаемый для определённости "формой" - наследником от TForm) распадается на несколько "зон активностей" (или "зон пользовательского фокуса"). На самом деле эти "зоны" (забегая вперёд) это отдельные вложенные прецеденты. На эту тему я немножко "закинул удочку" тут - http://18delphi.blogspot.com/2013/08/usecase.html.

Каждая из зон характеризуется тем, что у них у каждой может быть СОБСТВЕННЫЙ набор Toolbar'ов, каждая может принимать ОТДЕЛЬНО фокус ввода, каждая имеет СОБСТВЕННОЕ контекстное меню, каждая может ВЛИЯТЬ на главное (командное) меню приложения.

Каждая из зон может обладать СОБСТВЕННЫМ состоянием, которое может записываться в историю работы приложения.

Предположим, что распадаются они так (в скобках буду указывать имена "реализующих классов"):

1. Работа с документом (TDocumentForm):
а. Собственно "текст документа" (TDocumentTextViewArea).
б. Оглавление документа (TDocumentContentsViewArea):
б.1 Список структурных единиц документа (TDocumentStructureViewArea).
б.2 Список иллюстраций в документе (TDocumentPicturesViewArea).
в. Список входящих ссылок на документ (TDocumentIncomingLinksViewArea).
г. Список исходящих ссылок из документа (TDocumentOutgoingLinksViewArea).
д. Аннотация к документу (TDocumentAnnotationViewArea).

2. Работа со словарной статьёй (TDictionEntryForm):
а. Русский текст статьи (TRussianTextViewArea).
б. Английский текст статьи (TEnglishTextViewArea).
в. Список словарных статей (TDictionEntriesViewArea).

3. Работа со статьёй энциклопедии (TEncyclopediaForm):
а. Собственно "текст статьи" (TArcticleViewArea).
б. Оглавление энциклопедии (TEncyclopediaContentsViewArea).

4. Обзор изменений документа (TDocumentReviewForm):
а. Собственно "текст обзора документа" (TDocumentReviewTextViewArea).

5. Сравнение соседних редакций документа (TEditionsCompareForm):
а. Текст предыдущей редакции документа (TPrevEditionTextViewArea).
б. Текст "актуальной" редакции документа (TActualEditionTextViewArea).

Что это за классы TXXXViewArea и каково их место в иерархии того же VCL - мы поговорим - чуть позже.

Если вы хотите аналогию из "классического" Delphi и VCL, то скажем так, что TXXXViewArea - "похожи" на TFrame. Именно - ПОХОЖИ. Но НЕ ЯВЛЯЮТСЯ таковыми. Но ПОКА - так думать будет понятно (надеюсь).

Далее надо отметить, что каждый прецедент имеет "точку входа". Это нечто, что принимает "данные для конструирования прецедента" и возвращает собственно ЭКЗЕМПЛЯР прецедента.

В терминах "классики" это - конструктор класса (формы). Но если забегать вперёд и думать об "инъекции зависимостей" (http://ru.wikipedia.org/wiki/Dependency_Injection), то это - фабрика. Фабрика прецедента.

Но! ПОКА забудем о фабриках. До поры до времени.

ПОКА будем считать, что "точка входа" в прецедент это конструктор объекта в терминах "классики".

Также ПОКА забудем о сигнатуре конструктора TForm (и TComponent) и что на самом деле он выглядит так - Create(anOwner : TComponent).

Также ПОКА забудем о "геометрии" и размещении форм (визуализующих прецеденты) в пространстве, а также о свойстве Parent.

Об этом мы поговорим чуть позже. Обещаю.

ПОКА будем считать, что "точка входа" выглядит примерно так CreateUseCase(aData : TUseCaseData).

Где TUseCaseData - некоторый базовый (в пределе - шаблонный) класс, обеспечивающий связь данных прецедента и собственно логики и визуализации прецедента.

Забегая вперёд - отметим, что это скорее всего не класс, а - интерфейс. Но на данном этапе - нам ПОКА это не важно.

Почему ИНТЕРФЕЙС? Потому, что - ПОДСЧЁТ ССЫЛОК. Пока - это - не важно. Но позже - я остановлюсь и на этом моменте.

(Для тех немногих, кто знаком с фреймворком VCM скажу, что TUseCaseData - это собственно и есть IsdsXXX).

Пока дело обстоит так. Есть "визизуализация прецедента" - TXXXForm и "данные прецедента" - TXXXUseCaseData.

TXXXForm обладает конструктором CreateUseCase, с сигнатурой - CreateUseCase(aData : TXXXUseCaseData).

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

Тут ПОКА - всё очень просто. Отображение тут - 1:1. Т.е. "визуализация прецедента" TXXXForm требует "данных прецедента" TXXXUseCaseData.

Хочется отметить тот факт, что в описываемой "модели" - "данные прецедента" - являются КОНСТАНТНЫМИ. Т.е. они не меняют своё состояние с момента своего рождения до момента своей смерти. Это означает, что собственно прецедент (и его визуализация) могут рассматривать эти данные как инвариант на всём времени своей жизни. Если "данные прецедента" надо поменять (например переходим из одного документа в другой), то это означает, что надо СМЕНИТЬ текущий прецедент. Создав НОВЫЙ экземпляр, подав в "точку входа" (конструктор/фабрику) новые "данные прецедента". Этот ВАЖНЫЙ факт - мы тоже - обсудим. Чуть позже.

Тут бы уже можно было бы нарисовать модель прецедентов и их данных в терминах UML, но к сожалению - инструмент у меня недоступен - посему "буду писать текстом". Пока. А модель - нарисую чуть позже.

Итак - посмотрим на описанные выше прецеденты и их данные.
Пусть запись вида - TXXXForm <=| TXXXUseCaseData - означает "данные TXXXUseCaseData - могут порождать экземпляр прецедента TXXXForm". Т.е. что данные совместимы с входным параметром соответствующего прецедента.

Итак - прецеденты и их данные:

1. Работа с документом - TDocumentForm <=| TDocumentUseCaseData
2. Работа со словарной статьёй - TDictionEntryForm <=| TDictionEntryUseCaseData
3. Работа со статьёй энциклопедии - TEncyclopediaForm <=| TEncyclopediaUseCaseData
4. Обзор изменений документа - TDocumentReviewForm <=| TDocumentReviewUseCaseData
5. Сравнение соседних редакций документа - TEditionsCompareForm <=| TEditionsCompareUseCaseData

Теперь собственно рассмотрим - "что же такое эти TXXXUseCaseData":
Как мы уже "договорились" - они все наследуются от TUseCaseData.

Т.е. как-то так (ПОКА):
1. Работа с документом - TDocumentForm <=| TDocumentUseCaseData = class(TUseCaseData)
2. Работа со словарной статьёй - TDictionEntryForm <=| TDictionEntryUseCaseData = class(TUseCaseData)
3. Работа со статьёй энциклопедии - TEncyclopediaForm <=| TEncyclopediaUseCaseData = class(TUseCaseData)
4. Обзор изменений документа - TDocumentReviewForm <=| TDocumentReviewUseCaseData = class(TUseCaseData)
5. Сравнение соседних редакций документа - TEditionsCompareForm <=| TEditionsCompareUseCaseData = class(TUseCaseData)

Это - "крупными мазками".

Теперь поговорим об их наполнении.

Мы уже говорили о TXXXViewArea. "Активных зонах".
И "по индукции" было бы логично раз TXXXUseCaseData порождает TXXXForm, то должен найтись TXXXViewAreaData, который порождает TXXXViewArea.

Про "геометрию", Owner'ов и Parent'ов - опять же - ПОКА - не говорим.

Считаем, что TXXXViewArea имеет "конструктор" CreateViewArea(aData : TXXXViewAreaData).

Откуда берутся эти самые TXXXViewAreaData?

В описываемой модели - они "висят" в виде read-only-property на TXXXUseCaseData. И наследуются (забегая вперёд) от TViewAreaData.

(Для тех немногих, кто знаком с VCM скажу TXXXViewAreaData это ни что иное как IdsXXX)

И тогда картина описываемых прецедентов приобретает следующий вид:

1. Работа с документом - TDocumentForm <=|
TDocumentUseCaseData = class(TUseCaseData)
 property DocumentText: TDocumentTextViewAreaData;
 property DocumentContents: TDocumentContentsViewAreaData = class(TViewAreaData)
  property DocumentStructure: TDocumentStructureViewAreaData;
  property DocumentPictures: TDocumentPicturesViewAreaData;
 end;//TDocumentContentsViewAreaData
 property DocumentIncomingLinks: TDocumentIncomingLinksViewAreaData;
 property DocumentOutgoingLinks: TDocumentOutgoingLinksViewAreaData;
 property DocumentAnnotation: TDocumentAnnotationViewAreaData;
end;//TDocumentUseCaseData

2. Работа со словарной статьёй - TDictionEntryForm <=|
TDictionEntryUseCaseData = class(TUseCaseData)
 property RussianText: TRussianTextViewAreaData;
 property EnglishText: TEnglishTextViewAreaData;
 property DictionEntries: TDictionEntriesViewAreaData;
end;//TDictionEntry 

3. Работа со статьёй энциклопедии - TEncyclopediaForm <=|
TEncyclopediaUseCaseData = class(TUseCaseData)
 property Arcticle: TArcticleViewAreaData;
 property EncyclopediaContents: TEncyclopediaContentsViewAreaData;
end;//TEncyclopediaUseCaseData

4. Обзор изменений документа - TDocumentReviewForm <=|
TDocumentReviewUseCaseData = class(TUseCaseData)
 property DocumentReviewText: TDocumentReviewTextViewAreaData;
end;//TDocumentReviewUseCaseData 

5. Сравнение соседних редакций документа - TEditionsCompareForm <=|
TEditionsCompareUseCaseData = class(TUseCaseData)
 property PrevEditionText: TPrevEditionTextViewAreaData;
 property ActualEditionText: TActualEditionTextViewAreaData;
end;//TEditionsCompareUseCaseData

Итак - мы ПОКА имеем ЧЕТЫРЕ основных понятия - TXXXForm (прецедент), TXXXViewArea (активная зона/вложенный прецедент), TXXXUseCaseData (данные прецедента) и TXXXViewAreaData (данные активной зоны).

Теперь давайте более детально спроектируем один прецедент. Самый "большой". Первый. "Работа с документом":

Ранее мы ПОКА договорились, что TXXXViewArea это "нечто вроде TFrame". Пока этих знаний - нам достаточно.

Но кто же реально визуализирует данные и осуществляет интерактив с пользователем?

Этот момент - мы сейчас более-менее подробно - разберём.

Итак - наш прецедент "работа с документом" с точки зрения представления пользователю ПОКА выглядит вот так:

1. Работа с документом (TDocumentForm):
а. Собственно "текст документа" (TDocumentTextViewArea).
б. Оглавление документа (TDocumentContentsViewArea):
б.1 Список структурных единиц документа (TDocumentStructureViewArea).
б.2 Список иллюстраций в документе (TDocumentPicturesViewArea).
в. Список входящих ссылок на документ (TDocumentIncomingLinksViewArea).
г. Список исходящих ссылок из документа (TDocumentOutgoingLinksViewArea).
д. Аннотация к документу (TDocumentAnnotationViewArea).

Давайте разберёмся - какие конечные компоненты визуализируют те или иные "активные зоны".

Пусть из требований следует что:
а. Собственно "текст документа" (TDocumentTextViewArea) - отображается в виде "текста с гиперссылками и прочим оформлением".
б. Оглавление документа (TDocumentContentsViewArea):
б.1 Список структурных единиц документа (TDocumentStructureViewArea) - отображается в виде "древесной структуры".
б.2 Список иллюстраций в документе (TDocumentPicturesViewArea) - отображается в виде "одноуровневого списка".
в. Список входящих ссылок на документ (TDocumentIncomingLinksViewArea) - отображается в виде "одноуровневого списка".
г. Список исходящих ссылок из документа (TDocumentOutgoingLinksViewArea) - отображается в виде "одноуровневого списка".
д. Аннотация к документу (TDocumentAnnotationViewArea)- отображается в виде "текста с гиперссылками и прочим оформлением" (как и сам текст документа).

Итак мы ПОКА имеем три вида отображения данных:
1. "текст с гиперссылками и прочим оформлением".
2. "древесная структура".
3. "список".

(Не могу не отметить, что для БОЛЬШИНСТВА бизнес-приложений этих способов представления данных - БОЛЕЕ чем достаточно)

Пусть у нас УЖЕ ЕСТЬ компоненты, которые умеет отображать структуры данных в заданном виде:
1. "текст с гиперссылками и прочим оформлением" - TDocumentView.
2. "древесная структура" - TTreeView.
3. "список" - TListView.

Тогда - будем ПОКА считать, что в каждую TXXXViewArea (которая ПОКА по договорённости является TFrame) - вставлен - соответствующий компонент. Например как alClient (это для любителе Delphi). Больше про "геометрию", Owner'а и Parent'а - опять же - ПОКА не говорим.

Теперь как выглядит описание прецедента с точки зрения представления?

А вот так:

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

Проведём предварительный рефакторинг получившейся картины путём выделения "похожих" классов:

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

Но теперь встаёт вопрос - "откуда же берутся данные для соответствующих компонент"? Как TXXXUseCaseData и TXXXViewAreaData коррелируют с данными для конечных компонентов?

Это вопрос - мы сейчас и разберём.

В общем - всё - по индукции.
TXXXUseCaseData - содержит в себе TXXXViewAreaData в виде read-only-property, значит - логично предположить, что TXXXViewAreaData содержит в себе TXXXViewData в виде всё тех же read-only-property.

В общем - так оно и есть.

Картина приобретает следующий вид:
type
 TDocumentUseCaseData = class(TUseCaseData)
 
  property DocumentText: TDocumentTextViewAreaData = class(TViewAreaData)
   property Text: TDocument;
  end;//TDocumentTextViewAreaData
 
  property DocumentContents: TDocumentContentsViewAreaData = class(TViewAreaData)
 
   property DocumentStructure: TDocumentStructureViewAreaData = class(TViewAreaData)
    property DocumentStructure : TTree;
   end;//TDocumentStructureViewAreaData
 
   property DocumentPictures: TDocumentPicturesViewAreaData = class(TViewAreaData)
    property Pictures : TList;
   end;//TDocumentPicturesViewAreaData
 
  end;//TDocumentContentsViewAreaData
 
  property DocumentIncomingLinks: TDocumentIncomingLinksViewAreaData = class(TViewAreaData)
   property IncomningLinks : TList;
  end;//TDocumentIncomingLinksViewAreaData
 
  property DocumentOutgoingLinks: TDocumentOutgoingLinksViewAreaData = class(TViewAreaData)
   property OutgoingLinks : TList;
  end;//TDocumentOutgoingLinksViewAreaData
 
  property DocumentAnnotation: TDocumentAnnotationViewAreaData = class(TViewAreaData)
   property Annotation: TDocument;
  end;//TDocumentAnnotationViewAreaData
 
 end;//TDocumentUseCaseData

(Для тех кто в курсе про VCM: TDocument это и есть TXXXDocumentContainer)

И ОПЯТЬ! Проведём предварительный рефакторинг получившейся картины путём выделения "похожих" классов:

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

Идея понятна?

Вот тут наверное подходящий момент, чтобы остановиться и спросить - "а зачем так сложно?"

Я сейчас постараюсь это пояснить.

Во-первых - мне кажется, что в общем - НИЧУТЬ не сложно. Процедура - ФОРМАЛЬНАЯ. И устроена - как "матрёшка", что само по себе (меня лично - радует).

Выделяем прецеденты, потом описываем вложенные прецеденты, потом описываем данные прецедентов и вложенных прецедентов. Потом - детализируем прецеденты и получаем компоненты, которые взаимодействуют с пользователем, потом - описываем - данные для компонентов. Процедура - ФОРМАЛЬНАЯ и рекурсивная.

А во-вторых - вся эта "сложность" во-первых направлена на взаимозаменяемость и гибкость архитектуры, а во вторых - на упрощение рефакторинга и повышение ПОВТОРНОГО использования.

Как? Поясню - чуть позже.

Собственно это станет видно - когда мы перейдём к оставшимся прецедентам.

(Тут "синергия" видна ИМЕННО для СЛОЖНЫХ систем и для систем в которых есть МНОЖЕСТВО "похожих", но в то же время "не похожих" прецедентов)
.....

Теперь вернёмся к списку ответственностей (требований) наших прецедентов.

ПРЕДПОЛОЖИМ, что указанные ответственности:
1. Отображение документа.
2. Скроллирование документа.
3. Навигация курсором в рамках текста документа.
4. Выделение участков текста документа.
5. Копирование выделенных участков в буфер обмена.
-- ПОЛНОСТЬЮ реализуются компонентом TDocumentView (ну - ПОВЕЗЛО нам так с указанным компонентом).

Соответственно эти ответственности - мы смело можем считать реализованными (ну по крайней мере - пока требования не поменяются). И мы СМЕЛО можем их вычеркнуть из списка ответственностей, которые нам надо реализовать. Считаем их - УЖЕ реализованными.

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

На этот вопрос - мы сейчас попробуем ответить. Реализуя далее наш "большой" прецедент - "работа с документом":

Делать мы это будем тут - http://18delphi.blogspot.com/2013/08/mvc_8.html

to be continued...

Ссылка. Чужая статья. Why UML Fails to Add Value to the Design and Development Process

Есть ещё одна проблема - "размазывание бизнес-логики по различным уровням"

Есть ещё одна проблема - "размазывание бизнес-логики по различным уровням".

И это не проблема проектирования. Просто бизнес-логика такова. Часть её влияет на данные. Часть на транспорт. Часть - на различные аспекты визуализации. Она не "солидна", она - "размазана". Просто по сути своей. А не потому, что "недодумали".

Мне лично - модель - тут тоже помогает. Она позволяет проследить такую бизнес-логику.

А коли "размазывание" - имеет место, то с "болтами-гайками" - не всё так просто. Как мне кажется.

К чему это я? А к тому, что раз имеет место "сложное принятие" решений, то оно скорее всего описывается каким-то how-to. Типа - "тут заводим объект такого, то типа, тут - такого, тут протягиваем между ними мост.. и т.п." А раз есть подобные "инструкции" и how-to, то скорее всего их можно изобразить на модели в виде "чертежа". Сделав шаблонным стереотипным решением. Вот собственно это и становится - "болтами-гайками". И процесс - рекурсивный.

Offtopic. Offtopic. "Бизнес в России"

Смотрю рекламу по ТВ. Всяких Газпромов и прочих Транснефтей.

Мотивировать пытаются - "Участием в общем деле", "необходимостью стране" и прочее и прочее.

Только не деньгами.

Это очередная попытка "украсть" или "подхвачена особенность менталитета"?

Зачем UML. Ещё раз

http://habrahabr.ru/post/188604/

Поверьте - никого не хочется "уговаривать". Но хочется всё же - "донести мысль". Тем более, что она кажется перспективной. И она - так или иначе - опробована на практике.

Основные претензии, которые я увидел, такие:

1. У вас неправильно организован процесс.
2. У вас недостаточно выразительный язык.
3. UML - мешает полёту мысли.
4. Неудобные инструменты.

Для начала хочется сказать - ужасно неприятно полемизировать с людьми скрытыми за "никами" и анонимностью.

Значит по поводу процесса. А кто-нибудь может ответить на вопросы:

1. Вы писали проекты сравнимые хотя бы с Word?
2. Сколько в них разнородных сущностей?
3. Сколько ответственностей в среднем на сущность?
4. Сколько UseCase'ов верхнего уровня в системе?
5. Каков объём требований?
6. Сколько экранных форм?
7. Сколько людей в среднем в проекте?
8. В скольких проектах используются одни и те же проектные и компонентные решения?
9. Сколько строк кода попадает в среднем в день в систему контроля версий?
10. Сколько аспектов "пронизывают" ваши приложения?

По поводу выразительности:
1. Рефакторинг. В "квадратиках" - проще.
2. Конструктивизм - возможность создания "крупных" конструкций из более "мелких" кубиков. С возможностью статической подстановки типов и определения статической арности связей.
3. Шаблонные решения. Нарисовать что-нибудь типа "фабричный метод" - однозначно проще, чем закодировать. Другие сложные шаблонные решения - тем более.
4. Тестовый проект, тестирующий GUI, из обычного проекта делается просто. Гораздо проще, чем "в коде".
5. Возможность генерации дополнительных сущностей. Например контрольных точек или обвязки для тестирования.
6. Возможность генерации инфраструктуры проекта. Например поддержки ночных сборок. Или прочих cmd-файлов или make-скриптов.
7. Наглядная трассировка требований в код и обратно.
8. Как одно из "шаблонных решений". Но стоит выделения "отдельной строкой". MVC-подобные решения - проще и правильнее воспринимаются на модели, а не в "голом" коде.
9. Форматы документов удобнее и правильнее описывать на модели (или другом мета-языке) нежели чем в императивном коде.

По поводу "мешает":
1. За себя лично - Я СЧАСТЛИВ, что модель ограничивает "поток сознания".
2. За других скажу - кому "мешает" - это он либо из "вредности" (чисто заради абсолютного обсуждения свободы), либо - от недостатка информации. В том-то и дело, что если что-то "мешает" - всегда можно настроить. Главное - ОСОЗНАВАТЬ - "что" и "зачем". А "абсолютная" свобода выражения "потока сознания" в код - если не "зло", то уж точно - неудобство. При достаточно большом количестве участников. По-любому возникают всякие "инструкции программирования" и прочие Development case.

to be continued...