MVC wurde an so vielen Stellen behandelt, dass es hier nicht viel zu wiederholen geben sollte. Im Wesentlichen möchten Sie, dass Ihr Objektdiagramm, Ihre Helfer und Ihre Logik in der Modellebene enthalten sind. Die Ansichten sind die Bildschirme, die herausgedrückt werden, um den dynamischen Teil der Seite auszufüllen (und möglicherweise eine geringe Menge an Logik und Hilfsprogrammen enthalten). Und der Controller, der eine einfache Implementierung darstellt, um die Bildschirme basierend auf den verfügbaren Informationen aus den Objektdiagrammen, Hilfsprogrammen und der Logik bereitzustellen.
Modell
Hier sollte sich das Fleisch der Anwendung befinden. Es kann in eine Serviceschicht, eine Logikschicht und eine Entitätsschicht unterteilt werden. Was bedeutet das für Ihr Beispiel?
Entitätsebene
Dies sollte die Definitionen der Modelle und internen Verhaltensweisen Ihres Spiels enthalten. Wenn Sie beispielsweise ein Spiel für Minensuchboot hatten, befanden sich hier die Definitionen für Bretter und Quadrate sowie die Art und Weise, wie sie ihren internen Status ändern.
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;
};
Das MineTile kennt also seinen internen Zustand, z. B. ob es angezeigt wird oder untersucht wurde ( this.pristine
), wenn es eines der Kacheln war, das eine Mine hat ( this.hasMine
), aber nicht bestimmt, ob es eine Mine haben sollte. Das liegt an der Logikschicht. (Um noch weiter in OOP einzusteigen, könnte MineTile von einer generischen Kachel erben).
Logikebene
Dies sollte die komplexen Möglichkeiten enthalten, wie die Anwendung mit sich ändernden Modi, dem Beibehalten des Status usw. interagiert. Hier würde also ein Mediatormuster implementiert, um den Status des aktuellen Spiels beizubehalten. Hier befand sich die Spiellogik, um beispielsweise zu bestimmen, was während eines Spiels passiert, oder um festzulegen, welche MineTiles eine Mine haben sollen. Es würde Aufrufe in der Entitätsebene tätigen, um instanziierte Ebenen basierend auf logisch bestimmten Parametern zu erhalten.
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];
}
};
Serviceschicht
Hier hat der Controller Zugriff. Es wird Zugriff auf die Logikschicht zum Erstellen der Spiele haben. Ein Anruf auf hoher Ebene kann in die Serviceschicht getätigt werden, um ein vollständig instanziiertes Spiel oder einen modifizierten Spielstatus abzurufen.
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();
}
Regler
Controller sollten leicht sein, im Wesentlichen ist dies das, was als Client dem Modell ausgesetzt ist. Es wird viele Controller geben, daher wird die Strukturierung dieser Controller wichtig. Controller-Funktionsaufrufe sind das, was die Javascript-Aufrufe basierend auf UI-Ereignissen treffen. Diese sollten die in der Service-Schicht verfügbaren Verhaltensweisen offenlegen und dann die Ansichten für den Client füllen oder in diesem Fall ändern.
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());
};
Aussicht
Die Ansichten sollten relativ zum Verhalten des Controllers organisiert sein. Sie werden wahrscheinlich der intensivste Teil Ihrer Anwendung sein, da sie sich mit der Leinwand befasst.
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();
}
Jetzt haben Sie Ihr gesamtes MVC-Setup für dieses eine Spiel. Zumindest wäre es übertrieben gewesen, das ganze Spiel aufzuschreiben.
Sobald dies alles erledigt ist, muss irgendwo ein globaler Bereich für die Anwendung vorhanden sein. Dies hält die Lebensdauer Ihres aktuellen Controllers, der in diesem Szenario das Gateway zum gesamten MVC-Stack darstellt.
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
Die Verwendung von MVC-Mustern ist sehr leistungsfähig, aber Sie müssen sich nicht zu sehr darum kümmern, jede Nuance von ihnen einzuhalten. Am Ende ist es das Spielerlebnis, das entscheidet, ob die Anwendung erfolgreich ist :)
Zur Überlegung: Lassen Sie sich von Architekturastronauten nicht erschrecken von Joel Spolsky