Предисловие к переводу
На днях обнаружил, что в русскоязычной сети нет перевода этой раритетной статьи, которая положила начало разработке самого известного архитектурного паттерна MVC. Восполняем пробел.
Содержание этого документа интересно и с исторической точки зрения (а как там “деды” воевали программировали), так и в плане уточнения некоторых современных представлений об этом паттерне и программной архитектуре в целом.
Иллюстрации по максимуму сохранены как в оригинале. По ходу есть сноски с подробностями и ассоциации редактора в конце.
СУЩНОСТЬ-МОДЕЛЬ-ВЬЮ1-РЕДАКТОР
на примере из системы планирования2
Кому: LRG3
От: Тригве Реенскауг4
Файл: [IVY]<Reenskaug>SMALL>TERMINOLOGY2.DOC
Дата: 12 мая 19795
Цель данной заметки - исследовать метафоры thing-model-view-editor через последовательный набор примеров. Все примеры взяты из моей системы планирования и иллюстрируют вышеуказанные четыре понятия. Все примеры были реализованы, хотя и не в рамках чистой структуры классов, описанной здесь. Метафоры соответствуют real world-Model-view-Tool, предложенным в заметке о требованиях DynaBook ([Ivy]<Reenskaug>DynaBook.doc).
THING (СУЩНОСТЬ)
ОПИСАНИЕ ТЕРМИНА
Нечто, представляющее интерес для пользователя. Это может быть что-то конкретное, как дом или интегральная схема. Это может быть что-то абстрактное, как новая идея или мнения о статье. Это может быть целое, как компьютер, или часть, как элемент схемы.
ПРИМЕР: КРУПНЫЙ ПРОЕКТ
Сущность здесь - это крупный проект. Это может быть проектирование и строительство большого моста, электростанции или морской нефтедобывающей платформы.
Такие проекты представляют собой сложные задачи с огромным количеством взаимозависимых деталей. Ответственные за проект должны отслеживать все эти детали и их зависимости, чтобы понимать последствия различных реальных или предполагаемых ситуаций, которые могут возникнуть.
Для контроля крупных проектов используется большое разнообразие различных абстракций. Каждая абстракция выделяет определенные аспекты общего проекта и используется отдельно или вместе с другими абстракциями для контроля этих аспектов. Примерами абстракций являются способы мышления о требованиях к материалам; оценка затрат; различные теории бюджетирования и учета.
Одной из особенно полезных абстракций является понятие сетей активностей или диаграмм Pert. Эта абстракция связывает воедино то, что должно быть сделано, с тем, кто должен это делать, и когда это должно быть сделано.
В сетевой абстракции каждая задача, которая должна быть выполнена в проекте, отображается как простой элемент, называемый активностью. В основном активность характеризуется своей продолжительностью и предшественниками. Продолжительность определяет время, необходимое для выполнения соответствующей задачи, а предшественники - это активности, которые должны быть завершены до того, как может быть начата текущая активность.
Эта простая абстракция обычно расширяется различными способами. На основе предшественников активностей легко найти последователей каждой активности. Начальные активности сети - это все активности, не имеющие предшественников, а конечные ��ктивности - это активности, не имеющие последователей. К каждой активности может быть привязана другая информация. Требования активности к ресурсам - один пример, стоимость и денежный поток, связанные с активностью - другой.
MODEL (МОДЕЛЬ)
ОПРЕДЕЛЕНИЕ
Модель - это активное представление абстракции в форме данных в компьютерной системе.
КОММЕНТАРИИ
Как упоминалось выше, обычно существует множество различных способов абстрагирования одной и той же Сущности, поэтому часто полезно иметь несколько сосуществующих Моделей данной Сущности. Альтернативно можно думать о проекте как имеющем одну большую Модель, которая подразделяется на несколько суб-Моделей.

