Apa itu Configuration Object, dan Mengapa Repot Menggunakannya?
() translation by (you can also view the original English article)
Sungguh menyakitkan harus mengubah parameter fungsi; Anda harus mengubah setiap panggilan lain ke fungsi itu untuk menghindari kesalahan. Tetapi Anda dapat menyiasatinya dengan menggunakan hanya satu parameter: obyek konfigurasi.
Seperti Ini Rupanya
Berikut ini contoh konyol dari fungsi untuk membuat robot:
1 |
|
2 |
function generateRobot(arms:int, personality:String):Robot { |
3 |
var robot:Robot = new Robot(); |
4 |
|
5 |
for (var i:int = 0; i < arms; i++) { |
6 |
//create arm and add it to robot
|
7 |
}
|
8 |
|
9 |
if (personality == "evil") { |
10 |
robot.commands = "Destroy mankind."; |
11 |
}
|
12 |
else { |
13 |
robot.commands = "Bake cookies." |
14 |
}
|
15 |
return robot; |
16 |
}
|
17 |
|
18 |
generateRobot(2, "evil"); |
Sekarang, inilah contoh yang sama, menggunakan obyek konfigurasi:
1 |
|
2 |
function generateRobot(conf:Object):Robot { |
3 |
var robot:Robot = new Robot(); |
4 |
|
5 |
for (var i:int = 0; i < conf.arms; i++) { |
6 |
//create arm and add it to robot
|
7 |
}
|
8 |
|
9 |
if (conf.personality == "evil") { |
10 |
robot.commands = "Destroy mankind."; |
11 |
}
|
12 |
else { |
13 |
robot.commands = "Bake cookies." |
14 |
}
|
15 |
return robot; |
16 |
}
|
17 |
|
18 |
generateRobot({arms:2, personality:"evil"}); |
Saya telah menyoroti baris yang perlu diubah; Anda dapat melihat bahwa tidak ada banyak perbedaan.
Mengapa Repot-repot?
Jadi jika hampir tidak ada perbedaan, mengapa kita repot-repot melakukannya dengan cara kedua? Bagaimanapun, itu sebenarnya membuat fungsi sedikit lebih sulit untuk digunakan; sedangkan sebelum IDE kami dapat memberi kami informasi tentang parameter yang diharapkan fungsi:

...sekarang hanya bisa memberi kita ini:

