MVC - Model-View-Controller Design Pattern

Objektorientering eksemplifisert ved å anvende MVC-mønsteret på et program som løser en andregradsligning.

Versjon: 02.06.14
© H-P Ulven

Design Pattern:

Hvis man ikke kjenner til slike mønstre, hjelper det ikke å bruke et godt objektorientert språk og vite hva abstraksjon, innkapsling, arv og polymorfi er.
God programmering krever i tillegg kjennskap til hvordan teoretikere og praktikere gjennom lang erfaring har funnet mønstre for programmering som leder til gode løsninger.

Anbefalt lesing:


Demo av applikasjon: 2Grad.swf

Design mønsteret Model-View-Controller separerer en applikasjon i tre deler (klasser):

Fordelene med denne oppdelingen er:

Ideelt skal kommunikasjonen mellom bruker og applikasjonens deler skje slik:

Vi forenkler det litt hvis vi ikke skal ha alt for kompliserte brukergrensesnitt:

Legg merke til at Viewer og Model ikke trenger å vite noen ting om hverandre,
dette er et viktig ideal i OOP; ha så få avhengigheter som mulig.

Med UML - diagrammer kan vårt andregradsprogram se slik ut:

Model Grad2_Loser.as (utdrag av kode):

Grad2_Loser.as:
package  {
	public class Grad2_Loser {
		public const INGEN_LOSNINGER: int = 0;
		public const EN_LOSNING: int = 1;
		public const TO_LOSNINGER: int = 2;
		public const KOMPLEKSE_LOSNINGER: int = 3;
		public const UENDELIG_MANGE_LOSNINGER: int = 4;
		private var a: Number = 0.0;
		...
		private var x1: Number = 0.0;
		...
		private var state: int = 0;
		public function setA(a: Number): void {
			this.a = a;
			beregn();
		}//setA()
		...
		public function getState():int {
			return state;
		}//getState();
		public function getX1(): Number{
			return x1;
		}//getX1()
		...
		private function beregn(): void {
			if(a==0){
				if(b==0){
					if(c==0){
						state = UENDELIG_MANGE_LOSNINGER;   
					}else{
						state = INGEN_LOSNINGER;
					}//if c
				}else{
					state = EN_LOSNING;
					x1 = -c/b;
				}//if b
			}else{
				d = b*b - 4*a*c;
				if(d>0){
					state = TO_LOSNINGER;
					x1 = (-b+Math.sqrt(d))/(2*a);
					x2 = (-b-Math.sqrt(d))/(2*a);
				}else if(d==0){
					state = EN_LOSNING;
					x1 = -b/(2*a);
				}else{
					state = KOMPLEKSE_LOSNINGER;
					r = -b/(2*a);
					im = Math.sqrt(-d)/(2*a);
				}//if d
			}//if a
		}//beregn()

	}//class Model
	
}//pacakage
Denne koden kan testes fra Flash, uten å lage grensesnitt, eksempelvis slik:
/*	Test av modellen i andregradsprogrammet     */
const FEILMARGIN: Number = 0.00001;
var m: Grad2_Loser = new Grad2_Loser();
// x^2 -3x +2=0 skal ha løsning 1 og 2:
	trace("1,-3,2:   1,2");
   m.setABC(1,-3,2);
   if( m.getState()==m.TO_LOSNINGER && Math.abs(m.getX1()-2)<FEILMARGIN &&  Math.abs(m.getX2()-1)<FEILMARGIN ) {
      trace("Ok!");
   }else{
      trace(" *** Test  feilet!!! ***");
   }//if

Viewer kode:

Ingen kode nødvendig, kobles opp fra Controller.
Unngår dermed at addEventListener-funksjoner blir utført i tide og utide ved avspilling av MovieClip.

Egenskapene fremgår av figuren:

Kan teste ut Viewer med litt enkel kode, som senere kan fjernes/kommenteres ut.:

/*   Kode for å teste grensesnitt: (Uten Controller og Model)
     Bare skriver ut verdier                                  */   
btnBeregn.addEventListener(MouseEvent.CLICK,beregnKlikket);
function beregnKlikket(evt: MouseEvent):void {
   var a: Number = Number(txtInputA.text);
   var b: Number = Number(txtInputB.text);
   var c: Number = Number(txtInputC.text);
   taResult.text =
	  "a:   " + a +"\n"+
	  "b:   " + b +"\n"+
	  "c:   " + c +"\n";
	
}//beregnKlikket()

Controller kode:

Conroller.as:

package  {
	public class Controller extends MovieClip {
		private var model: Grad2_Loser = new Grad2_Loser();		//Lager ny data-model

		public function Controller() {
			// Konstruktør setter opp applikasjonen:
			btnBeregn.addEventListener(MouseEvent.CLICK,beregn);	//btnBeregn i Viewer
		}//constructor
		//Lytterfunksjon:
		private function beregn(evt:Event): void {
			model.setA(Number(txtInputA.text));
			model.setB(Number(txtInputB.text));
			model.setC(Number(txtInputC.text));
			var status: int = model.getState();
			if(status==model.INGEN_LOSNINGER){
				taResult.text="Ingen løsninger!";
			}else if(status==model.EN_LOSNING) {
				taResult.text="En løsning: "+model.getX1();
			}else if(status==model.TO_LOSNINGER) {
				taResult.text="To reelle løsninger:\n"+
					    	   model.getX1()+"\n"+
							   model.getX2();
			}else if(status==model.KOMPLEKSE_LOSNINGER) {
				taResult.text="To komplekse løsninger:\n"+
							    model.getR() + " + i "+model.getIm()+"\n"+
							    model.getR() + " - i "+model.getIm();
			}else if(status==model.UENDELIG_MANGE_LOSNINGER) {
				taResult.text="Uendelig mange løsninger.";
			}else {
				taResult.text="Skal ikke skje, feil !!!";
			}//if status
		}//beregn()
		
	}// Class
	
}//Package