Модели представлены в компьютере как коллекция данных вместе с методами, необходимыми для обработки этих данных.
В идеале все модели должны быть полностью согласованными. Этот идеал недостижим на практике, так как потребовал бы подавляющей бюрократии и удушающей жесткости. Поэтому следует принять некоторые несогласованности, стремясь к тому, чтобы общий набор моделей был разумно точным представлением проекта без чрезмерных усилий на мелочи.
В нашем примере данная сетевая Модель представлена в системе Smalltalk как экземпляр класса NetworkModel. Каждая активность в сетевой Модели представлена как экземпляр класса Activity.
Одно из полей, определенных для класса NetworkModel, - это активности (activities), Dictionary, соединяющий имена активностей (UniqueString) и экземпляры Activity. Три поля, определенные для класса Activity: network, predecessors и successors. network содержит указатель на соответствующий экземпляр NetworkModel, а predecessors и successors - это Vectors указателей на экземпляры Activity.
Общая структура нашей Модели (= Smalltalk представление сетевой модели проекта) выглядит следующим образом:

В рамках этой общей структуры мы можем теперь добавить информацию о нашем проекте, которая может быть связана с сетью в целом и с каждой из ее активностей. К network мы можем, например, добавить поля plannedStart и plannedFinish. К определению класса Activity мы можем добавить поля duration, earlyStart, lateStart и resourceRequirements. Мы также можем добавить поля для позднего начала и позднего окончания, но решаем вычислять эти величины при необходимости (из duration и earlyStart или lateStart).
Обратите внимание, что сетевая Модель с ее суб-Моделями для каждой активности не содержит информации о том, как информация может быть показана на экране, это чистое представление абстрактной модели проекта.
VIEW (ВЬЮ)
ОПРЕДЕЛЕНИЕ
К любой данной Модели прикреплен один или несколько Вью, каждый Вью способен показывать одно или несколько графических (ред. - визуальных) представлений Модели на экране и на бумажной копии. Вью также способен выполнять такие операции над Моделью, которые разумно связаны с этим Вью.
КОММЕНТАРИЙ
Реализация всех Вью была бы упрощена, если бы они основывались на метафорах Form-Path-Image.
ПРИМЕР 1: Список сетей

Этот Вью принадлежит супер-сети, т.е. коллекции сетей. Таким образом, он немного выходит за рамки обсуждаемых до сих пор Моделей, но включен для полноты. Внешний вид списка сетей показан слева.
Список сетей является экземпляром класса NetworkList, который является подклассом ListView.
ListView имеет поля, где он запоминает свой фрейм, список текстовых элементов и возможное выделение в списке. Он способен отображать свой список на экране в пределах своего фрейма и реагирует на сообщения с просьбой прокрутить себя. Он понимает сообщение с запросом элемента, расположенного в данной точке в пределах его фрейма, и сообщение с просьбой выбрать данный элемент.
ListView, таким образом, несколько похож на ListPane в текущей системе, но он не является подклассом Window и не может быть запланирован. Поэтому он должен зависеть от Редактора, который сообщит ему, где находится его фрейм, и организует прокрутку. Одна возможная последовательность для выбора заключается в том, что Редактор реагирует на redbug и запрашивает у ListView элемент, на который указывают. Затем Редактор просит ListView и любые другие Вью выбрать этот элемент. Такое разделение пользовательского интерфейса и фактического выполнения команд обеспечивает большую гибкость в Редакторе.
Класс NetworkList основывается на общих возможностях ListView. Он должен знать, что данный список - это список сетей, и должен знать, как получить такой список. Кроме того, он должен быть способен выполнять команды, которые могут представлять интерес в связи с его Вью. Мы рассмотрим только две такие команды: дать имя выбранной сети и сказать Редактору редактировать данную сеть.
ПРИМЕР 2: Список активностей в сети

Список активностей в сети является экземпляром класса ActivityList, который является подклассом ListView (см. выше описание этого общего класса).
Экземпляр класса ActivityList должен знать, к какой сети он принадлежит, и как получить список всех активностей в этой сети. Нет специальных команд, кроме общих механизмов выбора, предоставляемых ListView.
ПРИМЕР 3: Вид атрибутов активности