Misalkan Anda ingin menambahkan beberapa parameter lagi: satu menentukan bahan yang akan digunakan dan yang lain untuk menentukan warna laser yang seharusnya. Itu tidak terlalu sulit, dalam kedua kasus:
1 |
|
2 |
function generateRobot(arms:int, personality:String, material:String, laserColor:String):Robot { |
3 |
var robot:Robot = new Robot(); |
4 |
|
5 |
for (var i:int = 0; i < arms; i++) { |
6 |
//create arm and add it to robot
|
7 |
}
|
8 |
|
9 |
if (personality == "evil") { |
10 |
robot.commands = "Destroy mankind."; |
11 |
}
|
12 |
else { |
13 |
robot.commands = "Bake cookies." |
14 |
}
|
15 |
|
16 |
switch (material) { |
17 |
case "wood": |
18 |
//wooden robot
|
19 |
break; |
20 |
case "steel": |
21 |
default: |
22 |
//steel robot
|
23 |
break; |
24 |
}
|
25 |
|
26 |
robot.laser = new Laser(); |
27 |
robot.laser.color = laserColor; |
28 |
|
29 |
return robot; |
30 |
}
|
31 |
|
32 |
generateRobot(2, "evil", "steel", "red"); |
1 |
|
2 |
function generateRobot(conf:Object):Robot { |
3 |
var robot:Robot = new Robot(); |
4 |
|
5 |
for (var i:int = 0; i < conf.arms; i++) { |
6 |
//create arm and add it to robot
|
7 |
}
|
8 |
|
9 |
if (conf.personality == "evil") { |
10 |
robot.commands = "Destroy mankind."; |
11 |
}
|
12 |
else { |
13 |
robot.commands = "Bake cookies." |
14 |
}
|
15 |
|
16 |
switch (conf.material) { |
17 |
case "wood": |
18 |
//wooden robot
|
19 |
break; |
20 |
case "steel": |
21 |
default: |
22 |
//steel robot
|
23 |
break; |
24 |
}
|
25 |
|
26 |
robot.laser = new Laser(); |
27 |
robot.laser.color = conf.laserColor; |
28 |
|
29 |
return robot; |
30 |
}
|
31 |
|
32 |
generateRobot({arms:2, personality:"evil", material:"steel", laserColor:"red"}); |
Sejauh ini, masih tidak banyak perbedaan. Bagaimana jika Anda ingin semua robot memiliki laser merah? Sederhana lagi. Tanpa obyek konfigurasi, Anda hanya perlu mengubah metode signature (baris function
), dan kemudian Anda dapat menghapus argumen terakhir dari panggilan fungsi:
1 |
|
2 |
function generateRobot(arms:int, personality:String, material:String, laserColor:String = "red"):Robot { |
3 |
//this is all the same
|
4 |
}
|
5 |
|
6 |
generateRobot(2, true, "steel"); //I removed the last argument |
Dengan obyek konfigurasi, ini sedikit rumit - meskipun tidak banyak:
1 |
|
2 |
function generateRobot(conf:Object):Robot { |
3 |
if (!conf.laserColor) { |
4 |
conf.laserColor = "red"; |
5 |
}
|
6 |
|
7 |
var robot:Robot = new Robot(); |
8 |
|
9 |
for (var i:int = 0; i < conf.arms; i++) { |
10 |
//create arm and add it to robot
|
11 |
}
|
12 |
|
13 |
if (conf.personality == "evil") { |
14 |
robot.commands = "Destroy mankind."; |
15 |
}
|
16 |
else { |
17 |
robot.commands = "Bake cookies." |
18 |
}
|
19 |
|
20 |
switch (conf.material) { |
21 |
case "wood": |
22 |
//wooden robot
|
23 |
break; |
24 |
case "steel": |
25 |
default: |
26 |
//steel robot
|
27 |
break; |
28 |
}
|
29 |
|
30 |
robot.laser = new Laser(); |
31 |
robot.laser.color = conf.laserColor; |
32 |
|
33 |
return robot; |
34 |
}
|
35 |
|
36 |
generateRobot({arms:2, personality:"evil", material:"steel"}); //I removed the last argument |
Baiklah. Sekarang anggaplah Anda menemukan bahwa Anda mengatur hampir semua robot Anda menjadi jahat (maksud saya, mengapa tidak?), jadi sebenarnya agak merepotkan untuk menulis "kejahatan" sebagai parameter setiap waktu. Secara alami, Anda ingin mengatur "jahat" sebagai default - tetapi Anda tidak ingin mengatur materi default.
Satu-satunya cara Anda dapat melakukan ini, dengan seperangkat parameter fungsi biasa, adalah dengan mengubah urutan parameter personality
dan material
:
1 |
|
2 |
function generateRobot(arms:int, material:String, personality:String = "evil", laserColor:String = "red"):Robot { |
Ah, tapi sekarang Anda harus mengganti urutan putaran argumen pada setiap panggilan fungsi tunggal!
1 |
|
2 |
generateRobot(2, "evil", "steel"); //no longer works |
Obyek konfigurasi tidak memberi Anda masalah ini. Coba lihat:
1 |
|
2 |
function generateRobot(conf:Object):Robot { |
3 |
if (!conf.laserColor) { |
4 |
conf.laserColor = "red"; |
5 |
}
|
6 |
if (!conf.personality) { |
7 |
conf.personality = "evil" |
8 |
}
|
9 |
|
10 |
//this is all the same
|
11 |
}
|
12 |
|
13 |
generateRobot({arms:2, material:"steel"}); //no "personality" parameter? no problem! |
Rapi! Semua panggilan fungsi generateRobot()
lama Anda akan terus berfungsi, tetapi Anda dapat membuat panggilan baru yang tidak mengganggu dalam menentukan personality
.
Anda bahkan dapat memutuskan untuk menghapus parameter personality
sekaligus:
1 |
|
2 |
function generateRobot(conf:Object):Robot { |
3 |
if (!conf.laserColor) { |
4 |
conf.laserColor = "red"; |
5 |
}
|
6 |
if (!conf.personality) { |
7 |
conf.personality = "evil" |
8 |
}
|
9 |
|
10 |
var robot:Robot = new Robot(); |
11 |
|
12 |
for (var i:int = 0; i < conf.arms; i++) { |
13 |
//create arm and add it to robot
|
14 |
}
|
15 |
|
16 |
robot.commands = "Destroy mankind."; |
17 |
|
18 |
switch (conf.material) { |
19 |
case "wood": |
20 |
//wooden robot
|
21 |
break; |
22 |
case "steel": |
23 |
default: |
24 |
//steel robot
|
25 |
break; |
26 |
}
|
27 |
|
28 |
robot.laser = new Laser(); |
29 |
robot.laser.color = conf.laserColor; |
30 |
|
31 |
return robot; |
32 |
}
|
Versi fungsi di atas sama sekali tidak merujuk ke conf.personality
- tetapi Anda tidak akan mendapatkan kesalahan jika Anda masih memiliki panggilan seperti ini:
1 |
|
2 |
generateRobot({arms:2, personality:"evil", material:"steel"}); |
Tentu saja, Anda mungkin mendapatkan beberapa pengguna yang bingung jika ada panggilan seperti ini:
1 |
|
2 |
generateRobot({arms:2, personality:"good", material:"steel"}); |
...karena semua robot sekarang jahat. Tetapi setidaknya kode akan dikompilasi.
Untuk alasan yang sama, Anda dapat mengubah urutan argumen tanpa masalah sama sekali, dan bahkan menambahkan parameter baru yang belum melakukan apa pun:
1 |
|
2 |
generateRobot({material:"steel", laserColor:"green", arms:2, voice:"Mr. T"}); |
Membuatnya Lebih Mudah untuk Mengatur Default
Kode untuk pengaturan default mudah dipahami sejauh ini, tetapi akan sangat menjengkelkan untuk memperpanjang jika kita perlu memiliki banyak parameter:
1 |
|
2 |
if (!conf.laserColor) { |
3 |
conf.laserColor = "red"; |
4 |
}
|
5 |
if (!conf.personality) { |
6 |
conf.personality = "evil" |
7 |
}
|
Mari kita menulis beberapa kode yang lebih umum untuk mengatasinya:
1 |
|
2 |
var defaults:Object = { |
3 |
laserColor:red, |
4 |
personality: "evil" |
5 |
}
|
6 |
|
7 |
for (var key:String in defaults){ |
8 |
if (!conf[key]) { |
9 |
conf[key] = defaults[key]; |
10 |
}
|
11 |
}
|
Perulangan for
itu mungkin sedikit membingungkan, jadi saya akan memecahnya. Pertama, lihat ini:
1 |
|
2 |
for (var key:String in defaults){ |
3 |
trace(key); |
4 |
}
|
Ini adalah perulangan for...in
, yang akan menampilkan nama-nama kunci di dalam obyek default
:
1 |
|
2 |
laserColor |
3 |
personality |
Selanjutnya, lihat baris ini:
1 |
|
2 |
trace(defaults["laserColor"]); |
Ini akan menghasilkan red
- sama dengan menulis trace(defaults.laserColor)
.
Setelah itu, lihat contoh ini:
1 |
|
2 |
var example:Object = { demo: "test" }; |
3 |
trace(example["demo"]); |
4 |
trace(example["foo"]); |
Menurut Anda apa yang akan dihasilkan ini?
Nah, example["demo"]
sama dengan example.demo
, yang sama dengan "test"
. Tapi example.foo
tidak ada, jadi example["foo"]
akan mengembalikan null
. Ini berarti bahwa !example["foo"]
(perhatikan tanda seru) akan sama dengan true
.
Gabungkan semuanya, dan Anda harus dapat memahami mengapa kode ini bekerja:
1 |
|
2 |
var defaults:Object = { |
3 |
laserColor:red, |
4 |
personality: "evil" |
5 |
}
|
6 |
|
7 |
for (var key:String in defaults){ |
8 |
if (!conf[key]) { |
9 |
conf[key] = defaults[key]; |
10 |
}
|
11 |
}
|
Beri saya teriakan di komentar jika Anda butuh bantuan!
Saya ingin lebih!
Untuk versi yang lebih cepat, coba ini:
1 |
|
2 |
function generateRobot(conf:Object = null):Robot { |
3 |
var conf:Object = conf || {}; |
4 |
var defaults:Object = { |
5 |
laserColor:red, |
6 |
personality: "evil" |
7 |
}
|
8 |
|
9 |
for (var key:String in defaults){ |
10 |
conf[key] = conf[key] || defaults[key]; |
11 |
}
|
Perubahan pada Baris 1 (dan Baris 2 yang baru) berarti bahwa bahkan obyek conf
itu sendiri adalah opsional, jadi Anda bisa memanggil generateRobot()
. (Tentu saja, Anda harus mengubah kode untuk menangani nilai-nilai yang saat ini tidak memiliki default.)
Membantu IDE agar Membantu Anda
Seperti yang saya sebutkan di atas, IDE tidak dapat memberi Anda tips tentang parameter apa yang diharapkan oleh fungsi, jika fungsi itu menggunakan obyek konfigurasi. Ini adalah kelemahan utama, karena dapat membuat kode Anda sangat sulit digunakan; Anda harus mengingat parameter mana yang masuk dalam obyek conf
, serta semua nama dan tipe mereka.
Tetapi kita masih bisa menampilkan informasi ini ke pembuat kode ketika dibutuhkan; kita hanya perlu melakukannya secara manual, seperti:
1 |
|
2 |
/**
|
3 |
* Generate a robot, based on the parameters given.
|
4 |
* @param conf Configuration object. Expects:
|
5 |
* arms (int) Number of arms robot should have.
|
6 |
* personality (String) Personality of robot. Can be "evil" or "good". Defaults to "evil".
|
7 |
* material (String) What the robot should be made out of. Can be "steel" or "wood" at this time.
|
8 |
* laserColor (String) Color of the robot's laser. Defaults to "red".
|
9 |
* voice (String) Vocal stylings of robot. Currently not implemented.
|
10 |
* @return The finished robot.
|
11 |
*/
|
12 |
function generateRobot(conf:Object):Robot { |
13 |
//
|
14 |
}
|
Sekarang, jika saya mulai menulis panggilan ke fungsi ini di FlashDevelop (IDE pilihan saya), saya melihat ini:



Tentu, ini agak menyebalkan untuk tetap memperbarui ini secara manual, tetapi dalam banyak kasus itu sepadan.
Kesimpulan
Saya tidak mengklaim bahwa Anda harus menggunakan obyek konfigurasi untuk setiap fungsi tunggal yang Anda buat mulai sekarang; anggap saja itu sebagai alat lain yang berguna dalam gudang senjata Anda.
Secara pribadi, saya menemukan pola yang sangat berguna setiap kali saya membangun konsep pertama dari beberapa kelas yang semua harus bekerja bersama. Fleksibilitas tambahan dari sebuah conf
memberikan saya jauh lebih banyak fleksibilitas, membebaskan saya untuk menangani semua fungsi yang berbeda dan mengubah cara mereka memanggil satu sama lain, tanpa khawatir tentang memecahkan kode dengan memasukkan atau menghapus parameter.
Ingat manfaatnya:
- Sangat mudah untuk menambah dan menghapus parameter (di kedua ujungnya).
- Mudah untuk menetapkan nilai default.
- Anda tidak perlu khawatir tentang urutan parameter.
Ada beberapa kelemahan dalam menggunakan obyek sederhana seperti yang saya miliki - terutama jika Anda melakukannya dalam proyek yang melewati tahap prototyping. Lihat komentar-komentar hebat di bawah ini untuk lebih jelasnya!