ActionScript 3.0: State Pattern
1. Pendahuluan
State Pattern merupakan salah satu bentuk dari design pattern yang memungkinkan sebuah objek merubah behaviournya ketika terjadi perubahan state.
Contohnya: pada objek lampu. Lampu punya dua buah state yaitu: on dan off. Kedua state tersebut punya struktur yang sama, tapi fungsinya jelas jauh berbeda. Satu lagi perbedaannya adalah bagaimana tiap state menrespon transisi. Untuk kasus lampu ini, transisinya adalah tombol on-off.
Keuntungan menggunakan pattern ini:
- Tiap behaviour object diletakkan pada state object yang berbeda-beda
- Transisi diletakkan pada tiap state object, sehingga tidak terbentuk monolithic conditional statement (if atau switch). Ketika jumlah state hanya sedikit, mungkin masih bisa pakai if atau switch, tetapi ketika jumlah state sudah cukup besar, pemakaian if atau switch untuk menghandle transisi jelas akan menimbulkan masalah
- Menghindari state yang inkonsisten, karena dengan memecah menjadi beberapa state object, dapat dipastikan transisi hanya akan terjadi di satu state object saja
Sedangkan untuk kekurangannya yaitu: jumlah objek menjadi lebih banyak. Sangat jelas sekali….
2. Struktur
Untuk struktur class dari state pattern kurang lebih adalah sebagai berikut ini:
Ada 4 komponen utama:
1. State Interface
Nantinya semua state objek akan mengimplement interface ini. Yang artinya juga semua state object bakal memiliki method yang didefinisikan di interface ini.
Selain itu, interface juga punya sifat dapat berperan sebagai datatype, jadi ketika ingin menginstatiating state object di context, kita cukup pake data tye dari interface ini
2. State object
Tempat di mana behaviour objek bakal diletakkan
3. Context
Berperan sebagai proxy untuk semua state object. Context ini juga untuk mengatur pemanggilan dan pergantian state object
4. Transisi
Sesuatu yang akan men-trigger sebuah state untuk berpidah ke state lainnya
Ada dua bentuk transisi:
- transisi didefinisikan di context
- transisi didefinisikan di state object
3. Lets Rawk!
Contoh implementasi state pattern ini gw ambil dari Buku Advanced ActionScript3.0 by Joey Lott & Danny Pattersen yang ceritanya membuat sebuah agent pemaen basket. Agent tsb punya 3 state: layup, threepoint, dan freethrow. Dan di sini transisinya nanti akan didefinisikan di MainClass, jangan bingung dulu, walau didefinisikan di MainClass namun pada intinya adalah berarti transisinya didefinisikan di context bukan di state object.
1. Pertama kita bangun dulu state interface nya. Kita beri nama IShooterState:
1: package
2: {
3: public interface IShooterState
4: {
5: function getAccuracy():Number;
6: function getPointByVal():Number;
7: }
8: }
Ada dua method yaitu getAccuracy() untuk meng-generate kemungkinan masuk atau tidak masuk, dan method getPointByVal() untuk mendapatkan nilai dari shoot tersebut kalo berhasil masuk.
2. Membuat State Object nya
Di sini kita akan membangun 3 buah state object yang di beri nama: FreeThrowState, LayUpState, ThreePointState. Semua tentunya harus mengimplement interface IShooterState
1: package State
2: {
3: import IShooterState;
4:
5: public class FreeThrowState implements IShooterState
6: {
7:
8: public function getAccuracy():Number
9: {
10: return 0.7;
11: }
12:
13: public function getPointByVal():Number
14: {
15: return 1;
16: }
17: }
18: }
Yang di atas adalah contoh untuk FreeThrowState, karena mengimplement interface IShooterState jadi dia juga punya method getAccuracy() dan getPointByVal(). Method ini akan me-return nilai dari akurasi tembakan dan point kalo bola berhasil masuk.
Untuk dua state laennya strukturnya sama persis, hanya nilai yang di return oleh method2xnya yg beda. Monggo silakan berimajinasi dikit
3. Membuat context atau StateMachine
Buat kelas dan kasih nama ShooterStateMachine
1: package
2: {
3: import State.*;
4:
5: public class ShooterStateMachine
6: {
7: private var state:IShooterState;
8:
9: private var index:int;
10: private var stateArray:Array = new Array();
11:
12: public static const LAY_UP_STATE:String = "lay_up_state";
13: public static const FREE_THROW_STATE:String = "free_throw_state";
14: public static const THREE_POINT_STATE:String = "three_point_state";
15:
16: public function ShooterStateMachine()
17: {
18: index = 0;
19:
20: stateArray.push(LAY_UP_STATE);
21: stateArray.push(FREE_THROW_STATE);
22: stateArray.push(THREE_POINT_STATE);
23:
24: setState(stateArray[index]);
25: }
26:
27: public function getAccuracy():Number
28: {
29: return state.getAccuracy();
30: }
31:
32: public function getPointByVal():Number
33: {
34: return state.getPointByVal();
35: }
36:
37: public function getIndex():int
38: {
39: return index;
40: }
41:
42: public function setIndex(val:int):void
43: {
44: index = val;
45: }
46:
47: public function getStateArray():Array
48: {
49: return stateArray;
50: }
51:
52: public function incrementIndex():void
53: {
54: index++;
55: }
56:
57: public function setState(stateName:String):void
58: {
59: switch(stateName)
60: {
61: case LAY_UP_STATE:
62: {
63: state = new LayUpState();
64:
65: break;
66: }
67: case FREE_THROW_STATE:
68: {
69: state = new FreeThrowState();
70:
71: break;
72: }
73: case THREE_POINT_STATE:
74: {
75: state = new ThreePointState;
76:
77: break;
78: }
79: default:
80: {
81: state = null;
82:
83: break;
84: }
85: }
86: }
87:
88: public function getState():String
89: {
90: var temp:String;
91:
92: switch(index)
93: {
94: case 0:
95: {
96: temp = "lay up";
97:
98: break;
99: }
100: case 1:
101: {
102: temp = "free throw";
103:
104: break;
105: }
106: case 2:
107: {
108: temp = "three point";
109:
110: break;
111: }
112: }
113:
114: return temp;
115: }
116: }
117: }
lets break this f***ing code:
7: kita cukup buat satu variabel object state, dengan datatype IShooterState (inilah salah satu the power of interface)
9: variabel index untuk menunjukkan index dalam stateArray
10: stateArray, array untuk menyimpan state2x tsb
16-25: set nilai index menjadi = 0, masukkan semua state ke dalam array, set state awal
27: method getAccuracy(), untuk mengambil data akurasi dari state yang sedang berjalan
32: method getPointByVal(), untuk mengambil data point dari state yang sedang berjalan
37: method getIndex(), untuk mengambil current index di array
42: setIndex(), untuk mengganti index array
52: incrementIndex(), untuk menambahkan nilai index array seiring dengan berjalannya waktu
57: setState(), untuk mengganti state yang sedang berjalan sekarang dengan state baru
88: getState(), untuk mendapatkan data state yang sedang berjalan
4. Buat MainClassnya….
Almost there….
1: package
2: {
3: import flash.display.Sprite;
4: import flash.events.Event;
5: import flash.events.TimerEvent;
6: import flash.utils.*;
7:
8: import com.carlcalderon.arthropod.Debug;
9:
10: public class Main extends Sprite
11: {
12: private var point:Number;
13: private var shooter:ShooterStateMachine;
14:
15: public function Main():void
16: {
17: if (stage) init();
18: else addEventListener(Event.ADDED_TO_STAGE, init);
19: }
20:
21: private function init(e:Event = null):void
22: {
23: removeEventListener(Event.ADDED_TO_STAGE, init);
24:
25: point = 0;
26:
27: shooter = new ShooterStateMachine();
28:
29: var timer:Timer = new Timer(1000, 0);
30: timer.addEventListener(TimerEvent.TIMER, update, false, 0, true);
31: timer.start();
32: }
33:
34: private function calculateShot(acc:Number):Boolean
35: {
36: return Math.random() < acc;
37: }
38:
39: private function update(event:TimerEvent):void
40: {
41: if (calculateShot(shooter.getAccuracy()) == true)
42: {
43: point += shooter.getPointByVal();
44:
45: Debug.log(shooter.getState() + " " + point);
46: }
47: else
48: {
49: Debug.log(shooter.getState() + " miss");
50: }
51:
52: shooter.incrementIndex();
53:
54: if (shooter.getIndex() >= shooter.getStateArray().length)
55: {
56: shooter.setIndex(0);
57: }
58:
59: shooter.setState(shooter.getStateArray()[shooter.getIndex()]);
60: }
61: }
62:
63: }
12-13: buat variabel point untuk menampung poin dari hasil tembakan, dan object shooter sbg agen yang akan diujicoba
25-31: beri nilai awal point = 0. Instatiate object shooter. Buat sebuah timer dengan delay 1 detik. Nantinya state akan terus berubah tiap periode satu detik
34: method yang akan melakukan randomisasi masuk tidaknya tembakan berdasarkan nilai akurasi dari tiap2x state
39-50: Method update() atau ‘main game loop’. Pertama, cek apakah tembakan masuk atau tidak. Jika masuk, tambahkan point berdasarkan nilai poin yang dimiliki state, trace outputnya. Jika tidak, maka hanya akan mengeluarkan output “miss”.
52: tambahkan index dengan memanggil method incrementIndex()
54-57: Jika index lebih besar dari jumlah item dalam array, maka set index menjadi 0, atau berarti balik mulai dari index pertama lagi
59: ganti state dengan state yang batu sesuai nilai indexnya.
5. Test dan liad hasilnya….
Yap, that’s all buat state pattern…..
silakan mencoba…
untuk source codenya dapat di donlot dengan mencet tombol bawah ini:
thanks gan pzuh, mantaps
ReplyDelete