Это текстовое представление всех атрибутов активности. Это экземпляр класса ActivityText, который является подклассом TextView.
Класс TextView имеет поля, где он запоминает свой фрейм и абзац текста. Он способен отображать текст в пределах своего фрейма с переносом строк и понимает сообщения, которые просят его прокрутить текст в пределах фрейма. Он также способен связывать данные координаты с позициями в тексте и выделять текст между заданными позициями. Он далее понимает сообщения с просьбой выполнить различные манипуляции на основе выделения, такие как replace, cut, paste, again и т.д.
Класс TextView, таким образом, похож на текущий ParagraphEditor, но каждый прямой пользовательский интерфейс изменен на одно или несколько сообщений, чтобы он мог работать вместе с другими Вью через Редактор и с различными Редакторами.
Класс ActivityText должен знать, как получить текст для данной активности. Эта операция может быть удобно запущена сообщением select, обычно от Редактора. Он должен далее быть способен реагировать на сообщения об активности, инициированные пользователем через Редактор. Один набор таких сообщений применяется к модификации данных, представленных во Вью, это обрабатывается суперклассом TextView. Вероятно, самая важная команда для самого ActivityText - это accept, запрашивающая у Вью проверку его текущего содержимого и передачу информации в сетевую Модель как обновление атрибутов активности.
Этот Вью мог бы быть значительно улучшен, если бы класс был сделан подклассом табличного Вью, а не текущего бегущего текста.
ПРИМЕР 4: Сетевая диаграмма

Диаграмма выше является экземпляром класса DiagramView. Это единственный пример, где данные во Вью не могут быть полностью выведены из его Модели, и он должен иметь поля, содержащие форму и положение всех символов, поскольку эта информация не является частью самой Модели. (Существуют программы для автоматического позиционирования символов в диаграмме, но мы предполагаем, что потребуется хотя бы некоторое ручное редактирование). Размещение стрелок может быть выведено из зависимостей между активностями, эта информация может быть получена из NetworkModel всякий раз, когда это необходимо для отображения. Однако такая процедура будет очень медленной, и мы предполагаем, что Diagram View хранит копию этой информации для эффективности.
Как и все другие Вью, Diagram View должен будет предоставить две операции, необходимые для выбора активности, и операции, необходимые для прокрутки (на этот раз в двух измерениях). Кроме того, ему понадобятся некоторые операции над самим Вью, они связаны с позиционированием символов в диаграмме. Другие операции связаны с сетевой Моделью, например, изменение зависимостей активности и перенос активности из одной сети в другую.
ПРИМЕР 5: Вариант сетевой диаграммы

Этот Вью предоставляет альтернативу предыдущему примеру. Символы меньше, и поэтому возможно представить большую часть сети на экране. Малый размер символа делает непрактичным печатать имя активности в символе, и пользователь должен получить эту информацию другими средствами.
Сетевая диаграмма представлена на сетчатом фоне. Это используется, когда пользователь размещает диаграмму, и точки определяют допустимые позиции символов активности.
Этот Вью может быть реализован как подкласс DiagramView, или оба могут быть подклассами некоторого общего Вью. В текущей реализации, однако, оба Вью могут генерироваться поочередно одним и тем же объектом: класс DiagramView имеет поле displayType, которое может принимать значения #large или #small, и различные методы отображения контролируются этим полем. Сетка отображается в диаграмме через отдельный метод.
ПРИМЕР 6: Диаграмма Гантта

Этот Вью показывает активности вдоль вертикальной оси и время вдоль горизонтальной оси. В этом конкретном Вью показано только одно временное расписание для каждой активности. Путем изменения затенения довольно легко расширить представление, чтобы включить, насколько каждая активность может плавать во времени без нарушения общего расписания проекта.
Диаграмма является экземпляром класса GanttView, который является подклассом ChartView. ChartView знает о фоне диаграммы: ось с легендой, сетка и т.д. Он не знает ничего об информации, которая должна быть помещена в диаграмму, в данном случае горизонтальные полосы. Однако он помогает своим подклассам в представлении этой информации, предоставляя методы для преобразования из любой системы координат, используемой внешне, в координаты фрейма диаграммы.
Класс GanttView понимает ряд сообщений от пользователя, чаще всего передаваемых через Редактор. Одно такое сообщение - viewNetwork:, это сообщение предоставляет имя текущей NetworkModel. Он также сможет ответить на вопрос о том, какая активность принадлежит полосе под данной точкой в диаграмме, и выбрать полосу, принадлежащую данной активности. Он также способен передавать операции над сетью и ее активностями, которые связаны с этим конкретным Вью. Типичные операции связаны с изменением текущего расписания. Должны быть предоставлены операции для планирования сети в целом (backload: и frontload:), для изменения расписания отдельной активности (plannedStart: и plannedFinish:), и для того, чтобы последствия такой модификации распространялись к началу или концу сети.
NetworkModel должна быть способна ответить на несколько вопросов от GanttPane, давая список всех активностей, расписание сети в целом и расписание каждой отдельной активности.
ПРИМЕР 7: Диаграмма ресурсов

