MVC zostało omówione w tak wielu miejscach, więc nie powinno być tu wiele do powtórzenia. Zasadniczo chcesz, aby wykres obiektów, pomocniki i logika były zawarte w warstwie modelu. Widoki będą ekranami wypychanymi w celu wypełnienia dynamicznej części strony (i mogą zawierać niewielką ilość logiki i pomocników). I kontroler, który jest lekką implementacją służącą do obsługi ekranów w oparciu o to, co było dostępne z grafów obiektowych, pomocników i logiki.
Model
To powinno być miejsce, w którym znajduje się mięso aplikacji. Może być podzielony na warstwy usługi, warstwy logiki i warstwy jednostek. Co to oznacza dla twojego przykładu?
Warstwa jednostki
Powinno to zawierać definicje modeli gry i zachowań wewnętrznych. Na przykład, jeśli masz grę dla trałowca, to tutaj znajdowałyby się definicje planszy i kwadratu oraz zmiany ich stanu wewnętrznego.
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;
};
Tak więc MineTile pozna swój stan wewnętrzny, na przykład, czy pokazuje lub został zbadany ( this.pristine
), jeśli był to jeden z kafelków, który ma kopalnię ( this.hasMine
), ale nie określi, czy miała mieć kopalnię. To zależy od warstwy logicznej. (Aby przejść dalej do OOP, MineTile może dziedziczyć z ogólnej płytki).
Warstwa logiczna
Powinno to obejmować złożone sposoby interakcji aplikacji ze zmieniającymi się trybami, utrzymywaniem stanu itp. W tym miejscu wzorzec mediatora zostałby wdrożony w celu utrzymania stanu obecnej gry. To byłaby logika gry, która określałaby na przykład, co się dzieje podczas gry, lub ustawiała, które MineTiles będą miały kopalnię. Wykonuje wywołania w warstwie Entity, aby uzyskać instancyjne poziomy na podstawie logicznie określonych parametrów.
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];
}
};
Warstwa usługowa
Będzie to miejsce, do którego kontroler ma dostęp. Będzie miał dostęp do warstwy logicznej do budowania gier. W warstwie serwisowej można wykonać połączenie wysokiego poziomu w celu odzyskania w pełni utworzonej gry lub zmodyfikowanego stanu gry.
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();
}
Kontroler
Kontrolery powinny być lekkie, w zasadzie to właśnie to, co klient jest narażony na model. Będzie wielu kontrolerów, więc ich struktura stanie się ważna. Wywołania funkcji kontrolera będą tym, co wywołają wywołania javascript na podstawie zdarzeń interfejsu użytkownika. Powinny one ujawnić zachowania dostępne w warstwie usługi, a następnie wypełnić lub w tym przypadku zmodyfikować widoki dla klienta.
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());
};
Widok
Widoki powinny być uporządkowane względem zachowań kontrolera. Prawdopodobnie będą one najbardziej intensywną częścią twojej aplikacji, ponieważ dotyczy ona akwizycji.
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();
}
Teraz masz całą konfigurację MVC dla tej jednej gry. A przynajmniej przykład z nagimi kośćmi, wypisywanie całej gry byłoby nadmierne.
Gdy to wszystko zostanie zrobione, gdzieś musi istnieć globalny zasięg dla aplikacji. Utrzyma to żywotność bieżącego kontrolera, który jest bramą do wszystkich stosów MVC w tym scenariuszu.
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
Używanie wzorców MVC jest bardzo wydajne, ale nie martw się zbytnio o przestrzeganie każdego z nich. Ostatecznie to gra sprawi, że aplikacja odniesie sukces :)
Do rozważenia: Nie pozwól, aby astronauci z architektury przestraszyli cię przez Joela Spolsky'ego