Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Psycho Tiger

Рейтинг: 4.79. Голосов: 19.

MVC, часть 1: про дубовый стол и сиськи

Запись от Psycho Tiger размещена 30.10.2010 в 02:36
Обновил(-а) Psycho Tiger 14.12.2010 в 18:10

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

...и вот появился на свет гений. Истинный Разработчик. Луч света падает на него, а клавиатура сокрушается под ударами его пальцев... Сейчас он сядет и сделает шедевр, в ритмах Вивальди извлекая звуки от щелчков мышки и ритмичных ударов о клавиатуре...

Думайте это о Вас? Обо мне? Пфф... Это Вам не сага о Горце. Тут придется включить свою голову. Ну или хотя бы сделать вид. Как бы то ни было - купите виски и откиньтесь на спинку кожаного кресла, положив свои ноги в ботинках из крокодиловой кожи на стол из столетнего дуба. Да-да, вы не ослышались. 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 нужно написать примерно следующее:
Код AS3:
new BaseController(this);
Мы просто создали этот контроллер и передали ссылку на себя - то есть на то место, где будет скапливаться вся графика. Нам даже не нужно сохранять ссылку на главный контроллер!
Код класса BaseController:
Код AS3:
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:
Код AS3:
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, в итоге нам нужно всего лишь визуализировать эти данные.
Код AS3:
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 секунды цвета. Что-ж, ничего сложного! Делаем:
Код AS3:
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();
		}
 
	}
 
}
И снова пытаемся представить контроллер как нечто "не связанное" с моделью и вьюшкой логически. Да-да, мы уже абстрагировались ещё перед написанием, но давайте не ленится. Если у вас получается, то он снова кажется простым - всего лишь меняем массив в каком-то объекте каждые 3 секунды. После этого пытаемся представить целостную картину, но это не всё. Мы не сделали интерактивность: стрелка (в нашем случае это круг) всегда будет указывать в одно место, а нам же необходимо чтобы стрелочка меняла положение на случайное по клику. Напоминаю, что это положение находится в модели.
Как это сделать? Наверное вам в голову лезет мысль - в контроллере отлавливать клик и менять модель. Нет, это не совсем правильно: это лепка программы "под конкретный вариант", а это как раз то, против чего создано MVC. Пользователь с чем взаимодействует? Только с вьюшкой. Он не может кликнуть по модели или по контроллеру - он кликает по экрану, что по сути является вьюшкой. Значит клик должна обрабатывать вьюшка. При этом это слишком связано, что именно по клику: сегодня по клику, завтра по нажатию клавиши, а послезавтра вообще по голосовой команде. Вьюшка должна сообщить о том, что надо бы поменять стрелочку - а уж отчего вьюшка так решила знает только она сама. В итоге во View дописываем функционал, что по клику диспатчим какое-то событие, которое слушает контроллер. Я опять буду использовать событие Event.CHANGE - поначалу могут возникнуть сложности - как же так, мы ведь его уже используем в модели - но это здорово развивает абстракцию. В реальных проектах типов событий у меня очень часто больше 30-40, тут, конечно, одним Event.CHANGE не обойтись и я делаю свой класс, расширяющий Event. Однако, какой случай - такой и инструмент, поэтому здесь Event.CHANGE.
Код AS3:
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;
		}
 
	}
 
}
Это полный код View. Просто дописали, что по клику происходит испускание события Event.CHANGE.
Теперь немного перепишем контроллер:
Код AS3:
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();
		}
 
	}
 
}
Тут тоже всё банально и просто - как только поймали событие Event.CHANGE от View - сразу же поменяли модель, которая в свою очередь побудила измениться отображению. Такой круговорот получится - View - Controller - Model - View. Забавно, правда?

Теперь пролистните вверх и посмотрите снова на картинку и вновь перечитайте часть, где я рассказывал про связи между M, V и C (Model, View и Controller). Теперь всё куда понятней, не правда ли?

