MVC se ha cubierto en tantos lugares, por lo que no debería haber mucho para repetir aquí. Esencialmente, desea que su gráfico de objetos, ayudantes y lógica estén contenidos en el nivel del modelo. Las vistas serán las pantallas que se expulsan para llenar la parte dinámica de la página (y pueden contener una pequeña cantidad de lógica y ayudantes). Y el controlador, que es una implementación ligera para servir las pantallas en función de lo que estaba disponible en los gráficos de objetos, ayudantes y lógica.
Modelo
Esto debería ser donde se sienta la carne de la aplicación. Se puede agrupar en una capa de servicio, una capa lógica y una capa de entidad. ¿Qué significa esto para tu ejemplo?
Capa de entidad
Esto debería contener las definiciones de los modelos y comportamientos internos de tu juego. Por ejemplo, si tuvieras un juego para buscaminas, aquí sería donde se encontraban las definiciones de tablero y cuadrado junto con cómo cambian su estado interno.
function Location(x,y){
this.x = x;
this.y = y;
}
function MineTile(x,y){
this.flagged = false;
this.hasMine = false;
this.pristine = true;
this.location = new Location(x,y);
}
MineTile.prototype.expose = function(){
if( this.hasMine ) return false;
this.pristine = false;
return this.location;
};
Por lo tanto, MineTile sabrá su estado interno, como si se está mostrando o si se examinó ( this.pristine
), si era uno de los mosaicos que tiene una mina ( this.hasMine
), pero no determinará si se supone que debe tener una mina. Eso dependerá de la capa lógica. (Para ir aún más lejos en OOP, MineTile podría heredar de un mosaico genérico).
Capa lógica
Esto debería albergar las formas complejas en que la aplicación interactuará con los modos cambiantes, el estado de mantenimiento, etc. Así que aquí sería donde se implementaría un patrón de mediador para mantener el estado del juego actual. Aquí sería donde residía la lógica del juego para determinar qué sucede durante un juego terminado, por ejemplo, o para configurar qué MineTiles tendrá una mina. Haría llamadas a la capa Entity para obtener niveles instanciados basados en parámetros determinados lógicamente.
var MineSweeperLogic = {
construct: function(x,y,difficulty){
var mineSet = [];
var bombs = 7;
if( difficulty === "expert" ) bombs = 15;
for( var i = 0; i < x; i++ ){
for( var j = 0; i j < y; j++ ){
var mineTile = new MineTile(i,j);
mineTile.hasMine = bombs-- > 0;
mineSet.push(mineTile);
}
}
return mineSet;
},
mineAt: function(x,y,mineSet){
for( var i = 0; i < mineSet.length; i++ )
if( mineSet[i].x === x && mineSet[i].y === y ) return mineSet[i];
}
};
Capa de servicio
Aquí será donde el controlador tiene acceso. Tendrá acceso a la capa lógica para construir los juegos. Se puede realizar una llamada de alto nivel en la capa de servicio para recuperar un juego completamente instanciado o un estado de juego modificado.
function MineSweeper(x,y,difficulty){
this.x = x;
thix.y = y;
this.difficulty = difficulty;
this.mineSet = MineSweeperLogic.construct(x,y,difficulty);
}
MineSweeper.prototype.expose = function(x,y){
return MineSweeperLogic.mineAt(x,y,this.mineSet).expose();
}
Controlador
Los controladores deben ser ligeros, esencialmente esto es lo que está expuesto como cliente al modelo. Habrá muchos controladores, por lo que estructurarlos será importante. Las llamadas a la función del controlador serán lo que las llamadas de JavaScript golpean según los eventos de la interfaz de usuario. Estos deben exponer los comportamientos disponibles en la capa de servicio y luego completar o, en este caso, modificar las vistas para el cliente.
function MineSweeperController(ctx){
var this.context = ctx;
}
MineSweeperController.prototype.Start = function(x,y,difficulty){
this.game = new MineSweeper(x,y,difficulty);
this.view = new MineSweeperGameView(this.context,this.game.x,this.game.y,this.game.mineSet);
this.view.Update();
};
MineSweeperController.prototype.Select = function(x,y){
var result = this.game.expose(x,y);
if( result === false ) this.GameOver();
this.view.Select(result);
};
MineSweeperController.prototype.GameOver = function(){
this.view.Summary(this.game.FinalScore());
};
Ver
Las vistas deben organizarse en relación con los comportamientos del controlador. Probablemente serán la parte más intensiva de su aplicación, ya que trata con el lienzo.
function MineSweeperGameView(ctx,x,y,mineSet){
this.x = x;
this.y = y;
this.mineSet = mineSet;
this.context = ctx;
}
MineSweeperGameView.prototype.Update = function(){
//todo: heavy canvas modification
for(var mine in this.mineSet){}
this.context.fill();
}
Así que ahora tienes toda tu configuración de MVC para este juego. O al menos, un ejemplo básico, escribir todo el juego hubiera sido excesivo.
Una vez hecho todo esto, será necesario que exista un alcance global para la aplicación en algún lugar. Esto mantendrá la vida útil de su controlador actual, que es la puerta de entrada a toda la pila MVC en este escenario.
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
El uso de patrones MVC es muy poderoso, pero no se preocupe demasiado por adherirse a cada matiz de ellos. Al final, es la experiencia del juego la que determinará si la aplicación es exitosa :)
Para consideración: no dejes que los astronautas de la arquitectura te asusten por Joel Spolsky