Подтвердить что ты не робот

Вложенные шаблоны связи MVC

Это полностью вопрос типа практики, поэтому язык не имеет значения. Я понимаю основные принципы MVC и что существуют разные тонкие вкусы (т.е. Представления, имеющие прямую ссылку на модели против делегата данных с контроллера).

Мой вопрос связан с перекрестным сообщением MVC, когда эти MVC вложены. Примером этого может служить программа рисования (например, Paint или что-то еще). Сам холст может быть MVC, но так же может быть каждый нарисованный объект (например, Shapes, Text). С точки зрения модели имеет смысл для CanvasModel иметь набор сущностей, но должны ли CanvasView и CanvasController иметь соответствующие коллекции представлений и контроллеров сущностей соответственно?

Кроме того, какой лучший/самый чистый способ добавить новый объект? Скажите, что у пользователя активен CircleTool, они нажимают вид Canvas и начинают рисовать фигуру. CanvasView может запускать соответствующие события мыши/перемещения/выключения, которые может прослушать CanvasController. Затем контроллер мог в основном проксировать эти события в CircleTool (шаблон состояния). При мыши вниз CircleTool захочет создать новый круг. Должен ли Инструмент создать новый CircleEntityController прямо и вызвать что-то вроде canvasController.addEntity(circleController)? Где же ответственность за создание модели и представления Circle тогда лежит?

Извините, если эти вопросы несколько туманны:)

- ИЗМЕНИТЬ -

Вот пример псевдокодировки того, о чем я говорю:

CircleTool {
    ...
    onCanvasMouseDown: function(x, y) {
        // should this tool/service create the new entity model, view, and controller?
        var model = new CircleModel(x, y);
        var view = new CircleView(model);
        var controller = new CircleController(model, view);

        // should the canvasController add method take in all 3 components
        // and then add them to their respective endpoints?
        this.canvasController.addEntity(model, view, controller);
    }
    ...
}


CanvasController {
    ...
    addEntity: function(model, view, controller) {
        // this doesn't really feel right...
        this.entityControllers.add(controller);
        this.model.addEntityModel(model);
        this.view.addEntityView(view);
    }
    ...
}
4b9b3361

Ответ 1

Ничего себе, хорошо, что у меня есть, возможно, удивительный ответ на этот вопрос: у меня давний разглагольствование о том, как MVC считается этим ярким символом совершенства в программировании, с которым никто не видит никаких проблем. Излюбленный вопрос интервью: "Каковы некоторые проблемы или проблемы, с которыми вы можете столкнуться в MVC?" Удивительно, как часто этот вопрос встречается с озадаченным, мутным взглядом.

Ответ на самом деле довольно прост: MVC полагается на понятие нескольких потребителей, имеющих свои потребности, от одного объекта общей модели. Вещи действительно начинают уходить в ад, когда разные взгляды предъявляют разные требования. Несколько лет назад была статья, в которой авторы выдвинули понятие иерархического MVC. Кто-то еще пришел в комментарии и сказал им, что то, что они думали, что они изобретают, уже существует: шаблон, называемый Presentation-Abstraction-Controller (PAC). Вероятно, единственное место, которое вы видите в литературе по шаблонам, - в книге Бушманна, иногда называемой "Банда из пяти", или POSA (Архитектура программного обеспечения с программным обеспечением). Интересно, что всякий раз, когда я объясняю PAC, я использую программу рисования в качестве прекрасного примера.

Основное отличие состоит в том, что в PAC элементы представления, как правило, имеют свои собственные модели, что A из PAC: для каждого компонента вам не обязательно, но может иметь абстракцию. По некоторым другим ответам здесь, то, что происходит, у вас есть координационный контроллер, в этом случае, который будет управлять над холстом. Предположим, мы хотим добавить небольшой вид в сторону холста, который показывает количество различных фигур (например, квадраты 3, круги 5 и т.д.). Этот контроллер будет регистрироваться в координационном контроллере для прослушивания двух событий: elementAdded и elementRemoved. Поскольку он получил каждое уведомление, он просто обновил карту, имевшуюся в своей абстракции. Представьте себе, насколько абсурдно было бы изменить общую модель, которую куча компонентов использует, чтобы добавить поддержку для такой вещи. Кроме того, человеку, который сделал компонент ShapesSummary, не нужно было ничего узнавать, но протоколы событий и, конечно же, все его взаимодействия с соавторами неизменны.

