Sebenarnya cukup sederhana:
Alih-alih memiliki Konstruktor yang melakukan pengaturan Anda,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... minta konstruktor Anda melakukan sedikit atau tidak sama sekali, dan tulis metode yang disebut .init
atau .initialize
, yang akan melakukan apa yang biasanya dilakukan konstruktor Anda.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Jadi sekarang, bukannya hanya seperti:
Thing thing = new Thing(1, 2, 3, 4);
Anda bisa pergi:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
Keuntungannya adalah sekarang Anda dapat menggunakan dependensi-injeksi / inversi-kontrol lebih mudah di sistem Anda.
Alih-alih mengatakan
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
Anda dapat membangun tentara, memberinya metode melengkapi, di mana Anda menyerahkan dia senjata, dan KEMUDIAN memanggil semua sisa fungsi konstruktor.
Jadi sekarang, alih-alih musuh subkelas di mana satu tentara memiliki pistol dan yang lain memiliki senapan dan yang lain memiliki senapan, dan itulah satu-satunya perbedaan, Anda bisa mengatakan:
Soldier soldier1 = new Soldier(),
soldier2 = new Soldier(),
soldier3 = new Soldier();
soldier1.equip(new Pistol());
soldier2.equip(new Rifle());
soldier3.equip(new Shotgun());
soldier1.initialize("Bob", 32, 48);
soldier2.initialize("Doug", 57, 200);
soldier3.initialize("Mike", 92, 30);
Kesepakatan yang sama dengan kehancuran. Jika Anda memiliki kebutuhan khusus (menghapus pendengar acara, menghapus instance dari array / struktur apa pun yang bekerja dengan Anda, dll.), Anda kemudian akan memanggil mereka secara manual, sehingga Anda tahu persis kapan dan di mana dalam program yang sedang terjadi.
EDIT
Seperti yang ditunjukkan oleh Kryotan, di bawah ini, ini menjawab "Bagaimana" posting asli , tetapi tidak benar-benar melakukan pekerjaan yang baik dari "Mengapa".
Seperti yang mungkin Anda lihat dalam jawaban di atas, mungkin tidak ada banyak perbedaan antara:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
dan menulis
var myObj = new Object(1,2);
sementara hanya memiliki fungsi konstruktor yang lebih besar.
Ada argumen yang harus dibuat untuk objek yang memiliki 15 atau 20 pra-kondisi, yang akan membuat konstruktor sangat, sangat sulit untuk dikerjakan, dan itu akan membuat segalanya lebih mudah untuk dilihat dan diingat, dengan menarik hal-hal itu ke antarmuka. , sehingga Anda dapat melihat cara instantiation bekerja, satu tingkat lebih tinggi.
Konfigurasi opsional objek adalah ekstensi alami untuk ini; secara opsional mengatur nilai pada antarmuka, sebelum membuat objek berjalan.
JS memiliki beberapa cara pintas yang bagus untuk ide ini, yang sepertinya tidak sesuai dengan bahasa c-type yang lebih kuat.
Yang mengatakan, kemungkinannya adalah, jika Anda berurusan dengan daftar argumen yang lama di konstruktor Anda, bahwa objek Anda terlalu besar dan melakukan terlalu banyak, sebagaimana adanya. Sekali lagi, ini adalah hal preferensi pribadi, dan ada pengecualian jauh dan luas, tetapi jika Anda memasukkan 20 hal ke dalam objek, kemungkinan besar Anda bisa menemukan cara untuk membuat objek itu melakukan lebih sedikit, dengan membuat objek yang lebih kecil .
Alasan yang lebih relevan, dan yang dapat diterapkan secara luas adalah bahwa inisialisasi objek bergantung pada data asinkron, yang saat ini tidak Anda miliki.
Anda tahu bahwa Anda memerlukan objek, jadi Anda tetap akan membuatnya, tetapi untuk membuatnya berfungsi dengan baik, ia membutuhkan data dari server, atau dari file lain yang sekarang perlu dimuat.
Sekali lagi, apakah Anda mengirimkan data yang diperlukan ke init raksasa, atau membangun antarmuka tidak terlalu penting untuk konsep, sebanyak itu penting untuk antarmuka objek Anda, dan desain sistem Anda ...
Tetapi dalam hal membangun objek, Anda mungkin melakukan sesuatu seperti ini:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
mungkin melewati nama file, atau nama sumber daya atau apa pun, memuat sumber daya itu - mungkin memuat file suara, atau data gambar, atau mungkin memuat statistik karakter yang disimpan ...
... dan kemudian akan memasukkan data itu kembali ke obj_w_async_dependencies.init(result);
.
Jenis dinamis ini sering ditemukan di aplikasi web.
Tidak harus dalam konstruksi objek, untuk aplikasi tingkat yang lebih tinggi: misalnya, galeri mungkin memuat dan menginisialisasi segera, dan kemudian menampilkan foto saat mereka streaming - itu tidak benar-benar inisialisasi async, tetapi di mana itu terlihat lebih sering akan menjadi di perpustakaan JavaScript.
Satu modul mungkin tergantung pada yang lain, dan inisialisasi modul itu mungkin ditunda sampai pemuatan tanggungan selesai.
Dalam hal contoh khusus game ini, pertimbangkan Game
kelas yang sebenarnya .
Mengapa kita tidak bisa memanggil .start
atau .run
di konstruktor?
Sumber daya perlu dimuat - sisanya semuanya telah cukup banyak didefinisikan dan baik untuk dijalankan, tetapi jika kita mencoba menjalankan game tanpa koneksi database, atau tanpa tekstur atau model atau suara atau level, itu tidak akan menjadi permainan yang sangat menarik ...
... jadi apa, lalu perbedaan antara apa yang kita lihat dari tipikal Game
, kecuali bahwa kita memberikan metode "maju" nama yang lebih menarik daripada .init
(atau sebaliknya, pisahkan inisialisasi lebih jauh terpisah, untuk memisahkan pemuatan, mengatur hal-hal yang telah dimuat, dan menjalankan program ketika semuanya telah diatur).