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

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

Оценить эту запись

Простой MVC фрэймворк

Запись от Котяра размещена 31.03.2009 в 00:02
Обновил(-а) Котяра 25.02.2010 в 09:55 (объединение)

Понадобился мне вдруг MVC фрэймворк.
Поискал я в интернетах, нашел разных там "корнХулио" (с) Beaves и pureMVC, и понял что "чо-то мне как-то не так" (с) Бочарик
В общем решил написать свой мега простой фрэймворк))
Прошу любить и жаловать. А особенно замечания давать.

Итак :Часть первая. Цель.Структура фрэймворка. Интерфейсы
Начнем со структуры фрэймворка, и хотя изначально, было чуток по другому, в итоге мой проект стал выглядеть так:




Собственно папочка simpleMVC и есть мега фрэймворк.
Задачи фрэймворка: создать базовые классы и интерфейсы для реализации MVC.
Я начал с Интерфейса модели:
класс simpleMVC.models.IModel

Модель сама не является данными, для этого у нее есть свойство data. также модель должна диспатчить события, поэтому наследуем интерфейс IEventDispatcher
Код AS3:
package simpleMVC.models
{
	import flash.events.IEventDispatcher;
 
	/**
	 * ИНтерфес представления модели
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru 
	 */
	public interface IModel extends IEventDispatcher
	{
		function get data():IModelData;
		function set data(__data:IModelData):void;
		function update():void;
 
	}
 
}
Далее реализация базовой модели:
класс simpleMVC.models.BaseModel
Код AS3:
package simpleMVC.models 
{
	import flash.events.EventDispatcher;
 
	/**
	 * Базовый класс модели
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class BaseModel extends EventDispatcher implements IModel 
	{
		/**
		 * Данные модели
		 */
		private var _data:IModelData;
 
		public function BaseModel() 
		{	
		}
 
		public function get data():IModelData
		{
		return _data;	
		}
 
		public function set data(__data:IModelData):void
		{
			_data = __data;
			update();
		}
 
		public function update():void
		{
			trace ("update " + data);
			dispatchEvent(new ModelEvent(ModelEvent.CHANGE));
		}
 
 
	}
 
}
То, что данные не типизированы, небольшое упущение, Данные должны быть представлены интерфейсом IModelData
Код AS3:
package simpleMVC.models 
{
	/**
	 * Пустой интерфейс данных модели.
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public interface IModelData 
	{		
	}	
}
Расширяем класс BaseModel. User реализует IModelData
Код AS3:
package 
{
	import remoteClasses.User;
	import simpleMVC.models.BaseModel 
	/**
	 * Модель данных о Юзере
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class UserModel extends BaseModel
	{
 
		private var _data:User;
	}
 
}
Пожалуйста. Расширяем и типизируем данные.

В реализации интерфейса модели возник ModelEvent.
Код AS3:
package simpleMVC.models 
{
	import flash.events.Event;
 
	/**
	 * Событие изменения модели
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class ModelEvent extends Event
	{
		public static const CHANGE:String = "change";
 
		public function ModelEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) 
		{
			super(type, bubbles, cancelable);	
		}
 
 
	}
 
}
Опять же, data не типизированна, плохо, но по другому не придумал пока. Здесь в качестве параметров события передаем данные модели. Можно использовать обычный Event.CHANGE, но пока пусть так.

Часть вторая: View

Интерфейс для View:
Код AS3:
package simpleMVC.views 
{
 
	import flash.events.IEventDispatcher;
	import simpleMVC.models.IModel;
 
	/**
	 * Интерфейс для всех вьюшек.
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public interface IView extends IEventDispatcher
	{
		function get model():IModel;
		function set model(_model:IModel):void;
		//function update():void;
	}
 
}
Простая реализация (для вьюшек не являющихся дисплэйобжэктами)
Код AS3:
package simpleMVC.views 
{
	//import flash.display.MovieClip;
	//import flash.display.Sprite;
	import flash.events.EventDispatcher;
	import simpleMVC.models.IModel;
	import simpleMVC.models.ModelEvent;
 
	/**
	 * Базовый класс моделей
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class BaseView extends EventDispatcher implements IView
	//public class BaseView extends Sprite implements IView
	//public class BaseView extends MovieClip implements IView
	{
		private var _model:IModel;
 
		/**
		 * Данные модели
 
		private var data:*;
                */
		/**
		 * Установка модели
		 */
		public function set model(__model:IModel):void
		{
			_model = __model;
			_model.removeEventListener(ModelEvent.CHANGE, update);
			_model.addEventListener(ModelEvent.CHANGE, update);
		}
 
		public function get model():IModel
		{
			return _model;
		}
 
		/**
		 * Обновление представления
		 * @param	e		Событие изменения модели
		 */
		private function update(e:ModelEvent):void 
		{
			var data:String = String(e.target.data);
			trace (" BaseView.update: "+data);
		}
 
 
		public function BaseView() 
		{
			super();
		}
 
	}
 
}
аналoгично для Sprite
Код AS3:
package simpleMVC.views 
{
	import flash.display.Sprite;
	import simpleMVC.models.IModel;
	import simpleMVC.models.ModelEvent;
 
	/**
	 * ...
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class BaseSpriteView extends Sprite implements IView
	{
		private var _model:IModel;
 
		/**
		 * Данные модели
 
		private var data:*;
		 */
 
		/**
		 * Установка модели
		 */
		public function set model(__model:IModel):void
		{
			_model = __model;
			_model.removeEventListener(ModelEvent.CHANGE, update);
			_model.addEventListener(ModelEvent.CHANGE, update);
		}
 
		public function get model():IModel
		{
			return _model;
		}
 
		/**
		 * Обновление представления
		 * @param	e		Событие изменения модели
		 */
		private function update(e:ModelEvent):void 
		{
			var data:String = String(e.target.data);
			trace (" BaseSpriteView.update: "+data);
		}
 
		public function BaseSpriteView() 
		{
 
		}
 
	}
 
}
В общем вьшки подписываются на изменение модели, берут данные модели и изменяются.