Иерархическая часть состоит в том, что в PAC могут быть уровни контроллеров: например, координация на уровне Canvas не будет знать ни о каких компонентах со специализированным поведением. Мы могли бы создать форму, которая может содержать другие вещи, для которых потребуется логика для принятия перетаскиваний и т.д. У этого компонента будет своя абстракция и контроллер, и что контроллер будет координировать свои действия с CanvasController и т.д. Это может даже потребоваться в некоторая точка для связи с содержащимися в нем компонентами.

Здесь POSA Book.

Ответ 2

Существует много разных способов атаковать это. Я не согласен с другими ответами, размещенными здесь.

Однако один из способов, которым я это сделал, - использовать шаблон наблюдателя. И позвольте мне объяснить, почему.

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

Каждый инструмент, который можно использовать на холсте, добавляется как наблюдаемое событие. Затем, когда событие запускается - например, "начать рисовать" - инструмент отправляется как контекст (в данном случае "круг" ). Оттуда выполняются действия инструмента круга.

Другим способом представить это является то, что каждый слой имеет свой собственный сервис, модель и представление. Контроллер действительно находится на внешнем уровне и связан с холстом. Таким образом, услуги вызываются только другими службами или контроллером. Нет никаких контроллеров инструментов окружения - так что может вызвать только другой сервис (в нашем случае наблюдаемое событие). Эта служба отвечает за агрегирование данных, построение модели и предоставление представления.

Ответ 3

RenderingService (из-за отсутствия лучшего имени - того, что управляет взаимодействием фигур) будет создан новый объект Circle домен и сообщает об этом (либо непосредственно, либо когда представление запрашивает новые данные).

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

P.S. Я предполагаю, что вы не говорите о HMVC.

Ответ 4

Это всего лишь идея, но подумайте, применим ли шаблон посредника.

Из банды четырех:

Намерение

Определить объект, который инкапсулирует, как взаимодействует набор объектов. Посредник продвигает свободную связь, удерживая объекты от ссылки на друг друга явно, и это позволяет вам изменять их взаимодействие независимо друг от друга.

Применимость

Используйте шаблон посредника, если

  • набор объектов обменивается четко определенными, но сложными способами. Получающиеся взаимозависимости неструктурированы и понять.
  • Повторное использование объекта затруднено, поскольку оно относится к другим объектам и связывается с ними.
  • поведение, распределенное между несколькими классами, должно настраиваться без большого количества подклассов.

Последствия

Посредник имеет следующие преимущества и недостатки:

  • Он ограничивает подклассы. Медиатор локализует поведение, которое иначе было бы распределено между несколькими объектами. Изменение этого для поведения требуется только подклассический посредник; Классы коллег могут быть повторно используется как есть.
  • Он отделяет коллег. Посредник способствует свободному взаимодействию между коллегами. Вы можете изменять и повторно использовать классы коллег и посредников самостоятельно.
  • Он упрощает протоколы объектов. Посредник заменяет взаимодействие "многие-ко-многим" с взаимодействием "один-ко-многим" между посредником и его коллеги. Отношения "один ко многим" легче понять, поддерживать и расширять.
  • Он абстрагирует, как взаимодействуют объекты. Создание медиации независимой концепции и ее инкапсуляция в объект позволяет сосредоточиться о том, как объекты взаимодействуют отдельно от их индивидуального поведения. Это может помогите выяснить, как объекты взаимодействуют в системе.
  • Он централизует контроль. Шаблон медиатора торгует сложностью взаимодействия для сложности в медиаторе. Потому что посредник инкапсулирует протоколы, он может стать более сложным, чем любой коллега. Это может сделать сам медиатор монолитом, который трудно поддерживать.

Ответ 5

Если бы я был вами, я бы выбрал композитный рисунок при работе с фигурами. Независимо от того, есть ли у вас круги, квадраты, прямоугольники, треугольники, буквы и т.д., Я бы рассматривал все как форму. Некоторые формы могут быть простыми формами, такими как линии, другие формы могут быть более сложными составными фигурами, такими как графики, круговые диаграммы и т.д. Хорошей идеей было бы определить базовый класс, который ссылается на основные фигуры и на сложные (сложные) фигуры. Как основные фигуры, так и расширенные формы - это один и тот же тип объекта, а именно, что у расширенных форм могут быть дети, которые помогают определить этот сложный объект.

Следуя этой логике, вы можете использовать логику, в которой каждая форма рисует себя, и каждая форма знает свое местоположение, и на основе этого вы применили бы определенную логику и алгоритм, чтобы спросить, какая фигура была нажата, и каждая фигура может ответить на ваше событие.

В соответствии с GoF book:

Намерение

