MVC puede definirse como: "Un patrón de arquitectura para programación". Estas siglas quieren decir: model/view/contoller ó modelo/vista/controlador.
En esencia lo que este concepto trata de hacer es tener una mejor separación en la estructura de las aplicaciones que uno suele programar. En español humano quiere crear diferentes capas para:
- elementos gráficos
- elementos lógicos
- almacenamiento y extracción de valores
Desde mi punto de vista MVC es una especia que ayuda a cocinar nuestras recetas de programación. Por si sola no sabe bien y no sirve de nada, pero mezclada con nuestros otros ingredientes se crean grandes recetas.
Ahora la parte cruda del tema: El MVC de fabrica es engorroso, tedioso y pesado de asimilar. Para llegar a configurar algo útil es necesario hacer pasos extras y que no les encontré sentido. Hay capas intermedias que no les vi razón de existir. Debo admitirlo yo no programo todavía aplicaciones ni remotamente parecidas a sistemas operativos, programas utilitarios, aplicaciones de servidores. Mi área de desempeño es mayormente publicidad, entretenimiento casual y sitios divertidos. El paquete MVC de fabrica resulta pesado y demasiado para aplicarlo en proyectos así. En la oficina se llego al consenso de que era mejor usar una versión reducida y propia de la "idea" detrás de MVC. Según lo que investigue esta idea la han aplicado otras personas. El orden que utilizan es ligeramente diferente pero el resultado siempre es el mismo. Mantener una separación estable y robusta de las capas que queremos identificar MODELO / VISTA / CONTROLADOR.
Trate de crear una versión reducida como la que utilizo en el entorno de producción y trataré de explicar el proceso para usarla y hacer notar algunas peculiaridades y utilidades de MVC.
Una peculiaridad de MVC es que el concepto se puede aplicar a diferentes lenguajes. Esto significa que el concepto se puede usar en JavaScript, PHP, AS3. Existen deferentes paquetes creados para cada lenguaje, pero el paquete que cree fue hecho pensando en AS3.Como es de esperar, el ejemplo basico de MVC para flash es bastante estable. Pero como mencione es demasiado para pequeños proyectos. Debido a esto trate de encontrar alternativas más reducidas, pero no tuve mucha suerte. Existen opciones, pero al final me pareció mejor crear una por mi cuenta, ya que mi interes era crear una que fuera extendible, o sea usar una clase base y las que cree en el camino extiendan esta clase base. Esta es una lista de muy buenos ejemplos que me ayudaron a entender MVC y a crear el paquete reducido.
- http://www.florian-spier.de/blog/basic-model-view-controller-mvc-example-as3/
- http://dev.tutsplus.com/tutorials/understanding-the-puremvc-open-source-framework--active-627
- http://www.swinburne.edu.au/design/tutorials/P-flash/T-The-Model-View-Controller-Design-Pattern-in-Actionscript-3/ID-144/
- http://www.ndid.se/2010/12/as3-mvc-example-and-analogy-%E2%80%93-revised/
- http://www.actionscript.org/forums/showthread.php3?t=145016
- https://code.google.com/p/as3-simplemvc/
- http://www.codingcolor.com/as3/as3-simple-mvc/
En el modelo MVC casi siempre encontraras una pareja de controlador / vista, pero el modelo no siempre forma el trio. Un modelo puede ser usado y accesado por diferentes vistas.Esta es la estructura que queremos crear. Es un pequeño proyecto con pocos elementos para ayudar a entender el formato:
Lo que me funciona para iniciar a crear el proyecto es tener una imagen visual de lo que deseamos lograr, asi que veamos una imagen del resultado final:
En este caso los graficos que tenemos son:
- la ventana blanca sobre la que se colocaran las estrellas y dentro de ella el botón "pop"
- los elementos que se colocan sobre la ventana al apretar el botón "pop"
| Gráfico | Model | View | Controller |
|---|---|---|---|
| PopUpTrigger/clase principal | StageModel | StageController | |
| ventana | WindowModel | WindowView | WindowController |
| pop up | PopUpModel | PopUpView | PopUpController |
package {
import com.project.controller.StageController;
import com.project.model.StageModel;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width="500",height="500",frameRate="30",backgroundColor="#D5EAFF")]
public class PopUpTrigger extends Sprite {
private var _stageModel:StageModel;
private var _stageController:StageController;
public function PopUpTrigger():void {
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
_stageModel = new StageModel();
_stageController = new StageController(_stageModel, this);
}
}
}
StageModel se crea, pero probablemente no coloquemos ninguna variable dentro de ella. Pero si la aplicación crece es posible que más adelante si sea necesario. StageController es donde se continua la creación de los otros elementos _windowModel, _windowView y _windowController:
package com.project.controller {
import com.bengalamedialab.mvc.Controller;
import com.bengalamedialab.mvc.View;
import com.project.model.StageModel;
import com.project.model.WindowModel;
import com.project.view.WindowView;
import flash.display.MovieClip;
import flash.events.Event;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class StageController extends Controller {
public static const NAME:String = "StageController";
private var _windowModel:WindowModel
private var _windowView:WindowView;
private var _windowController:WindowController;
private var _targetAll:Object;
public function StageController(model:StageModel, viewcomponent:Object) {
super(model, viewcomponent, NAME);
_targetAll = viewcomponent;
_windowModel = new WindowModel();
_windowView = new WindowView(_windowModel);
_windowController = new WindowController(_windowModel, _windowView);
_targetAll.addChild( _windowView );
}
}
}
package com.project.model {
import com.bengalamedialab.mvc.Model;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class StageModel extends Model {
public static const NAME:String = 'StageModel';
public function StageModel() {
super(NAME);
}
}
}
Cada una de las capas (modelo /vista /controlador ) utiliza una constante llamada NAME. que a veces es modificada por las nuevas clases. Se dice que es buena práctica tener esta constante, ya que ayuda a identificar cada capa.El patrón de creación es muy similar de aquí en adelante, como es un ejemplo sencillo no hay muchas variantes entre las capas. La capa en donde empieza la parte divertida es en WindowController, en un momento veremos porque.
package com.project.model {
import com.bengalamedialab.mvc.Model;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class WindowModel extends Model {
public static const NAME:String = 'WindowModel';
private var _windowModelName:String;
public function WindowModel(modelName:String = null) {
_windowModelName = (modelName != null) ? modelName : NAME;
super( _windowModelName);
}
}
}
Podemos decir que WindowView es nuestra capa gráfica, en ella desplegamos los gráficos que componen la ventana.
package com.project.view {
import com.bengalamedialab.mvc.View;
import com.project.model.WindowModel;
import flash.display.MovieClip;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class WindowView extends View {
public static var NAME:String = "WindowView";
public static var SHOW:String = "Show";
public static var HIDE:String = "Hide";
private var _windowViewModel:WindowModel;
private var _windowViewName:String;
private var _windowViewLayout:MovieClip;
public function WindowView(model:WindowModel, viewName:String=null) {
_windowViewModel = model;
_windowViewName = (viewName != null) ? viewName : NAME;
super(_windowViewModel, _windowViewName);
init();
}
private function init():void {
_windowViewLayout = new WindowViewLayout();
addChild ( _windowViewLayout );
}
public function get windowViewLayout():MovieClip {
return _windowViewLayout;
}
}
}
WindowController es en donde tiene lugar la parte lógica de nuestra aplicación, la logica en StageController se limita a la creacion de las capas para el elemento window, pero WindowController realizamos algo un poco más completo. Dependiendo del evento click en el botón "pop", WindowController creará el correspondiente PopUpModel - PopUpView - PopUpController
package com.project.controller {
import com.bengalamedialab.mvc.Controller;
import com.project.model.PopUpModel;
import com.project.model.WindowModel;
import com.project.view.PopUpView;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class WindowController extends Controller {
public static const NAME:String = "WindowController";
private var _windowControllerModel:WindowModel;
private var _windowControllerViewComp:Object;
protected var _windowControllerName:String;
private var _windowViewLayoutRef:Object;
private var _popUpModel:PopUpModel;
private var _popUpView:PopUpView;
private var _popUpController:PopUpController;
public function WindowController(model:WindowModel, viewcomponent:Object=null, controllerName:String=null) {
_windowControllerModel = model;
_windowControllerViewComp = viewcomponent;
_windowControllerName = (controllerName != null) ? controllerName:NAME;
super(_windowControllerModel, _windowControllerViewComp, _windowControllerName);
}
override public function onRegister( ):void {
_windowViewLayoutRef = _windowControllerViewComp;
makeButton(_windowViewLayoutRef.windowViewLayout.popBtn, clickFunction );
}
private function clickFunction(e:MouseEvent):void {
_popUpModel = new PopUpModel();
_popUpView = new PopUpView(_popUpModel);
_popUpController = new PopUpController(_popUpModel, _popUpView);
_popUpView.x = ( randomNumber(33.50, 433) );
_popUpView.y = ( randomNumber(26.05, 447.95) );
_windowControllerViewComp.addChild (_popUpView);
}
private function randomNumber(low:Number = 0, high:Number = 1):Number {
return Math.floor(Math.random() * (1 + high - low)) + low;
}
private function makeButton(which_mc:MovieClip, clickFunction:Function=null, overFunction:Function=null, outFunction:Function=null ):void {
which_mc.buttonMode = true;
which_mc.useHandCursor = true;
which_mc.mouseChildren = false;
if ( clickFunction != null ) {
which_mc.addEventListener(MouseEvent.CLICK, clickFunction);
}
if ( overFunction != null ) {
which_mc.addEventListener(MouseEvent.MOUSE_OVER, overFunction);
}
if ( outFunction != null ) {
which_mc.addEventListener(MouseEvent.MOUSE_OUT, outFunction);
}
}
}
}
Estas son las clases correspondientes para el pop up.
package com.project.model {
import com.bengalamedialab.mvc.Model;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class PopUpModel extends Model {
public static const NAME:String = 'PopUpModel';
private var _popUpModelName:String;
public function PopUpModel(modelName:String = null) {
_popUpModelName = (modelName != null) ? modelName : NAME;
super ( _popUpModelName );
}
}
}
package com.project.view {
import com.bengalamedialab.mvc.View;
import com.project.model.PopUpModel;
import flash.display.MovieClip;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class PopUpView extends View {
public static var NAME:String = "PopUpView";
public static var SHOW:String = "Show";
public static var HIDE:String = "Hide";
private var _popUpViewModel:PopUpModel;
private var _popUpViewName:String;
private var _popUpViewLayout:MovieClip;
public function PopUpView(model:PopUpModel, viewName:String=null) {
_popUpViewModel = model;
_popUpViewName = (viewName != null) ? viewName : NAME;
super(_popUpViewModel, _popUpViewName);
init();
}
private function init():void {
_popUpViewLayout = new PopUpLayout();
addChild ( _popUpViewLayout );
}
}
}
package com.project.controller {
import com.bengalamedialab.mvc.Controller;
import com.project.model.PopUpModel;
/**
* ...
* @author Marcos Diaz / http://www.blogdev.bengalamedialab.com/
*/
public class PopUpController extends Controller {
public static const NAME:String = "PopUpController";
private var _popUpControllerModel:PopUpModel;
private var _popUpControllerViewComp:Object;
protected var _popUpControllerName:String;
public function PopUpController(model:PopUpModel, viewcomponent:Object=null, controllerName:String=null) {
_popUpControllerModel = model;
_popUpControllerViewComp = viewcomponent;
_popUpControllerName = (controllerName != null) ? controllerName:NAME;
super(_popUpControllerModel, _popUpControllerViewComp, _popUpControllerName);
}
}
}
Estas son las clases que crean nuestro pequeño projecto, como ven las capas nuevas que creamos "extienden" las clases principales de model, view y controller. En esta versión el orden de creación es:- Model
- View
- Controller
Cree un repositorio para esta mini versión de MVC en github, hay un ejemplo incluido:MVC_micro