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