MVC

Kali ini saya jelaskan ten­tang cara mem­buat ?struk­tur? MVC seder­hana untuk menulis kode yang main­tainable dengan memecah kode men­jadi beberapa bagian ber­dasarkan fung­sinya ( separation of con­cerns ). Saya ang­gap Anda sudah cukup meng­etahui dasar OOP dan memp­rak­tek­kan­nya, jadi saya nggak perlu men­jelaskan lagi apa itu class, instance, static, getter/setter, dsb. Saya juga nggak men­jelaskan apa itu cus­tom events dan bagaimana cara mem­buat & meng­gunakan­nya karena sudah per­nah saya jelaskan dalam artikel yang lain. Selain itu, Anda juga harus meng­erti cara men­definisikan & mem­buat cus­tom class untuk objek di library. Design Pat­terns Design pat­terns (DP) adalah sekum­pulan solusi generik untuk masalah-masalah yang umum ditemui oleh para developer dalam mem­buat aplikasi. Pat­tern ber­arti pola, artinya DP bukan ber­upa kode, tetapi ber­upa pan­duan yang implemen­tasinya ter­serah developer yang menggunakannya. Info yang lebih leng­kap bisa Anda baca di wikipedia. Model-View-Controller MVC adalah meta-pattern , artinya MVC bukan merupakan pat­tern yang ber­diri sen­diri tapi merupakan kum­pulan dari beberapa pat­tern & dalam buku Head First Design Pat­terns , MVC dimasukkan dalam kelom­pok compound-pattern. Pada prin­sip­nya, implemen­tasi MVC mem­bagi kode ke dalam 3 bagian yaitu: * Model seba­gai sum­ber data * View seba­gai rep­resen­tasi data dan user inter­face * Con­troller yang ber­fungsi seba­gai ?otak? atau business logic yang memp­roses user input dan meng?update Model dan View ( kalau diperlukan ). Apa keun­tungan MVC ? Sekilas MVC kelihatan­nya merepotkan karena kita harus menulis kode lebih banyak tapi keun­tungan­nya adalah kode kita lebih main­tainable karena kita bisa meng­ubah salah satu bagian tanpa harus meng­ubah bagian yang lain. Seba­gai con­toh, misal­nya kita mem­buat aplikasi A yang memp­roses data ber­for­mat XML namun kemudian kita diharuskan meng­gunakan data ber­for­mat JSON. Dalam kasus seperti ini, kita cukup meng­ubah Model tanpa harus meng­ubah bagian yang lain. MVC juga mem­per­mudah debug­ging karena kita bisa mem­per­kirakan bagian mana yang ber­masalah tanpa harus mem­bong­kar seluruh kode yang sudah kita buat. Jadi secara umum, keun­tungan MVC jauh lebih besar daripada kerepotan yang ditim­bul­kan­nya. Di sam­ping itu, kalo Anda nggak mau repot menulis berbaris-baris kode, mung­kin men­jadi program­mer bukan profesi yang cocok buat Anda & sebaik­nya Anda cari peker­jaan lain. ;-) MODEL ->> Model adalah bagian dimana data ber­ada. Untuk data yang ber­sifat global, kita bisa meng­im­plemen­tasikan Singleton pat­tern. Singleton class hanya bisa diin­stan­siasi satu kali selama aplikasi ber­jalan. Umum­nya, referensi ke instance dari Singleton diak­ses dengan memang­gil static method getInstance(). Kalo kita bekerja ber­sama developer lain, kita bisa men­cegah mereka menginstansiasi/membuat objek Singleton dengan meng­gunakan inner class yaitu class yang didefinisikan diluar pac­kage tetapi masih di dalam file yang sama. Model saya gunakan untuk menyimpan data ber­upa posisi kotak kuning. Pada saat data ber­ubah, Model akan men-dispatch Event.CHANGE yang didengarkan oleh MainView yang kemudian meng-update posisi kotak kuning. [js] pac­kage simplemvc.model { import flash.events.Event; import flash.events.EventDispatcher; import flash.geom.Point; /** * ? * @author Ang­gie Bratadinata */ public class Model extends EventDispatcher { public var heroHeight:Number = 0; public var heroWidth:Number = 0; //???????????????????? HERO VELOCITY public var vx:Number = 10; public var vy:Number = 10; //???????????????????? HERO POSITION private var _heroPos:Point = new Point(); public fun­ction get heroPos():Point { return _heroPos; } public fun­ction set heroPos(value:Point):void { if (value.x >= heroBounds.xMin && value.x <= heroBounds.xMax && value.y >= heroBounds.yMin && value.y <= heroBounds.yMax ) { _heroPos = value; dispatchEvent(new Event(Event.CHANGE)); } } public fun­ction moveDown():void { heroPos = new Point(heroPos.x, heroPos.y + vy); } public fun­ction moveUp():void { heroPos = new Point(heroPos.x, heroPos.y ? vy); } public fun­ction moveLeft():void { heroPos = new Point(heroPos.x ? vx, heroPos.y); } public fun­ction moveRight():void { heroPos = new Point(heroPos.x + vx, heroPos.y ); } //???????????????????? HERO BOUNDS private var _heroBounds:Object = {}; public fun­ction get heroBounds():Object { return _heroBounds; } public fun­ction set heroBounds(value:Object):void { //trace(value); _heroBounds.xMin = value.x; _heroBounds.xMax = value.x + value.width ? heroWidth; _heroBounds.yMin = value.y; _heroBounds.yMax = value.y + value.height ? heroHeight; } //???????????????????? INIT private static var _instance:Model; public fun­ction Model(enf:SingletonEnforcer) {} public static fun­ction getInstance():Model { if (_instance == null) _instance = new Model(new SingletonEn­for­cer()); return _instance; } } } //INNER CLASS class SingletonEnforcer { }; [/js] VIEW & VIEW EVENT ->> Pac­kage view ber­isi class yang ber­hubungan dengan library sym­bol yaitu MainView dan ArrowButton. MainView ber­tugas menyiarkan ViewEvent jika salah satu tom­bol navigasi diklik. Event ini ditang­kap oleh Controller yang kemudian meng-update Model. Selain itu MainView juga menam­pilkan posisi kotak kuning. Kenapa saya mem­buat ViewEvent dan nggak mem­buat Controller yang lang­sung men­dengarkan MouseEvent.CLICK yang disiarkan oleh tom­bol kon­trol ? Karena saya ingin meminimalkan coupling antara MainView dan Controller sehingga jika saya perlu meng­ubah View, selama event yang disiar­kan­nya tetap ber­tipe ViewEvent, saya nggak perlu meng­ubah Controller. [js] pac­kage simplemvc.view { import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.text.TextField; import simplemvc.events.ViewEvent; import simplemvc.model.Model; /** * ? * @author Ang­gie Bratadinata */ public class Main­View extends MovieClip { //timeline objects public var hero:MovieClip; public var board:MovieClip; public var leftBtn:ArrowButton; public var rightBtn:ArrowButton; public var upBtn:ArrowButton; public var downBtn:ArrowButton; public var positionTxt:TextField; private var _model:Model = Model.getInstance(); //???????????????????? INIT public fun­ction MainView() { initButtonListener(); _model.addEventListener(Event.CHANGE, modelListener); } private fun­ction initButtonListener():void { var i:int = 0; while (i < numChildren) { if (this.getChildAt(i) is ArrowBut­ton) { this.getChildAt(i).addEventListener(MouseEvent.CLICK, but­tonLis­tener); } i++; } } private fun­ction buttonListener(e:MouseEvent):void { switch(e.currentTarget) { case lef­tBtn: dispatchEvent(new ViewEvent(ViewEvent.MOVE_LEFT)); break; case rightBtn: dispatchEvent(new ViewEvent(ViewEvent.MOVE_RIGHT)); break; case upBtn: dispatchEvent(new ViewEvent(ViewEvent.MOVE_UP)); break; case downBtn: dispatchEvent(new ViewEvent(ViewEvent.MOVE_DOWN)); break; } } private fun­ction modelListener(e:Event):void { hero.x = _model.heroPos.x; hero.y = _model.heroPos.y; positionTxt.text = _model.heroPos.toString(); } } } [/js] [js] pac­kage simplemvc.events { import flash.events.Event; /** * ? * @author Ang­gie Bratadinata */ public class ViewEvent extends Event { public static const MOVE_LEFT:String = ?moveLeft?; public static const MOVE_RIGHT:String = ?moveRight?; public static const MOVE_UP:String = ?moveUp?; public static const MOVE_DOWN:String = ?moveDown?; public fun­ction ViewEvent(type:String) { super(type, true); } over­ride public fun­ction clone():Event { return new ViewEvent(type); } over­ride public fun­ction toString():String { return formatToString(?ViewEvent?, ?type?, ?bubbles?, ?can­celable?, ?even­tPhase?); } } } [/js] CONTROLLER ->> Controller men­dengarkan ViewEvent yang disiarkan oleh MainView dan meng-update Model ber­dasarkan event tersebut. [js] pac­kage simplemvc.controller { import flash.events.Event; import flash.geom.Point; import simplemvc.model.Model; import simplemvc.view.*; import simplemvc.events.*; /** * ? * @author Ang­gie Bratadinata */ public class Controller { private var _model:Model = Model.getInstance(); private var _view:MainView; //???????????????????? INIT public fun­ction Controller(view:MainView) { _view = view; _model.heroHeight = _view.hero.height; _model.heroWidth = _view.hero.width; _model.heroBounds = _view.board.getBounds(_view); _view.addEventListener(ViewEvent.MOVE_DOWN, viewLis­tener); _view.addEventListener(ViewEvent.MOVE_UP, viewLis­tener); _view.addEventListener(ViewEvent.MOVE_LEFT, viewLis­tener); _view.addEventListener(ViewEvent.MOVE_RIGHT, viewListener); } public fun­ction startUp():void { _model.heroPos = new Point(0, 0); } private fun­ction viewListener(e:ViewEvent):void { switch(e.type) { case ViewEvent.MOVE_DOWN: _model.moveDown(); break; case ViewEvent.MOVE_LEFT: _model.moveLeft(); break; case ViewEvent.MOVE_RIGHT: _model.moveRight(); break; case ViewEvent.MOVE_UP: _model.moveUp(); break; } } } } [/js] Document/Main Class [js] package { import flash.display.MovieClip; import simplemvc.controller.Controller; import simplemvc.view.MainView; /** * ? * @author Ang­gie Bratadinata */ public class Main extends MovieClip { public var mainView:MainView; public var controller:Controller; public fun­ction Main() { con­troller = new Controller(mainView); controller.startUp(); } } } [/js] Seperti Anda lihat, menulis kode dengan struk­tur MVC nggak ter­lalu sulit, hanya sedikit ngerepotin. :-)

 



Comments:

Post a Comment:
  • HTML Syntax: Allowed