Но и это не всё.
View не может изменять Model, как вы уже поняли, а у нас может. Как это исправить? Вью нужно передавать не саму модель, а только интерфейс, в которым "порезаны" все set методы. Например, в нашем случае такой:
Код AS3:
package  
{	
	public interface IReadableModel 
	{
		function get pointer():int; 
		function get colors:Array;
	}
 
}
(необходимо будет добавить геттер colors в Model)
Однако, мы всё равно можем изменить элемента массива colors. Можно, конечно, придумать метод getElementAt, который бы лез в массив и возвращал элемент на заданной позиции, тем самым запретив изменять элементы массива... однако запрет на изменение модели не имеет другого характера, кроме как "защиты от дурака/запарки", чтобы случайно вы сами не поменяли модель. Мне не было бы стыдно оставить интерфейс в виде, который я привёл чуть выше - если вы не доверяете себе или людям, с которыми работаете - пишите метод. Это - инструмент, и он должен подстраиваться под вас, а не вы под него. Не стесняйтесь смотреть на картинку - она сильно помогает первое время. Если она даже сейчас кажется вам сложной - не смотрите на неё, а читайте текст и рисуйте эту картинку у себя на бумажке. Не ленитесь, это действительно помогает.

На этом я закончу первую лекцию. В следующих мы будем ветвить контроллеры, делать иерархические модели, общаться с сервером. В общем, в следующие разы будет действительно интересно. Stay tuned

Вторая часть.
Всего комментариев 231

Комментарии

Старый 15.11.2010 14:31 Сайлас вне форума
Сайлас
Ну так еще раз:
есть в контроллере, условно говоря, _views:Vector.<IView>

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

А если делать так: добавляем некий функционал в контроллер, изменяем модель и по циклу вызываем во вью некую функцию, например, из интерфейса IView. Как бы смысл тот же, но получается даже побыстрее, потому что система событий или сигналов - это дополнительная прослойка. Но на самом деле разница в скорости незначительная, так что можно опустить.
Старый 15.11.2010 15:05 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Сайлас да ради бога ) удачи ) я вам говорю, что вам придётся править кучу классов, а вы меня спрашиваете "чем это плохо" )
Старый 15.11.2010 15:15 Сайлас вне форума
Сайлас
Ладно, не IView, а BaseView, тогда не надо править все вьюхи.

Я в личку вопрос лучше перенесу, а то слишком в принципе специфичный
Обновил(-а) Сайлас 15.11.2010 в 15:19
Старый 15.11.2010 16:34 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Цитата:
Сообщение от Сайлас
Ну я же и узнаю, почему придется править кучу классов.
Ну предположим, что у нас в основе не интерфейс, а класс ViewBase, в котором по умолчанию мы задаем поведение для новой функции.
А нужных вьюхах оверрайдим актуальным поведением. Аналогично лисенеру по набору действий же получается.
вы сами описали способ правки всех упоминаний модели. допустим у вам надо отобразить такие параметры персонажа как ХП и мана. обычно эта инфа отображается в 3-4х местах:
1. левый верхний угол с аватарой персонажа.
2. над самим персонажаем.
3. в окне со статистикой.
4. в окошке выбора и т.д.
все эти вьюхи могут выглядеть по разному. если каждая вьюха просто подписана на модель, то проблем с обновлением никаких. но если у нас всё проходит через контроллер, нам придётся править как минимум 2 места для добавления:
1. изменение маны
2. изменение хп

параметров может быть сколько угодно много, так же как и вьюх.

это мы рассмотрели параметры нашего персонажа, а если нам надо отображать ХП ещё 100 персонажей? в общем представьте сколько вам придётся написать кода только по сбору этих чудесных массивов с ViewBase, которые могут быть появится где угодно, и по неизвестным событиям.