Составляйте объекты в древовидные структуры для представления иерархии целого целого. Composite позволяет клиентам обрабатывать отдельные объекты и композиции объектов равномерно.

Мотивация

Графические приложения, такие как редакторы чертежей и системы захвата схем, позволяют пользователям создавать сложные диаграммы из простых компонентов. Пользователь может группировать компоненты для формирования более крупных компонентов. [...]

Ключ к составному шаблону представляет собой абстрактный класс, который представляет как примитивы, так и их контейнеры. Для графической системы этот класс является графикой. Graphic объявляет операции типа Draw, которые относятся к графическим объектам. Он также объявляет операции, которые объединяют все составные объекты, такие как операции для доступа и управления его дочерними элементами.

Теперь вернемся к вашей проблеме. Одна из базовых функций, как упоминалось ранее, является методом Draw. Этот метод может быть реализован как абстрактный метод в базовом классе со следующей сигнатурой:

public virtual void Draw(YourDrawingCanvas canvas);

Каждая форма реализует свою собственную версию Draw. Аргумент, который вы передаете им, является ссылкой на холст чертежа, где каждая фигура будет рисовать себя. Каждая форма может хранить ссылку на себя во внутренней структуре, и ее можно использовать для сравнения местоположения щелчка мыши с этими координатами, чтобы сообщить вам, какая форма была нажата.

Ответ 6

С моей точки зрения, используя вложенные компоненты MVC, здесь вид избытка: В каждый момент времени модель содержит несколько элементов (разные круги, квадраты и т.д., которые могут быть вложенными конструкциями с использованием шаблона Composite, как указано в другой ответ). Тем не менее, холст, отображающий элементы, - это всего лишь одно представление! (И, что соответствует единому виду, потребуется только один контроллер.)

Один случай наличия нескольких представлений может быть списком элементов (который показан, например, рядом с холстом) - тогда вы могли бы реализовать холст и список элементов как два разных вида на одной и той же модели.

Что касается вопроса о том, как "лучше всего" реализовать добавление элемента: я бы рассмотрел следующую последовательность событий:

  • Представление уведомляет своих слушателей о том, что был нарисован новый элемент окружности (например, средняя точка и начальный радиус в качестве параметров).
  • Контроллер зарегистрирован в качестве слушателя для представления, поэтому вызывается метод прослушивания "draw-circle (point, radius)".
  • Контроллер создает экземпляр нового экземпляра в модели (либо непосредственно, либо через класс factory, который является частью модели), я думаю, что существует множество различных способов реализации создания новых элементов). Контроллер "находится в управлении" (буквально), поэтому я считаю, что ответственность диспетчеров заключается в создании нового элемента (или, по крайней мере, инициировании экземпляра).
  • В модели на предыдущем шаге вызывается какой-то метод "добавить элемент".
  • Модель поднимает уведомление о "новом элементе" всем своим слушателям (возможно, передавая ссылку на вновь созданный элемент).
  • Холст зарегистрирован в качестве слушателя для модели, поэтому вызывается метод "listener", созданный с помощью canvas canvas.
  • В качестве ответа на последнее уведомление холст рисует круг (сам по себе).

Ответ 7

Я предполагаю поведение, подобное MSPaint, где активный инструмент создает векторный графический глиф, который пользователь может манипулировать до тех пор, пока он не удовлетворится. Когда пользователь удовлетворен, глиф записывается на изображение, которое является растром пикселей.


Когда инструмент Circle Tool будет выбран, я бы CanvasController отменил выбор ранее активного инструмента MVC trio (если был активен другой инструмент) и создать новый CircleToolController, CircleModel и CircleView. Ранее активный глиф становится окончательным и привлекает себя к CanvasModel.

CanvasView необходимо передать ссылку на CircleView, чтобы он мог нарисовать пиксели CanvasModel на экране до того, как Circle будет нарисован. Фактическое рисование круга на экране, я бы делегировал CircleView.

Поэтому CircleView должен знать и наблюдать другие, более общие классы моделей, помимо CircleModel, я имею в виду модель выбора цвета/палитры и модель для стиля заполнения и толщины линии и т.д. Эти другие модели жить до тех пор, пока приложение выполняет и имеет свой собственный вид и контроллер. В конце концов, они полностью отделены от остальной части приложения.


В качестве побочного элемента: вы могли бы отделить чертеж CanvasModel (растра цветов пикселей) от CanvasView от координации обновления всего экрана. У более высокого уровня PaintView, который знает CanvasView и активный GlyphView (например, CircleView), скоординирует чертеж между CanvasView и GlyphView.