MVC, часть 1: про дубовый стол и сиськи
[Что-то очень глючил редактор, где то возможны нестыковки - текст порой просто удалялся. Замечания пишите в комменты. Спасибо]
...и вот появился на свет гений. Истинный Разработчик. Луч света падает на него, а клавиатура сокрушается под ударами его пальцев... Сейчас он сядет и сделает шедевр, в ритмах Вивальди извлекая звуки от щелчков мышки и ритмичных ударов о клавиатуре...
Думайте это о Вас? Обо мне? Пфф... Это Вам не сага о Горце. Тут придется включить свою голову. Ну или хотя бы сделать вид. Как бы то ни было - купите виски и откиньтесь на спинку кожаного кресла, положив свои ноги в ботинках из крокодиловой кожи на стол из столетнего дуба. Да-да, вы не ослышались. MVC - это действительно круто. Соплякам тут не место )
Мне всегда вот в научных статьях, учебниках по квантовой физике и статьях на википедии не нравилось одно - сухость материала. Ну пришел Шредингер, сказал что нашел универсальное уравнение в материи в целом - и понеслись иероглифы. Ну вот как это следует вообще понимать? Тут тоньше надо. Не спугнуть читателя, объяснять всё простым языком... Тут тренд Дисковери уловил, конечно. Поэтому давайте-ка я расскажу вам о трёх буквах.
Готовы? Отлично, так рад видеть на Ваших лицах заинтересованность - вот оно, "эм вэ цэ" да ещё и языком художественным текстом, где возможно. Только сразу огорчу - про дубовые столы я погорячился. Точнее, не погорячился, а совсем соврал. Это полная чушь, что MVC что-то там даёт. Да-да, я вас сильно обманул, но обещаю, что больше так делать не буду, а вы мне взамен обещайте что будете слушать меня внимательно. Договорились? Договорились. Дак вот, MVC - это просто инструмент, он не сделает из Вас ни "тру-флешера", ни человека способного на что-то большее, чем он умел до этого. Скажу больше, удобство использования вы ощутите... ну после нескольких недель работы. Это как накачать пресс - жирок то родной, булочки вкусные, а тренажеры не манят. Зато потом сверкая прессом перед девочками на пляже... ну вы поняли. Не испугались? Тогда вперёд )
Начнем, пожалуй, с истории среднестатистического флеш-разработчика. Речь, конечно же, пойдёт о человеке растущим на флеше с 0, а не матёрым Java`ером, который решил переквалифицироваться.
Сперва разработчик начинает писать свой код в кадрах/в одном классе. Он не понимает, зачем создавать другие классы, ведь всё можно написать здесь - и оформить функцией - то есть замыканием. Некоторые о приёме замыкания не знают - и слава богу - и пишут в методах.
Наступает момент, когда кода становится слишком много и под давлением общественности человек начинает делать попытки создавать другие классы. И тут же чувствует себя скованным - как же получить глобальную переменную... В следствии чего в конструктор пихает всё что только может. А ещё лучше - ссылку на класс, который его и создал. В итоге Main порождает пятерку других классов. И все знают всё о Main.
Потом человек понимает, что так передавать - крайне неэффективно. И начинает использовать синглтон. Ну как синглтон... Он думает, что это синглтон. На самом деле - просто набор статический методов и переменных. Ну должен же быть глобальный контроль!
И вот наконец человек понимает. Черт возьми, как же неудобно когда всё жутко связанно. Как много кода копируется копипастом... Как неудобно эту полоску жизней делать отдельно и для врагов, и для игрока - выгладят по разному, а работа одна и та же... Как же хочется чтобы можно было просто изменять внешний вид, а логика работы так и оставалась где то далеко... И тут мы у человека происходит озарение. Да-да, мы думаем об одном и том же. Человек начинает использовать наследование.
И вот только потом, через много времени он чувствует себя готовым. Готовым к большим открытиям и большим наградам. И виднеются на горизонте три большие, заглавные буквы - M, символизирующие Молодец разработчику, V, символизирующее пятерку за архитектуру и C - символизирующее Сиськи, которых порой так не хватает рядом во время длительной разработки. Ну, это для меня. MVC - у каждого своё. Вроде бы очевидно, что от MVC со временем не так то просто отказаться.
...Затянулось начало. Перейдём к делу.
Чтобы было сразу понятней - сразу же введем пример. Предположим, у нас есть герой у которого есть 3 вида оружия. Вид игры - сверху.
Рассмотрим отдельно все части:
M - Model. Модель. Хранит данные, которые нужны триаде (MVC). Это - координаты игрока в мире, текущие оружие в руках, количество здоровья, количество боеприпасов. Остальные данные - например количество крови хлыщущее из его вены в модели не хранятся.
V - View. Вьюшка. Отображалка. Занимается всем тем, что видит и слышит пользователь. Хранит в себе как раз количество крови, которое из игрока хлыщет, само изображение игрока, создаёт звуки выстрелов... Ну вы поняли. Это всё, что вообще есть на экране.
C - Controller. Контроллер. Он занимается логикой всей триады. Именно он обрабатывает нажатие клавиш и меняет данные в модели, записывая туда новые координаты игрока. Именно он разрешает игроку стрелять и меняет у него оружие.
Всё понятно? Не пытайтесь проводить грань и понять взаимодействие между ними - просто определитесь, кто что делает.
Сейчас я дам вам картинку, нагло взятую где то из интернета.

Какая прелесть, правда? Давайте разберемся. Сплошными линиями показаны жесткие связи между трио - то есть у контроллера есть прямая ссылка на модель и на вьюшку, а у вьюшки на модель. Штрихованными же показано кто кого слушает - контроллер слушает вьюшку, а вьюшка модель. Что такое "слушает" - ниже, на примере каждого.
Пример с игрой и человечком сверху был удачным для объяснения идеи текстом, но мне кажется для практической реализации нужно взять пример попроще. Давайте сделаем совсем элементарный пример: несколько квадратиков, все разных цветов. При этом есть одна стрелочка, которая указывает на один из квадратов. По клику стрелочка указывает на случайный квадратик, а каждые 3 секунды цвета у квадратов меняются. Это игра будет для детей дошкольного возраста: они должны будут произносить вслух цвет, на который указывает стрелочка, а воспитатель скажет, правильно или нет.
Итак. Сперва мы должны создать Главный контроллер - он всегда самый главный в любом приложении. У нас будет всего один контроллер, одна модель и одна вьюшка - поэтому контроллер главный, и по совместительству единственный. Контроллеру нужно знать, куда добавлять всю прелестную графику? Нужно. Поэтому ему нужен иметь какую-то точку привязки для графики - DisplayObjectContainer. Где её взять? Это - ваш класс Main, который базовый. Как создать главный контроллер? В Main, в конструкторе или в обработчике события Event.ADDED_TO_STAGE нужно написать примерно следующее:
Мы просто создали этот контроллер и передали ссылку на себя - то есть на то место, где будет скапливаться вся графика. Нам даже не нужно сохранять ссылку на главный контроллер!
Код класса BaseController:
package { import flash.display.DisplayObjectContainer; public class BaseController { private var _host:DisplayObjectContainer; private var _model:Model; private var _view:View; public function BaseController(host:DisplayObjectContainer) { super(); _host = host; _model = new Model(); _view = new View(_model); } } }
Давайте напишем модель. Сперва нужно понять, что должно быть в модели. В модели должны быть данные, которые нужны всей триаде. Это - цвета всех квадратов и номер квадрата, на который указывает стрелочка. "А как же графика для стрелочки, размеры квадрата, расстояние между ними и всё остальное?" - воскликнет читатель. Нет, эти данные не должны хранится в модели. Вся графика, все звуки и всё остальное - это забота вьюшки, в этом и прелесть: сегодня у меня на экране человек, а завтра киборг. При этом я переписываю только отображение, не трогая контроллер и модель, тем самым не "ломая" логику. Размер прямоугольников - не нужен в контексте этой задачи - нам плевать на форму, хоть квадратная, хоть скругленная. Нам так же плевать на позиции - хоть синусоидально, хоть по прямой. На логику это не влияет. Нам просто нужно выводить на экран какие-то цветные элементы и помечать их. Отсюда всплывает очередная прелесть - мы можем не сильно заботится о том, что творится на экране, оставив это на "потом". Будь другой контекст - например, если бы нужно было отгадать размер в пикселах этой фигуры - то эти данные попали бы в модель. Аналогично и с формой, и с расположением. Проще говоря, в модели хранятся значимые данные в данной задаче. А на фоне гусей или океана это происходит - программу не колышет.
Итак, нам нужно хранить в модели массив цветов и номер прямоугольника, на который указывает стрелка. При этом при изменении информации в модели неплохо было бы заявить всем желающим, что данные изменились - неплохо бы их обновить. Это означает, что модель должна испустить событие о том, что она изменилась. Делается это GoF паттерном Observer, но не пугайтесь: обычная событийная флешевая событийная модель реализует именно его, поэтому нам совсем не стоит об этом беспокоится. При обновлении информации есть 2 подхода: push и pull. При обновлении информации мы генерируем событие о том, что что-то изменилось. Представим, что это событие кто-то поймал. В событии может содержаться вся информация о том, что же изменилось - такой подход называется push - мы проталкиваем с событием всё что нужно обновить. Другой подход - pull - значит "тяни". В таком случае события носят нотификационный характер - что-то вроде "Эй парень, число народу в игре обновилось.". А сколько теперь онлайн - это событие не скажет. И в таком случае эти данные приходится "тянуть" с модели явно. Я использую оба подхода, оба подхода хороши. Но конкретно сейчас я ограничусь pull-подходом. Кстати, событийное поведение добавляет ещё одно удобство, одно из главных - если у нас в 5 разных местах показывается, например, баланс игрока - то при изменении баланса в одном месте он должен поменяться во всех.
Но тут всплывает проблема: поменяв номер прямоугольника, на который указывает стрелочка вызовется сеттер, который испустит событие об изменении. Но в случае с массивом всё не так: чтобы вызвать сеттер у массива нужно будет переопределить на него ссылку, что недопустимо: нам нужно просто менять элементы массива, но не ссылку на него. Решений у проблемы несколько. Например, самый извращенный - это унаследовать модель от Proxy, определив там поведение для [], попутно реализовав ей IEventDispatcher. Можно, например, сделать метод setElement, в теле которых и генерировать событие об изменении. Можно в модели добавить метод "сделай событие пожалуйста, что ты изменилась" и дёргать его. Но немного подумав вспоминаем, что модель мы меняем в контроллере. А в контроллере мы имеем полный контроль над вьюшкой. Поэтому в этом случае на мой взгляд самым лучшим решением будет явно вызвать метод у вьюшки после того как закончится изменение модели.
Код Model:
package { import flash.events.Event; import flash.events.EventDispatcher; [Event(name="change", type="flash.events.Event")] public class Model extends EventDispatcher implements IReadableModel { private var _pointer:int = 0; public var colors:Array = []; public function Model() { super(); } public function get pointer():int { return _pointer; } public function set pointer(value:int):void { if (_pointer == value) return; _pointer = value; super.dispatchEvent(new Event(Event.CHANGE)); } } }
Итак, теперь View. Как мы поняли ранее, у View есть прямая ссылка на Model, в итоге нам нужно всего лишь визуализировать эти данные.
package { import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; public class View extends Sprite { private var _model:Model; private var _squares:Array; private var _pointer:Shape; public function View(model:Model) { super(); _model = model; //здесь будем хранить ссылки на созданные графические объекты _squares = []; //это типа стрелочка _pointer = new Shape(); _pointer.graphics.beginFill(0x00FF00); //круглая такая стрелочка _pointer.graphics.drawCircle(0, 0, 30); _pointer.y = 100; _pointer.graphics.endFill(); super.addChild(_pointer); //вызываем обработчик как будто-бы модель изменилась. Это нужно чтобы указатель //сразу встал на нужный квадрат onPointerChange(null); //подписались на изменение модели _model.addEventListener(Event.CHANGE, onPointerChange); } public function updateSquares():void { //поменялись цвета - удаляем все старые прямоугольники var i:int = _squares.length; while (i--) super.removeChild(_squares.pop()); //рисуем новые i = _model.colors.length; while (i--) { var shape:Shape = new Shape(); shape.graphics.beginFill(_model.colors[i]); shape.graphics.drawRect(0, 0, 50, 50); shape.graphics.endFill(); shape.x = 100 * i; _squares.push(shape); super.addChild(shape); } } private function onPointerChange(event:Event):void { _pointer.x = 100 * _model.pointer; } } }
Абстрагируемся. Нам сообщают событиями о том, что модель изменилась. Если мы получили такое сообщение - меняем изображение, чтобы быть актуальным каким-то данным, которые есть в модели. Попутно имеем паблик метод updateSquares, который будет вызван кем-то сверху, когда нужно обновить квадраты. Если не думать о трио в целом, а только об одном элементе - всё очень просто.
Резюме. Мы имеем что-то, что предоставляет нам данные и говорит, когда данные поменялись. На основе этого мы строим изображение. Всё просто? Отлично. Теперь связываем в голове модель и вьюшку. Связываем, думаем.
Готовы продолжать? Теперь контроллер.
Задача контроллера - менять модель. Просто писать в неё данные. Абстрагируемся вновь. Мы просто делаем какую-то логику, и пишем её в объект, не задумываясь о том что происходит дальше. Но ещё нам нужно вот что сделать в контроллере: добавить вьюшку в дисплай лист - т.е. просто сделать addChild к тому, что мы передали в конструктор главному контроллеру. По условию задачи менять каждые 3 секунды цвета. Что-ж, ничего сложного! Делаем:
package { import flash.display.DisplayObjectContainer; import flash.events.TimerEvent; import flash.utils.Timer; public class BaseController { private var _host:DisplayObjectContainer; private var _model:Model; private var _view:View; public function BaseController(host:DisplayObjectContainer) { super(); _host = host; _model = new Model(); _view = new View(_model); //создали таймер и запустили var _timer:Timer = new Timer(3000); _timer.addEventListener(TimerEvent.TIMER, onTimer); _timer.start(); //сразу же вызвали обработчик, чтобы сразу при запуске забить массив //случайными квадратами onTimer(null); //добавили в дисплай лист - чтобы мы смогли увидеть работу вьюшки host.addChild(_view); } private function onTimer(event:TimerEvent):void { //забиваем модель случайными цветами var i:int = 5; while (i--) { _model.colors[i] = int(Math.random() * 0xFFFFFF); } //говорим, что мы изменили цвета //это как раз тот момент из за невозможности вызова геттера _view.updateSquares(); } } }
Как это сделать? Наверное вам в голову лезет мысль - в контроллере отлавливать клик и менять модель. Нет, это не совсем правильно: это лепка программы "под конкретный вариант", а это как раз то, против чего создано MVC. Пользователь с чем взаимодействует? Только с вьюшкой. Он не может кликнуть по модели или по контроллеру - он кликает по экрану, что по сути является вьюшкой. Значит клик должна обрабатывать вьюшка. При этом это слишком связано, что именно по клику: сегодня по клику, завтра по нажатию клавиши, а послезавтра вообще по голосовой команде. Вьюшка должна сообщить о том, что надо бы поменять стрелочку - а уж отчего вьюшка так решила знает только она сама. В итоге во View дописываем функционал, что по клику диспатчим какое-то событие, которое слушает контроллер. Я опять буду использовать событие Event.CHANGE - поначалу могут возникнуть сложности - как же так, мы ведь его уже используем в модели - но это здорово развивает абстракцию. В реальных проектах типов событий у меня очень часто больше 30-40, тут, конечно, одним Event.CHANGE не обойтись и я делаю свой класс, расширяющий Event. Однако, какой случай - такой и инструмент, поэтому здесь Event.CHANGE.
package { import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; [Event(name="change", type="flash.events.Event")] public class View extends Sprite { private var _model:Model; private var _squares:Array; private var _pointer:Shape; public function View(model:Model) { super(); _model = model; //здесь будем хранить ссылки на созданные графические объекты _squares = []; //это типа стрелочка _pointer = new Shape(); _pointer.graphics.beginFill(0x00FF00); //круглая такая стрелочка _pointer.graphics.drawCircle(0, 0, 30); _pointer.y = 100; _pointer.graphics.endFill(); super.addChild(_pointer); //вызываем обработчик как будто-бы модель изменилась. Это нужно чтобы указатель //сразу встал на нужный квадрат onPointerChange(null); //подписались на изменение модели _model.addEventListener(Event.CHANGE, onPointerChange); //нам должен быть доступен stage super.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); } private function onAddedToStage(event:Event):void { super.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage); //подписались на заветный click super.stage.addEventListener(MouseEvent.CLICK, onClick); } private function onClick(event:MouseEvent):void { //и выпустили событие super.dispatchEvent(new Event(Event.CHANGE)); } public function updateSquares():void { //поменялись цвета - удаляем все старые прямоугольники var i:int = _squares.length; while (i--) super.removeChild(_squares.pop()); //рисуем новые i = _model.colors.length; while (i--) { var shape:Shape = new Shape(); shape.graphics.beginFill(_model.colors[i]); shape.graphics.drawRect(0, 0, 50, 50); shape.graphics.endFill(); shape.x = 100 * i; _squares.push(shape); super.addChild(shape); } } private function onPointerChange(event:Event):void { _pointer.x = 100 * _model.pointer; } } }
Теперь немного перепишем контроллер:
package { import flash.display.DisplayObjectContainer; import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; public class BaseController { private var _host:DisplayObjectContainer; private var _model:Model; private var _view:View; public function BaseController(host:DisplayObjectContainer) { super(); _host = host; _model = new Model(); _view = new View(_model); //создали таймер и запустили var _timer:Timer = new Timer(3000); _timer.addEventListener(TimerEvent.TIMER, onTimer); _timer.start(); //сразу же вызвали обработчик, чтобы сразу при запуске забить массив //случайными квадратами onTimer(null); //добавили в дисплай лист - чтобы мы смогли увидеть работу вьюшки host.addChild(_view); //подписались на событие, по которому нужно изменить модель _view.addEventListener(Event.CHANGE, onViewChange); } private function onViewChange(event:Event):void { //просто случайная позиция для стрелочки _model.pointer = Math.random() * 5; } private function onTimer(event:TimerEvent):void { //забиваем модель случайными цветами var i:int = 5; while (i--) { _model.colors[i] = int(Math.random() * 0xFFFFFF); } //говорим, что мы изменили цвета //это как раз тот момент из за невозможности вызова геттера _view.updateSquares(); } } }
Теперь пролистните вверх и посмотрите снова на картинку и вновь перечитайте часть, где я рассказывал про связи между M, V и C (Model, View и Controller). Теперь всё куда понятней, не правда ли?
Но и это не всё.
View не может изменять Model, как вы уже поняли, а у нас может. Как это исправить? Вью нужно передавать не саму модель, а только интерфейс, в которым "порезаны" все set методы. Например, в нашем случае такой:
package { public interface IReadableModel { function get pointer():int; function get colors:Array; } }
Однако, мы всё равно можем изменить элемента массива colors. Можно, конечно, придумать метод getElementAt, который бы лез в массив и возвращал элемент на заданной позиции, тем самым запретив изменять элементы массива... однако запрет на изменение модели не имеет другого характера, кроме как "защиты от дурака/запарки", чтобы случайно вы сами не поменяли модель. Мне не было бы стыдно оставить интерфейс в виде, который я привёл чуть выше - если вы не доверяете себе или людям, с которыми работаете - пишите метод. Это - инструмент, и он должен подстраиваться под вас, а не вы под него. Не стесняйтесь смотреть на картинку - она сильно помогает первое время. Если она даже сейчас кажется вам сложной - не смотрите на неё, а читайте текст и рисуйте эту картинку у себя на бумажке. Не ленитесь, это действительно помогает.
На этом я закончу первую лекцию. В следующих мы будем ветвить контроллеры, делать иерархические модели, общаться с сервером. В общем, в следующие разы будет действительно интересно. Stay tuned

Вторая часть.
Всего комментариев 231
Комментарии
![]() ![]() |
|
Спасибо за отличный урок!
На простых случаях все понятно, а вот на более сложных проектах возникает куча вопросов с реализацией, поэтому жду продолжения ![]() |
![]() ![]() |
|
Очень хорошо написал. Но тема сисек до конца не раскрыта.
Да, еще неплохо было бы добавить исходники отдельным архивом. |
![]() ![]() |
|
Сиськи, сиськи... В девушке главное не сиськи, а правильное сочетание размеров и форм сисек с остальными параметрами (талия, бёдра и прочее)
![]() Статья довольно интересная. В коде тоже важно правильное сочетание, и использование MVC наряду с шаблонами может позволить сделать значительный шаг в эту сторону. |
|
Обновил(-а) f.g.programmer 30.10.2010 в 13:46
|
![]() ![]() |
|
Мы о MVC говорим, не? Да и потом, я не говорил что сиськи женские
![]() MVC он такой... коварный ) |
![]() ![]() |
|
Молодец. Пятёрка за Сиськи.
|
![]() ![]() |
|
Это неудобно - мне придётся помнить какое событие нужно кинуть меняя модель из контроллера, при этом собственно не забывая его кинуть.
|
![]() ![]() |
|
Почему неудобно? Вполне логично завести в модели такой метод
function changeColors():void { var i:int = _colors.length; while (--i) { _colors[i] = int(Math.random() * 0xFFFFFF); } dispatchEvent(new Event("change_colors")); } ну и обновлять представления на событие "change_colors" (само собой для "change_colors" нужно завести константу) |
|
Обновил(-а) f.g.programmer 30.10.2010 в 18:12
|
![]() ![]() |
|
Если частей несколько, обычно оглашают весь вписок, пожалуйста :-)
|
![]() ![]() |
|
Я вообще думал что одна часть будет. Но потом понял, что многовато будет для одной статьи и решил разбить. Будет 2 или 3
![]() |
![]() ![]() |
|
печально то как. я еще только на этапе "...думает, что это синглтон.." ))
|
![]() ![]() |
|
![]() ![]() |
|
Да. Что смешного?
|
![]() ![]() |
|
public перед function не употребляется также..
да ниче смешного, это просто не скомпилируется |
![]() ![]() |
|
Артём, отлично. Привет.)
|
![]() ![]() |
|
Цитата:
public перед function не употребляется также..
да ниче смешного, это просто не скомпилируется @Hauts: спасибо ) |
![]() ![]() |
|
Для реализации реального MVC мало просто разделить всю логику на составляющие. Необходима поддержка парадигмы Notification -> Observer. Я не хочу здесь никого рекламировать. Пишу свою собственную надстройку над одним небезизвестным фреймоворком. Базовая Архитектура примерно следующая:
Stage mediator -> работает со stage. Умеет, руководствуясь собственной логикой добавлять и удалять Вьюшки на сцену. Stage proxy -> хранит настройки stage mediator (неважно где, может хранить настройки по умолчанию а походу их менять, может записывать в shared object, может коммуницировать с сервером, либо тянуть настройки из xml зависит от типа выбраного прокси) Далее целый комплекс view Вьюшки разные, бывают статические, которые добавляются на сцену однажды при инициализации, никогда не удаляются и никак не меняются, например меню или простой бекграунд. Бывают динамические, которые добавляются по определенной логике. Каждая вьюшка имеет свой медиатор и прокси по аналогии со sтаge. Вьюшки также могут быть простыми и составными. в простых есть только ссылка на конкретный displayobject, некое api для управления объектом, а также подписка на определенные события от объекта. Когда происходит что то серьезное, медиатор отправляет notification. В составных вью вложены другие простые вью. Я называю их viewComponent, которые тоже привязаны к собственным медиаторам и прокси. Все общаются через notifications. Никто ничего не знает о своих соседях и потомках. вью не знает о том, что у нее есть медиатор, тупо отправляет события если что - то с ней случилось. Часть логики выполняется в медиаторе, если что - то серьезное: например нужно удалить со сцены текущую вью и добавить новую медиатор отправляет оповещение. Это в двух словах. Далее я уже наворачиваю уникальные прокси для лучшего контроля над системой. Есть отдельный прокси по инициализации новых вью. Есть прокси, контролирующий оперативную память. Медиатор, прослушивающий глобалную клавиатуру и т п |
|
Обновил(-а) inozemcev 31.10.2010 в 20:09
|
![]() ![]() |
|
Даже в двух словах это звучит устрашающе
![]() Для обучения сильно упрощенный и скомпилированный в голове пример Psycho Tiger лучше. |
![]() ![]() |
|
Вы, наверно, не с флешем работаете, ну или не с АС3
![]() У нас обсервер реализован нативно, как раз с нотификациями типа String. (я об этом пару слов указал). Прочитав Вас у меня возникло несколько вопросов: 1) Медиатор и Прокси не GoF`овские? Просто Ваше объяснение Цитата:
Прокси - часть программной логики, связанная с обработкой каких либо данных. Медиатор, часть программной логики связанная с визуальным объектом.
2) Я так понял StageMediator/Proxy это что то вроде главного контроллера... и контроллера вьюшки (???)? По сути если прокси - контроллер, то он заточен под что-то вроде адаптера - типа работаем с SharedObject/сервером/xml и т.д.? 3) Цитата:
Каждая вьюшка имеет свой медиатор и прокси по аналогии со sтаge
Цитата:
Медиатор, часть программной логики связанная с визуальным объектом.
4) Цитата:
Часть логики выполняется в медиаторе
Вообще по моим представлениям медиатор ставится чтобы устранить высокое сцепление объектов. На практике позволяет резко подменить вьюшку, что никто об этом не поймёт. Отсюда по моим представлением о какой-либо логике в медиаторе, по сути, речи быть не может. 5) По поводу разбора событий. Какой-то дисплей обджект выпустил событие, что нажали на меню, это поймал медиатор. Теперь медиатор это событие должен отбаблить до медиатора stage. Вопрос - как вы бабблите? В случае уровней вложенности большей двух (родитель и сын) каждое событие придётся редиспатчить, что не есть гуд - это ведь фреймворк, как никак. У Вас свой обсервер, или как Вы это делаете? Вопрос, конечно, актулаен если Вы со флешем работаете. |
|
Обновил(-а) Psycho Tiger 31.10.2010 в 22:01
|
![]() ![]() |
|
Цитата:
Медиатор и Прокси не GoF`овские?
Обсёрвер ГОФовский, он прослушивает события от пуремедиаторов и пурепрокси и либо выполняет комманды, либо говорит пуремедиаторам обновить их представления. Ещё вся эта система инициализируется и управляется через Фасад. В общем для начала довольно сложно. |
|
Обновил(-а) f.g.programmer 31.10.2010 в 22:19
|
![]() ![]() |
|
Цитата:
Разве в PureMVC эти самые медиаторы и прокси не гофовские?
|
![]() ![]() |
|
Я работаю с actionscript.
У нас обсервер реализован нативно, как раз с нотификациями типа String. (я об этом пару слов указал). Вообщем между событиями в actionscript и оповещениями есть разница. Суть именно в том, что события 'всплывают' наверх. А оповещения распространятся как бы это сказать 'горизонтально'. Допустим есть система из 15 наблюдателей, никакой иерархии зависимостей у них нет. Они ничего друг о друге не знают. Один обозреватель 'кричит' во все услышенье - 'со мной что - то случилось', и если другие обозреватели заитнересованы в этом (зарание подписались на оповещение) они отреагируют. Notification это не просто строка. Это внутренний объект в тело которого можно поместить все что угодно. Именно таким образом мой stage медиатор формирует свой лист из DisplayObject-s. Он просто получает соответствующее оповещение в теле которого есть ссылка на displayObject вьюшки и например ее id, ассоциированное с ее состоянием. Я не знаю насколько pureMVC хороший фреймоврк по сравнению с остальными. Но мне он приглянулся именно благодаря notifications. Теперь по поводу программной логики, в pureMC в можете хоть всю логику вынести за пределы прокси и медиаторов в простые и составные команды. pureMVC вас к этому и склоняет. Здесь уже я немного халтурю и оставляю логику в медиаторе, чтобы не плодить слишком простых команд. В команду я выношу какие -то важные этапы исполнения. Например команда InitUI - которая инициирует все вьюшки которые будут использоваться в проекте. Я стараюсь чтобы именно у каждой вьюшки был свой собственный медиатор, таким образом получаю более лаконичный контроль над вьюшкой нежели если иметь один общий медиатор на все. но при этом несколько вьюшек могут иметь один общий прокси, в котором описаны общие для приложения настройки. Если хотите 'тему'. Например у всех вьюшек в вашей программе может быть один общий шрифт, или общий цвет беккраунда. |
|
Обновил(-а) inozemcev 31.10.2010 в 23:11
|
![]() ![]() |
|
И еще pureMVC реализован на многих языках, так что можно писать и клиент и сервер, опираясь на общие парадигмы.
|
![]() ![]() |
|
Фактически, в пуре View играет роль глобального EventDispatcher-а. Он хранит массив всех слушателей, и при удалении конкретного медиатора происходит отписка.
Систему нотификаций в пуре можно совершенно безболезнено заменить на систему простых событий. |
|
Обновил(-а) f.g.programmer 01.11.2010 в 02:14
|
![]() ![]() |
|
не "всплытие", а "распространение".
|
![]() ![]() |
|
Да, учту, спасибо. В остальном мои догадки по поводу распространении событий верны?
|
![]() ![]() |
|
О каких именно событиях речь?
|
![]() ![]() |
|
MVC без прокси, медиаторов и обсерверов - проще.
|
![]() ![]() |
|
pureMVC, плох тем что он просто есть)
MVC фрэймворк не нужен - нужно просто осознание mvc подхода. Архитектуру проекта выстраиваем по mvc подходу, а не городим архитектуру вокруг какого нибудь pureMVC. Тяжело это и запутанно. Фасады, Мультитоны с Синглтонами.. Команды, Медиаторы и Прокси, Собственные Нотификаторы и Обсерверы... А всего навсего надо Модель, Контроллер и Виды. В pureMVC за деревьями не видно леса... Я ещё согласен с мате с его глобальной картой событий и некоторым раздолбайством.. Но pureMVC - перебор. Честно пытался его использовать, но вышло себе дороже.. А вообще кроме MVC есть MVP, MV-VM, MV with IoC и ещё куча всяких буков) |
|
Обновил(-а) Котяра 01.11.2010 в 18:46
|
![]() ![]() |
|
Хм, а действительно. call и apply как-то не применить. Какие есть другие способы?
|
![]() ![]() |
|
нормальный метод. других вариантов нет.
|
![]() ![]() |
|
Не сдержался. сохраняю эту ссылку на эту важную тему.
|
![]() ![]() |
|
Цитата:
А вообще кроме MVC есть MVP, MV-VM, MV with IoC
|
Последние записи от Psycho Tiger
- Тонкости и трюки ActionScript`а, которые... бесполезны (10.05.2011)
- Vkontakte: как пользоваться wall.post, нужен ли теперь wall.savePost? (05.03.2011)
- А пятый контер-страйк хорош. (19.01.2011)
- Пацаны, гоу Вконтакте? (21.12.2010)
- Давайте начистоту (18.12.2010)