Эта диаграмма показывает сумму требований к ресурсам для активностей как функцию времени. (В общем случае будет одна такая диаграмма для каждого типа ресурса.)
Диаграмма является экземпляром класса ResourceView, который является еще одним подклассом DiagramView. Комментарии о диаграмме Гантта применимы и к этой, но концепция выбора нуждается в разработке. Ответ Вью на who:point? может быть набором всех активностей, которым нужны ресурсы в момент времени, соответствующий x-значению точки. Реакцией на сообщение select: activity может быть выделение требований к ресурсам этой конкретной активности. Для симметрии было бы хорошо объединить эти два, и открыть возможность для множественных выборов во всех Вью NetworkModel.
EDITOR (РЕДАКТОР)
ОПРЕДЕЛЕНИЕ
Редактор - это интерфейс между пользователем и одним или несколькими Вью. Он предоставляет пользователю подходящую систему команд, например, в форме меню, которые могут динамически изменяться в соответствии с текущим контекстом. Он предоставляет Вью необходимую координацию и командные сообщения.
КОММЕНТАРИЙ
Пользовательская задача обычно может быть лучше выполнена с несколькими Вью, присутствующими на экране одновременно. Пользователь захочет манипулировать этими Вью, указывая на них, через выбор в меню или другими средствами. Команды, такие как выбор, обычно применяются к нескольким Вью одновременно. Цель Редактора - установить и позиционировать данный набор Вью на экране, координировать их и предоставить пользователю подходящий командный интерфейс.
ПРИМЕР 8: Рабочее пространство UserView

Это очень общий Редактор, предоставляющий пользовательский интерфейс к любой части работающей системы Smalltalk, которая может быть достигнута из глобальной переменной. Поэтому он может использоваться как интерфейс к Вью системы планирования, но он оставляет большую часть работы пользователю.
Поскольку этот Редактор всегда доступен, он используется для запуска более специализированных Редакторов и для выполнения команд, которые не используются достаточно часто, чтобы гарантировать такой редактор. Одним из примеров последнего является команда для выгрузки файла полной базы данных.
ПРИМЕР 9: Воображаемый редактор планирования

Этот Редактор очень похож на предыдущий, но он был создан в среде демонстрационной сети. Сообщения к этой сети и всем ее активностям могут поэтому быть введены и выполнены непосредственно через команду doit. Полные протоколы сети и активностей поэтому доступны через этот Редактор.
Редактор не был реализован в этой форме, но как часть большего Редактора, показан в следующем примере.
ПРИМЕР 10: Суб-Редактор, принадлежащий демонстрационному Редактору

Этот Редактор является частью большего демонстрационного Редактора (см. следующий пример) и предназначен для работы в его контексте. (Иллюстрация примера 9 была фактически тем же Редактором, прокрученным к его верху.)
Редактор является экземпляром класса DemoEditor, который является подклассом TextEditor. Общие тексты могут поэтому быть введены, сохранены и отредактированы в этом Редакторе. Далее, поскольку DemoEditor знает о демонстрационной сети anet и всех ее активностях, любое сообщение, понимаемое этими объектами, может быть введено и выполнено через команду doit.
Поскольку этот Редактор является частью большего демонстрационного Редактора (см. следующий пример), мы можем выполнять более общие выражения, такие как activity duration, где activity интерпретируется как текущая выбранная активность.
Вариант этого суб-Редактора мог бы общаться с экземпляром подкласса ListView, ограничивая возможности пользователя выполнением предопределенных команд путем запрета любого редактирования показанного текста.
ПРИМЕР 11: Демонстрационный Редактор
Внешний вид этого Редактора на экране показан на следующей странице. Редактор используется для демонстрации того, что одна данная Модель может отображаться через множество различных Вью.
Демонстрационный Редактор реализован как экземпляр класса DemoEditor, который является подклассом PanedWindow. Каждая из его панелей является суб-Редактором, который общается со своим конкретным Вью. Он также дает команды демонстрационному Редактору и получает команды от него.