а в случае с моделью и вьюхой, у нас нет таких проблем. вьюха сама подписалась на события конкретной модели, и ей совершенно побоку на контроллер, и точно так же побоку на то, где она расположена.
Старый 15.11.2010 19:49 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Цитата:
Ладно, не IView, а BaseView, тогда не надо править все вьюхи.
К мане и здоровью добавились деньги. Заводить там updateMoney, после чего тащить это во все вьюхи?
Старый 15.11.2010 21:51 Сайлас вне форума
Сайлас
Ну так не обязательно же имплементить во все вьюхи. Завел в базовом классе апдейтмани пустую, а где надо заоверрайдил.
А данные каждая вью получила из модели сама которые хочет.
Ну в принципе, я понял, что дело в красоте. Практически результат один
Старый 15.11.2010 22:52 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Результат в геморрое.
Вот есть у Вас вьюха. Она просто красиво отображает баланс игрока. А баланс хранится в общей моделе игрока, где ещё куча параметров. Имплементить их все? Супер!

Сделать отдельный интерфейс? Тогда только как суперинтерфейс для остального, чему тоже требуется баланс. А если таких штук 30? 30 интерфейсов + жуткий геморой от того что в бедном контроллере отдуши функционала уходит только на обновление вьюх. Да, поздравляю. Вы сэкономили на событиях.
Старый 16.11.2010 02:03 dimarik вне форума
dimarik
 
Аватар для dimarik
По-моему - это разгром )
Старый 16.11.2010 02:45 Сайлас вне форума
Сайлас
Цитата:
Результат в геморрое.
Вот есть у Вас вьюха. Она просто красиво отображает баланс игрока. А баланс хранится в общей моделе игрока, где ещё куча параметров. Имплементить их все? Супер!

Сделать отдельный интерфейс? Тогда только как суперинтерфейс для остального, чему тоже требуется баланс. А если таких штук 30? 30 интерфейсов + жуткий геморой от того что в бедном контроллере отдуши функционала уходит только на обновление вьюх. Да, поздравляю. Вы сэкономили на событиях.
Да я не экономлю, я передаю событиями. Просто я как-то задался таким вопросом на фоне и наткнулся на топик и решил выложить вопрос.
Задача, наверное, не всегда такая многосторонне сложная)
Если с замыканиями, с давнишним спором с Бладом я не согласился, то здесь лишь был вопрос у коммьюнити
Старый 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:11 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Котяра, честно говоря никогда не возлагал такую задачу на контроллер. у меня подобным пересчётом занималась всегда модель ( как у тебя написано ), а потом в методе рендера вьюхи происходил опрос модели. просто, на мой взгляд, такая архитектура не перестаёт быть мвц. связи сохраняются ровно такие же как и были. а смахивает на теорию нирта, в которой он мне доказывал, что мвц без событий не мвц.
Старый 16.11.2010 15:22 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Цитата:
Котяра, честно говоря никогда не возлагал такую задачу на контроллер. у меня подобным пересчётом занималась всегда модель ( как у тебя написано ), а потом в методе рендера вьюхи происходил опрос модели.
То есть вьюха вызывает опрашивает модель не по нужде, а через интервал времени?

Ну не знаю. Мне ближе периодически подстёгивать вью из контроллера.
Старый 16.11.2010 15:26 Сайлас вне форума
Сайлас
А почему же вы мне тут так доказывали, что это ни в коем случае нельзя делать? Если все-таки в этом случае все делают
Старый 16.11.2010 15:50 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Подстёгивать где надо и полностью отказаться от событий это 2 совсем разные вещи. Подстёгивать можно - в статье я это описал, но как правило бОльшая часть обновляется именно событиями.
Старый 16.11.2010 15:52 Сайлас вне форума
Сайлас
А, что я сформулировал так, что почему бы везде не отказаться от событий? Ну если так, то прошу прощения за неправильную формулировку
Старый 16.11.2010 19:38 Котяра вне форума
Котяра
 
Аватар для Котяра
Вот в MVP тоже отказались от связки модель-событие-вид.



контроллер(презентер) там как раз таки и вызывает конкретные методы у видов по интерфейсу.
Но MVP всё равно считается имплементацией MVC.
Обновил(-а) Котяра 16.11.2010 в 19:48
Старый 16.11.2010 20:34 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
@Psycho Tiger
у меня контроллер не подстёгивает вид, ибо это путь Сайласа, в котором контроллеру слишком много надо знать о виде.
а вот опросы модели со стороны вида по таймеру происходят. причём запускаются они по событию, и по событию же прекращаются.
Старый 17.11.2010 01:58 i.o. вне форума
i.o.
 
