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
Комментарии
15.11.2010 15:05 | |
Сайлас да ради бога ) удачи ) я вам говорю, что вам придётся править кучу классов, а вы меня спрашиваете "чем это плохо" )
|
15.11.2010 15:15 | |
Ладно, не IView, а BaseView, тогда не надо править все вьюхи.
Я в личку вопрос лучше перенесу, а то слишком в принципе специфичный |
|
Обновил(-а) Сайлас 15.11.2010 в 15:19
|
15.11.2010 19:49 | |
Цитата:
Ладно, не IView, а BaseView, тогда не надо править все вьюхи.
|
16.11.2010 02:03 | |
По-моему - это разгром )
|
16.11.2010 14:06 | |
mvc - не панацея. Очень часто в играх использую "контроллер" изменяющий состояния вьюшек напрямую. Это происходит в ф-ции рендера. Ибо: В момент рендеринга сначала контроллером высчитываем коллизии объектов (видов) , меняем состояние x,y[,z] вьюшек, меняем статус вида (если надо), проводим z-сортировку если надо, потом всё отрисовываем.. Модель, кстати, сама может менять себя (например твинами изменять координаты)
Если изменять виды по изменению модели - то обновления видов будут ассинхронными, а мне нужно строгая последовательность в строго выбранном тайминге. В то же самое время в этих же играх использую mvc для отображения hp и т.п. |
|
Обновил(-а) Котяра 16.11.2010 в 14:42
|
16.11.2010 14:30 | |
Вот на самом деле у меня возник применительно к этой же ситуации вопрос, на шаге изменения состояния мира после 1 шага
|
16.11.2010 15:26 | |
А почему же вы мне тут так доказывали, что это ни в коем случае нельзя делать? Если все-таки в этом случае все делают
|
16.11.2010 15:52 | |
А, что я сформулировал так, что почему бы везде не отказаться от событий? Ну если так, то прошу прощения за неправильную формулировку
|
16.11.2010 19:38 | |
Вот в MVP тоже отказались от связки модель-событие-вид.
контроллер(презентер) там как раз таки и вызывает конкретные методы у видов по интерфейсу. Но MVP всё равно считается имплементацией MVC. |
|
Обновил(-а) Котяра 16.11.2010 в 19:48
|
17.11.2010 01:58 | |
У меня вот такой вопрос есть. С точки зрения архитектуры, MVC - это все понятно и круто. Но вот есть такая ситуация - программную часть пишут одни люди, графику рисуют и собирают спрайты / мувиклипы из нее - другие. Причем вторые почти не знают АС3, ну кроме как типа stop() или play() и как создать простейшие классы и в их методах поменять что-нибудь в графике.
Так вот меня интересует один момент - получается, что каждую вьюху нужно будет собирать программерам, чтобы связать каждый элемент графики с моделью? Потому что, чтобы это все заработало - вьюха должна знать о модели. При этом получается, что так просто вьюху на другую не сменишь - у другой тоже должна быть написана программная часть, чтобы ее можно было всунуть между M и C. В этом свете, лично для меня, MVP выглядит более практичным - вся основная программная часть может писаться абсолютно независимо от графики. Единственное, что необходимо оговорить - имена мемберов во вьюхе, чтобы презентер напрямую задавал нужные значения. Или например вьюха должна имплементить определенный интерфейс, с которым бы работал презентер и которому сообщал, что изменилось. Что то вроде setHealth(...) или setMana(...). Т.о. во вьюхе мы из этих методов напрямую задавали бы что нам нужно, не задумываясь, откуда и что именно брать из модели, и что вообще есть какая-то там модель. В принципе ничто не мешает на основе MVC сделать V как промежуточное звено, которое по сути являлось бы презентером и уже дальше, опять же возможно через интерфейс, задавала бы значения вьюхе. По-моему перебор с классами и интерфейсами получается Хочу услышать конструктивную критику, в каких ситуациях, какой из этих подходов лучше / хуже. |
|
Обновил(-а) i.o. 17.11.2010 в 02:19
|
17.11.2010 02:50 | |
конструктивная критика: в разных ситуациях одни подходы лучше — другие хуже)
Я стараюсь графику вообще не использовать с кодом. графика идёт только на уровне ассетов, которые подключают к себе некие сабвью/презентеры/декораторы и исполняют роль вида уже по полной. Либо фабричный контроллер создаёт виды на основе конфигов и ассетов. Но случаи разные бывают. Иногда выдаю шаблон - говорю вот рисуйте как хотите, но называйте имена и классы как в шаблоне. Имён и строго определённых наименований линкованных спрайтов/клипов в либе в 99.9% достаточно. Даже никаких интерфейсов не надо. Ассеты, на то и ассеты. |
|
Обновил(-а) Котяра 17.11.2010 в 02:52
|
17.11.2010 03:03 | |
2Blood Котяра, честно говоря никогда не возлагал такую задачу на контроллер. у меня подобным пересчётом занималась всегда модель
тут сложность с разделением логики изменения модели от контроллера.. с этим всегда сложности у меня возникают. Я стараюсь инкапсулировать отдельные части модели друг от друга. И взаимодействие разных частей модели отдать на откуп контроллеру. т.е. изменение статуса (с "бегу" на "взорвался") отдельных частей модели связанных с персонажами в результате взаимодействия с другими частями модели. Т.е. при иерархической структуре моделей. каждый чилд знает только про себя и детей - парента он не знает. Можно переложить логику на модель верхнего уровня, но мне логичнее бизнес логику определять всё же в контроллере ибо она может меняться и больше завязана на виды. В общем сложно всё на самом деле - нет "серебрянной пули". очень часто повторно использую код, даже не изменяя а просто подключая пакеты из свн. А так как контроллер это часть которая практически всегда уникальная - всю изменяемую логику пихаю в него, причём старюсь выделять стратегии. |
|
Обновил(-а) Котяра 17.11.2010 в 03:25
|
17.11.2010 04:20 | |
Имею в виду строго определённые имена классов для спрайтов/мувиков в библиотеке fla файла. Кроме того, иногда(очень редко), нужны строго определённые имена объектов на таймлайне или даже имена меток.
По сути шаблоном я задаю "интерфейс" только не программный, а на уровне - "не сделаешь так как надо - будешь за бесплатно переделывать".. Щутка конечно.. чаще забесплатно приходится переделывать мне, т.к. объяснять что там не так - себе дороже. |
|
Обновил(-а) Котяра 17.11.2010 в 04:31
|
19.11.2010 14:49 | |
Отличная статья! Уже год пытаюсь понять MVC и перейти на него но никак не получается (( Буду очень рад второй и третей части!
|
19.11.2010 21:30 | |
@Psycho Tiger
Супер! |
14.12.2010 18:00 | |
Спасибо за статью.
Чтобы "стрелка" указывала каждый раз на разные квадрат, где поставить проверку? В контроллере? Что-то вроде: Или модель должна эта отслеживать? |
14.12.2010 18:08 | |
Модель не должна иметь мозгов чтобы решать что-то. Если "указать на другой квадрат" — это решение (а я считаю это решением), то в контроллере.
|
13.02.2011 22:52 | |
14.02.2011 01:02 | |
Цитата:
Как я понимаю контроллер всему голова, так почему документ-класс не создать сразу контроллером?
|
14.02.2011 02:23 | |
alatar, в моих реалия у меня документ-класс - не вьюха.
|
14.02.2011 14:01 | |
Document-class является тем, кто создаёт главный контроллер и параллельно является host`ом для главной вьюхи. Ну у меня )
|
14.02.2011 15:04 | |
Psycho Tiger, ну а у меня документ-класс - это контроллер, который создаёт модель и вьюху.
|
14.02.2011 15:21 | |
Тоже неплохо.
В своё время терзаний меня смущало, что контроллер будет наследовать от ДисплейОбджекта, это и послужило причиной моих текущих терзаний. |
15.02.2011 01:43 | |
Psycho Tiger, мммм ... меня тоже. поэтому у меня он не наследуется от него, как я уже писал ранее.
|
15.02.2011 16:37 | |
Опа. У тебя получается скомпилировать приложение так, что DocumentClass не наследуется от Sprite?
|
15.02.2011 17:03 | |
это же блуд
|
16.02.2011 04:22 | |
Psycho Tiger, да =) именно так. ничего сложного.
|
16.02.2011 11:33 | |
Цитата:
Psycho Tiger, да =) именно так. ничего сложного.
|
16.02.2011 11:57 | |
Интересная статейка. А можно узнать, метод "замыкания" это как ?
|
16.02.2011 12:31 | |
Цитата:
Для простых смертных - вложенная функция. Ну или http://ru.wikipedia.org/wiki/%D0%97%...D0%B8%D0%B5%29 |
16.02.2011 13:07 | |
16.02.2011 13:35 | |
BlooDHounD, ну это как бы не совсем document class, при таком подходе документ-классом будет прелоадер. Хотя согласен — момент получается спорным. You win
|
16.02.2011 18:21 | |
Интересно. Надо на досуге поковыряться, спасибо)
|
16.02.2011 20:36 | |
alatar, ну прелоадера уже нигде нет в момент создания BaseController.
|
Последние записи от Psycho Tiger
- Тонкости и трюки ActionScript`а, которые... бесполезны (10.05.2011)
- Vkontakte: как пользоваться wall.post, нужен ли теперь wall.savePost? (05.03.2011)
- А пятый контер-страйк хорош. (19.01.2011)
- Пацаны, гоу Вконтакте? (21.12.2010)
- Давайте начистоту (18.12.2010)