Примечания редактора
1. В русскоязычных работах по программированию исходное слово View часто переводится как представление. Но в контексте статьи Вью смотрится лучше. Хотя оно и не совсем корректно с точки зрения устоев языка, но прижилось как сленговое выражение.
2. В данном случае речь идёт о “системе планирования” и соответствующем ПО большого производственного проекта (см. в примечаниях ниже), которым автор руководил ранее и по причине успешной реализации которого он был приглашён в PARC.
3. LRG - это Learning Research Group или Группа исследования обучения. Это исследовательская лаборатория под руководством Алана Кея, работавшая в легендарном PARC, исследовательском центре компании XEROX в Пало Альто (Калифорния). Группа изучала, как люди (особенно дети) учатся через взаимодействие с компьютером. С этой целью они разрабатывали портативный компьютер под названием Dynabook. Алан Кей называл его усилителем обучения и мышления (“learning and thinking amplifier”). Также они разрабатывали один из первых объектно-ориентированных языков программирования Smaltalk. Таким образом, автор данного документа адресует его всем членам группы.
4. Автор документа. Оригинальное написание имени - Trygve Reenskaug. Он был норвежским учёным в области компьютерных наук. Известность к нему пришла после создания программы автоматизированного проектирования и производства (CAD/CAM), которая успешно использовалась судостроительными верфями по всему миру. В 1978 году он был приглашён в Learning Research Group, где проработал порядка 2-х лет. В материалах группы упоминается, что он разрабатывал подходы к отображению данных. Здесь надо упомянуть, что как раз в это время в PARC шла активная работа по внедрению изобретённого в этом же центре графического интерфейса, который и потребовал разработки программного обеспечения для показа данных на экране компьютера. В частности, по отношению к автору данного документа речь шла о разработке редактора диаграмм как одного из вариантов организации сложной информации.
5. Этот документ считается первым, где был описан подход, который привёл впоследствии к концептуализации архитектурного паттерна MVC.
Ассоциации редактора по ходу чтения
Развитие терминологии
Метафоры соответствуют real world-Model-view-Tool, предложенным в заметке о требованиях DynaBook ([Ivy]<Reenskaug>DynaBook.doc).
Здесь видим еще один набор понятий
realWorld - Model - View - Tool
Они перекликаются с терминами в названии рассматриваемого документа:
real World - Thing
Model - Model
View - View
Tool - Editor
Интересен также источник - заметка о требованиях (можно сказать о спецификации) того самого компьютера Dynabook, написанная самим Тригве Реенскауг.
Можно также заметить, что предшествующий набор терминов можно прочитать и так:
real world Model - view Tool
Здесь было всего два компонента - модель реального мира и инструмент для вью.
Т.е., вот так шаг за шагом выкристаллизовывается терминология и конкретизируется подход к отображению данных.
Необходимость визуализации данных, особенно в крупных проектах
“Сущность здесь - это крупный проект. Это может быть проектирование и строительство большого моста, электростанции или морской нефтедобывающей платформы.
Такие проекты представляют собой сложные задачи с огромным количеством взаимозависимых деталей. Ответственные за проект должны отслеживать все эти детали и их зависимости, чтобы понимать последствия различных реальных или предполагаемых ситуаций, которые могут возникнуть.”
Продолжая мысль автора можно сказать, что в программе Сущность трансформируется в Модель.
Но поскольку и Сущность и Модель очень сложные, то возникает необходимость в дополнительных средствах управления Моделью, а через неё и Сущностью. (Нередко такие сложные проекты называют ещё и динамическими)
И таким средством управления становится графическое, визуальное представление данных через View.
Две модели
“Модели представлены в компьютере как коллекция данных вместе с методами, необходимыми для обработки этих данных.”
Методы обработки данных в дальнейшем стали часто называть, в том числе и сам автор, бизнес-логикой. Еще вариант - предметная логика.
Но есть ещё один нюанс в понимании Модели. А именно: в последующих работах автор говорит о ментальной модели пользователя. Таким образом, получается, что есть сущность в реальном мире и есть модель этой сущности в сознании пользователя - ментальная модель.
Отсюда, мы имеем две модели: ментальную в голове пользователя и компьютерную, цифровую, программную.
В свою очередь, не только ментальная модель помогает пользователю управлять компьютерной моделью, но и компьютерная модель помогает глубже понимать сущность в реальном мире и через это управлять ментальной моделью.
Модели и классы по образу релятивных баз данных
“Одно из полей, определенных для класса NetworkModel, - это активности (activities), Dictionary, соединяющий имена активностей (UniqueString) и экземпляры Activity.”
Такое впечатление, что свойства классов создаются по образцу и подобию таблиц и их полей в релятивной базе данных.
Вполне возможно, что на первых порах так оно и было.
Взаимоотношения Модели и Вью
“К любой данной Модели прикреплен один или несколько Вью…”
Как видим, в первоначальных разработках автора Модель и Вью связаны напрямую.
"Вью также способен выполнять такие операции над Моделью, которые разумно связаны с этим Вью"
В какой-то степени это “еретическое” высказывание автора исходной концепции MVC, поскольку в современных интерпретациях этого паттерна категорически “запрещается” View и Model взаимодействовать напрямую, минуя Controller.
Вью может содержать данные, которых нет в Модели
Пример 4: Сетевая диаграмма
“Это единственный пример, где данные во Вью не могут быть полностью выведены из его Модели, и он должен иметь поля, содержащие форму и положение всех символов, поскольку эта информация не является частью самой Модели.”
Т.е., есть данные, которые не являются частью Модели.
Где же они тогда должны храниться?
“Как и все другие Вью, Diagram View должен будет … Кроме того, ему понадобятся некоторые операции над самим Вью, они связаны с позиционированием символов в диаграмме.”
По автору, сам Вью будет производить эти операции над собой.
Место редактора (контроллера) в схеме паттерна
“Редактор - это интерфейс между пользователем и одним или несколькими Вью.”
Т.е., Редактор в понимании автора, а в дальнейшем Контроллер, находится между пользователем и Вью.
В свою очередь, Модель интуитивно располагается нашим сознанием где-то позади Вью.
И здесь, конечно, надо вспомнить порядок терминов в обозначении подхода (паттерна). Расположим их в порядке появления:
real world-Model-View-Tool
Thing-Model-View-Editor
Model-View-Controller
Здесь везде практически одна и та же последовательность и Tool, Editor и Controller всегда располагаются после View.
Теперь становится понятно, почему в современных схемах Контроллер располагается посредине, а в текстовом названии - позади Вью.
Так было задумано изначально и это сохранилось в текстовом представлении паттерна. Но со временем на Контроллер возлагалось все большее количество задач и в результате он стал посредником между Моделью и Вью. Но крен в сторону работы со Вью остался, что время от времени всплывает в дискуссиях о слишком тесной привязанности Контроллера ко Вью.
Крупицы MVC
В разделе Вью автор показывает 10 примеров разных Вью и описывает свойства их классов.
И здесь по ходу встречаются первые высказывания, которые потом лягут в основу MVC.
Например:
“Такое разделение пользовательского интерфейса и фактического выполнения команд обеспечивает большую гибкость в Редакторе!
Разделение пользовательского интерфейса и команд - очень знакомо современным разработчикам!
И в описании Демонстрационного редактора (Пример 11) тоже есть привычная всем идея:
"По сути дела этот пример показывает основную идею, когда одни и те же данные могут быть показаны разными способами с помощью разных View. В данном случае, это разные диаграммы."
Заключение
В документе видно как пульсирует мысль автора, как пробиваются ростки MVC.
А это фотография самого автора

P.S. Оригинал статьи можно увидеть здесь:
https://folk.universitetetioslo.no/trygver/1979/mvc-1/1979-05-MVC.pdf