Аватар для i.o.
У меня вот такой вопрос есть. С точки зрения архитектуры, 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 03:34 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
что я потерял нить беседы. ты описал логику, которая по праву должна находится в контроллере. логика ваще должна находится в контролере. модель - это состояние приложения. в модели может находится только логика изменения состояний. например: перемещение. тобишь изменение какого-то свойства с течением времени. логичнее, что бы модель обновляла состояние, но диспатча не производила, ибо затратно. не каждая модель имеет вьюху, поэтому диспатчи могут быть ваще холостыми.
Старый 17.11.2010 03:43 Котяра вне форума
Котяра
 
Аватар для Котяра
ну ладно.. потерял так потерял.. я сам уже всё потерял... спать надо больше)
Резюмирую: MVC в "классическом виде" не всегда приятно и применимо.. нужно всегда опираться на потребности..
Но разделение модели и вида - хорошая практика в любом случае.. куда примкнуть контроллер и нужен ли он - другое дело..
Старый 17.11.2010 04:06 i.o. вне форума
i.o.
 
Аватар для i.o.
Котяра, спасибо.
Цитата:
Иногда выдаю шаблон - говорю вот рисуйте как хотите, но называйте имена и классы как в шаблоне. Имён и строго определённых наименований линкованных спрайтов/клипов в либе в 99.9% достаточно. Даже никаких интерфейсов не надо. Ассеты, на то и ассеты.
Можешь немного поконкретнее? Не совсем понял про "либу".
Старый 17.11.2010 04:20 Котяра вне форума
Котяра
 
Аватар для Котяра
Имею в виду строго определённые имена классов для спрайтов/мувиков в библиотеке fla файла. Кроме того, иногда(очень редко), нужны строго определённые имена объектов на таймлайне или даже имена меток.
По сути шаблоном я задаю "интерфейс" только не программный, а на уровне - "не сделаешь так как надо - будешь за бесплатно переделывать"..
Щутка конечно.. чаще забесплатно приходится переделывать мне, т.к. объяснять что там не так - себе дороже.
Обновил(-а) Котяра 17.11.2010 в 04:31
Старый 17.11.2010 14:31 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
В моей практике не было чего то такого, где аниматоры-художники сами были способны сделать что-то кроме анимации и изображений, ну оно и понятно. Эмэвэцэ, шмэнвэцэ - оживлять картинку всё равно приходится программистам.
Старый 17.11.2010 14:47 Котяра вне форума
Котяра
 