Часть третья. Контроллеры.

Самая наверное неоднозначная часть моего фрэймворка.
Контроллеры всего навсего сопоставляют пары MV и слушают V.
Интерфейс:
Код AS3:
package simpleMVC.controllers 
{
	import flash.events.IEventDispatcher;
	import simpleMVC.models.IModel;
	import simpleMVC.views.IView;
 
 
	/**
	 * Интерфейс контроллера
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public interface IController extends IEventDispatcher
	{
		function addModelViewer(__model:IModel, __view:IView):void;
		function changeModel(__model:IModel, __data:*):void;
		function addViewEventListener(__view:IView, e:String, handler:Function):void;
		function removeViewEventListener(__view:IView, e:String, handler:Function):void;
	}
 
}
Базовая реализация
Код AS3:
package simpleMVC.controllers 
{
	import flash.events.EventDispatcher;
	import simpleMVC.models.IModel;
	import simpleMVC.views.IView;
 
	/**
	 * Базовый класс контролллеров
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class BaseController extends EventDispatcher implements IController
	{
 
		public function BaseController() 
		{
		}
		/**
		 * Добавить вьювер к модели
		 * @param	__model			Модель
		 * @param	__view			Представление
		 */
		public function addModelViewer(__model:IModel, __view:IView):void
		{
			__view.model = __model;
		}
 
		/**
		 * Изменить данные модели
		 * @param	__model
		 * @param	__data
		 */
		public function changeModel(__model:IModel, __data:*):void
		{
			__model.data = __data;
		}
 
		/**
		 * Назначить слушатель представления
		 * @param	__view
		 * @param	e
		 * @param	handler
		 */
		public function addViewEventListener(__view:IView,e:String, handler:Function):void 
		{
		__view.addEventListener(e, handler);	
		}
 
		/**
		 * Удалить слушатель представления
		 * @param	__view
		 * @param	e
		 * @param	handler
		 */
		public function removeViewEventListener(__view:IView,e:String, handler:Function):void 
		{
		__view.removeEventListener(e, handler);	
		}		
 
	}	
 
}
Определим для начала пользовательский класс User, в формате которого будут представленны данные модели.Объект этого класса у меня приходит от BlaseDS, поэтому все сериализованные классы лежат в пакете remoteClasses, в данном случае это не важно:
Код AS3:
package remoteClasses 
{
 
	/**
	 * Класс данных модели Юзера. Объект класса передается с сервера в сериализованном виде
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru
	 */
	public class User 
	{
		public var id:int = 1;
		public var name:String = "peter";
 
		public function User(_id:int,_name:String) 
		{
			id = _id;
			name = _name;
		}
		public function toString():String
		{
			return ('[User id='+String(id)+' name="'+name+'"]');
		}
 
	}
 
}
А вот и тестовый класс:
Код AS3:
package 
{
	import simpleMVC.controllers.BaseController;
	import flash.display.Sprite;
	import flash.events.Event;
	import simpleMVC.models.ModelEvent;
	import simpleMVC.views.BaseView;
	import simpleMVC.views.BaseSpriteView;
	import remoteClasses.User;
 
	/**
	 * Тестирование MVC 
	 * класс фасада (бизнес контроллер)
	 * @author k0t0vich (c) 2009 b_konstantin@list.ru http://islandsworld.ru/
	 */
	public class Main extends Sprite 
	{
 
		public var userModel:UserModel;
		public var baseView:BaseView;
		public var baseSpriteView:BaseSpriteView;
		public var baseController:BaseController;
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			userModel = new UserModel();
			// простая вьюшка наследуется от EventDispatchera
			baseView = new BaseView();
			// вьюшка наследуемая от спрайта
			baseSpriteView = new BaseSpriteView();
			// базовый контроллер
			baseController = new BaseController();
 
			//Инициализация связки MV - назначение для вью модели
			baseController.addModelViewer(userModel, baseView);
			baseController.addModelViewer(userModel, baseSpriteView);
			// Изменение модели. Имитация поведения серверного контроллера
			baseController.changeModel(userModel, new User (12, "Bob"));
			// добавляем слушатель
			baseController.addViewEventListener(baseSpriteView, Event.ADDED_TO_STAGE, baseSpriteViewAddedtoStageListener);
 
			//добавляем сппрайтовую вьюшку на сцену
			addChild(baseSpriteView);
		}
		// команда назначенная контроллерам на событие спрайт-вьюшки
		private function baseSpriteViewAddedtoStageListener(e:Event):void
		{
			trace ("baseSpriteViewAddedtoStageListener: "+e);
		}
 
 
	}
 
}
Код AS3:
// в итоге получаем:
/*
update [User id=12 name="Bob"]
BaseView.update: [User id=12 name="Bob"]
BaseSpriteView.update: [User id=12 name="Bob"]
baseSpriteViewAddedtoStageListener: [Event type="addedToStage" bubbles=false cancelable=false eventPhase=2]
*/
Создаем две связки MV, меняем M видим как реагируют оба V, слушаем контроллером события V.
Что еще надо?

