MVC a été couvert dans de nombreux endroits, il ne devrait donc pas y avoir beaucoup de réitérations ici. Essentiellement, vous souhaitez que votre graphique d'objet, vos assistants et votre logique soient contenus dans le niveau modèle. Les vues seront les écrans qui seront poussés pour remplir la partie dynamique de la page (et peuvent contenir une petite quantité de logique et d'aides). Et le contrôleur, qui est une implémentation légère pour servir les écrans en fonction de ce qui était disponible à partir des graphiques d'objets, des assistants et de la logique.
Modèle
Cela devrait être l'endroit où se trouve la viande de l'application. Il peut être hiérarchisé en une couche de service, une couche logique et une couche d'entité. Qu'est-ce que cela signifie pour votre exemple?
Couche d'entité
Cela devrait contenir les définitions des modèles et des comportements internes de votre jeu. Par exemple, si vous aviez un jeu pour dragueur de mines, ce serait là que les définitions de plateau et de carré étaient ainsi que la façon dont ils changent leur état interne.
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;
};
Ainsi, le MineTile connaîtra son état interne, comme s'il montre ou a été examiné ( this.pristine
), s'il s'agissait d'une des tuiles qui a une mine ( this.hasMine
) mais ne déterminera pas s'il était censé avoir une mine. Ce sera à la couche logique. (Pour aller encore plus loin dans la POO, MineTile pourrait hériter d'une tuile générique).
Couche logique
Cela devrait contenir les façons complexes dont l'application interagira avec les modes changeants, le maintien de l'état, etc. Ce serait donc là qu'un modèle de médiateur serait mis en œuvre afin de maintenir l'état du jeu actuel. Ce serait là que la logique du jeu résiderait pour déterminer ce qui se passe pendant un jeu, par exemple, ou pour configurer quels MineTiles auront une mine. Il effectuerait des appels dans la couche Entity pour obtenir des niveaux instanciés en fonction de paramètres déterminés logiquement.
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];
}
};
Couche de service
Ce sera là où le contrôleur a accès. Il aura accès à la couche logique pour construire les jeux. Un appel de haut niveau peut être effectué dans la couche de service afin de récupérer un jeu entièrement instancié ou un état de jeu modifié.
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();
}
Manette
Les contrôleurs doivent être légers, c'est essentiellement ce qui est exposé en tant que client au modèle. Il y aura de nombreux contrôleurs, leur structuration deviendra donc importante. Les appels de fonction du contrôleur seront ce que les appels javascript atteindront en fonction des événements de l'interface utilisateur. Ceux-ci doivent exposer les comportements disponibles dans la couche de service, puis remplir ou, dans ce cas, modifier les vues pour le client.
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());
};
Vue
Les vues doivent être organisées en fonction des comportements du contrôleur. Ils constitueront probablement la partie la plus intensive de votre application, car elle traite du canevas.
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();
}
Alors maintenant, vous avez toute votre configuration MVC pour ce jeu. Ou du moins, un exemple à nu, écrire tout le jeu aurait été excessif.
Une fois que tout cela est fait, il devra y avoir une portée globale pour l'application quelque part. Cela conservera la durée de vie de votre contrôleur actuel, qui est la passerelle vers l'ensemble de la pile MVC dans ce scénario.
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
L'utilisation de modèles MVC est très puissante, mais ne vous inquiétez pas trop d'adhérer à toutes leurs nuances. Au final, c'est l'expérience de jeu qui déterminera si l'application est un succès :)
À considérer: Ne laissez pas les astronautes de l'architecture vous effrayer par Joel Spolsky