Аватар для Котяра
В моей практике встречались 2 великолепных художника (один из них главный артист фрагории, другой - 3Д моделлер сферы2) успешно переквалифицировавшихся во flash-программистов и делающих свои игры. Обратное не встречал.
Старый 19.11.2010 14:49 formid вне форума
formid
Отличная статья! Уже год пытаюсь понять MVC и перейти на него но никак не получается (( Буду очень рад второй и третей части!
Старый 19.11.2010 16:08 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Вторая где то на треть ~ половину написана. Во второй расскажу только о приёмах, кто чем занимается, как обеспечить "общение" разных частей программ. Третью думаю сделать целиком из создания тестового проекта со всеми плюшками MVC, объясняя как и зачем, но опираясь на материал прошлых частей - чтобы не приходилось размусоливать элементарщину.
Старый 19.11.2010 21:30 andrew911 вне форума
andrew911
@Psycho Tiger

Супер!
Старый 14.12.2010 18:00 Bred45 вне форума
Bred45
Спасибо за статью.

Чтобы "стрелка" указывала каждый раз на разные квадрат, где поставить проверку? В контроллере?
Что-то вроде:

Код AS3:
private function onViewChange(event:Event):void {
		var curPointer:Number = _model.pointer;
		do {
			_model.pointer = Math.random() * 5;
		} while (_model.pointer==curPointer);
}
Или модель должна эта отслеживать?
Старый 14.12.2010 18:08 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Модель не должна иметь мозгов чтобы решать что-то. Если "указать на другой квадрат" — это решение (а я считаю это решением), то в контроллере.
Старый 13.02.2011 22:52 TanaTiX вне форума
TanaTiX
 
Аватар для TanaTiX
Маленький один такой вопросик.
Зачем нам
Код AS3:
new BaseController(this);
и
Код AS3:
public function BaseController(host:DisplayObjectContainer)
		{
			_host = host;
Как я понимаю контроллер всему голова, так почему документ-класс не создать сразу контроллером?
Старый 14.02.2011 01:02 alatar вне форума
alatar
 
Аватар для alatar
Цитата:
Как я понимаю контроллер всему голова, так почему документ-класс не создать сразу контроллером?
В реалиях флеша документ-класс все-таки вьюха.
Старый 14.02.2011 01:34 TanaTiX вне форума
TanaTiX
 
Аватар для TanaTiX
Т.е. в качестве аргументов контроллеру передается и модель и вьюха? Это связано только с тем что документ-класс является DisplayObject-ом? Т.е. нет смысла использовать контроллеру весь функционал данного класса. И я так понимаю, что помещение всей логики, как в статье, в некий контейнер излишне и избыточно?
Старый 14.02.2011 02:23 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
alatar, в моих реалия у меня документ-класс - не вьюха.
Старый 14.02.2011 14:01 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Document-class является тем, кто создаёт главный контроллер и параллельно является host`ом для главной вьюхи. Ну у меня )
Старый 14.02.2011 15:04 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Psycho Tiger, ну а у меня документ-класс - это контроллер, который создаёт модель и вьюху.
Старый 14.02.2011 15:21 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Тоже неплохо.
В своё время терзаний меня смущало, что контроллер будет наследовать от ДисплейОбджекта, это и послужило причиной моих текущих терзаний.
Старый 15.02.2011 01:43 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Psycho Tiger, мммм ... меня тоже. поэтому у меня он не наследуется от него, как я уже писал ранее.
Старый 15.02.2011 16:37 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Опа. У тебя получается скомпилировать приложение так, что DocumentClass не наследуется от Sprite?
Старый 15.02.2011 17:03 Jewelz вне форума
Jewelz
 
Аватар для Jewelz
это же блуд
Старый 16.02.2011 04:22 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Psycho Tiger, да =) именно так. ничего сложного.
Старый 16.02.2011 11:33 arkadattx вне форума
arkadattx
Цитата:
Psycho Tiger, да =) именно так. ничего сложного.
а можно немного поподробней?
Старый 16.02.2011 11:57 Tails вне форума
Tails
 
Аватар для Tails
Интересная статейка. А можно узнать, метод "замыкания" это как ?
Старый 16.02.2011 12:31 i.o. вне форума
i.o.
 
Аватар для i.o.
Цитата:
Сообщение от dimarik Посмотреть сообщение
Можно было более высокопарно "лямбда-замыкания", чего постеснялся? В чем тут неразбериха? Молоко стало вкуснее и шИколаднее, из-за того, что корова стала альпийской?
)))
Для простых смертных - вложенная функция.
Ну или http://ru.wikipedia.org/wiki/%D0%97%...D0%B8%D0%B5%29
Старый 16.02.2011 13:07 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
Код AS3:
[Frame( factoryClass="Factory" )]
public class BaseController {
}
Старый 16.02.2011 13:35 alatar вне форума
alatar
 
Аватар для alatar
BlooDHounD, ну это как бы не совсем document class, при таком подходе документ-классом будет прелоадер. Хотя согласен — момент получается спорным. You win
Старый 16.02.2011 18:21 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Интересно. Надо на досуге поковыряться, спасибо)
Старый 16.02.2011 20:36 BlooDHounD вне форума
BlooDHounD
 
Аватар для BlooDHounD
alatar, ну прелоадера уже нигде нет в момент создания BaseController.
 

 


Часовой пояс GMT +4, время: 13:31.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.