исходники mvc.rar:
Изображения
Тип файла: gif 11.gif (7.4 Кб, 2112 просмотров)
Вложения
Тип файла: rar mvc2.rar (16.6 Кб, 256 просмотров)
Размещено в ru.k0t0vich
Комментарии 7 Отправить другу ссылку на эту запись
Всего комментариев 7

Комментарии

Старый 31.03.2009 10:29 Котяра вне форума
Котяра
 
Аватар для Котяра
По первым отзывам, сразу поменял классы IModel, ModelEvent и добавил интерфейс IModelData.
Данные модели должны реализовывать интерфейс IModelData. ModelEvent не будет вещать data, т.к. к ней можно прийти через e.target.data, можно его вообще исключить и использовать стандартный Event.CHANGE, но пока оставлю.
новая версия в mvc2.rar
Старый 08.04.2009 18:32 strelok222 вне форума
strelok222
Спасибо за статью, я только недавно заинтересовался ооп, может быть мой вопрос покажется не очень умным, но я в упор не могу понять для чего применяются все эти интерфейсы?
Старый 09.04.2009 14:32 Котяра вне форума
Котяра
 
Аватар для Котяра
Скажу может не по-научному, но по-простому)
пример:
имеем основной класс который подгружает модули, для каждого модуля нужно вызвать метод, например, draw.
Определяем интерфейс IDrawable, в котором есть метод draw,
все модули должны реализовывать этот интерфейс, т.е. у них должен быть метод draw.
Загрузочный класс загружает модель и работает с ним как с объектом класса IDrawable - реальные классы модулей могут быть любые, главное чтоб реализовывали интерфейс.
Обновил(-а) Котяра 09.04.2009 в 14:35
Старый 07.04.2011 00:02 mayakwd вне форума
mayakwd
 
Аватар для mayakwd
Небольшое упущение во вьюшках нет проверок на отсутствие модели при removeEventListener
Старый 07.04.2011 00:17 Котяра вне форума
Котяра
 
Аватар для Котяра
Да тут много косяков.
Старый 13.03.2012 20:04 Tfp вне форума
Tfp
 
Аватар для Tfp
[Fault] exception, information=TypeError: Error #1034: Ошибка типа Coercion: невозможно преобразовать remoteClasses::User@c89bc9 в simpleMVC.models.IModelData.
Ошибка при компиляции, я так понимаю public class User должен implements IModelData
Старый 13.03.2012 20:16 Котяра вне форума
Котяра
 
Аватар для Котяра
Да.
Записи 3 года скоро будет.
Цитата:
Да тут много косяков.
 

 


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


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