Saya menyaksikan pembicaraan Stuart Sierra " Thinking In Data " dan mengambil salah satu ide darinya sebagai prinsip desain dalam game yang saya buat ini. Perbedaannya adalah dia bekerja di Clojure dan saya bekerja di JavaScript. Saya melihat beberapa perbedaan utama antara bahasa kami dalam hal:
- Clojure adalah pemrograman yang fungsional secara idiomatis
- Kebanyakan negara tidak berubah
Saya mengambil ide dari slide "Semuanya adalah Peta" (Dari 11 menit, 6 detik hingga> 29 menit). Beberapa hal yang dia katakan adalah:
- Setiap kali Anda melihat fungsi yang membutuhkan 2-3 argumen, Anda bisa membuat case untuk mengubahnya menjadi peta dan hanya meneruskan peta. Ada banyak keuntungan untuk itu:
- Anda tidak perlu khawatir tentang urutan argumen
- Anda tidak perlu khawatir tentang informasi tambahan apa pun. Jika ada kunci tambahan, itu bukan urusan kami. Mereka mengalir begitu saja, mereka tidak ikut campur.
- Anda tidak perlu mendefinisikan skema
- Berlawanan dengan mengirimkan Obyek, tidak ada data yang disembunyikan. Tapi, dia membuat kasus bahwa persembunyian data dapat menyebabkan masalah dan dinilai terlalu tinggi:
- Performa
- Kemudahan implementasi
- Segera setelah Anda berkomunikasi melalui jaringan atau lintas proses, Anda harus tetap memiliki kedua belah pihak menyetujui representasi data. Itu pekerjaan ekstra yang bisa Anda lewati jika Anda hanya bekerja pada data.
Paling relevan dengan pertanyaan saya. Ini adalah 29 menit dalam: "Jadikan fungsi Anda dapat dikompilasi". Berikut contoh kode yang ia gunakan untuk menjelaskan konsep:
;; Bad (defn complex-process [] (let [a (get-component @global-state) b (subprocess-one a) c (subprocess-two a b) d (subprocess-three a b c)] (reset! global-state d))) ;; Good (defn complex-process [state] (-> state subprocess-one subprocess-two subprocess-three))
Saya mengerti sebagian besar programmer tidak terbiasa dengan Clojure, jadi saya akan menulis ulang ini dengan gaya imperatif:
;; Good def complex-process(State state) state = subprocess-one(state) state = subprocess-two(state) state = subprocess-three(state) return state
Inilah kelebihannya:
- Mudah diuji
- Mudah untuk melihat fungsi-fungsi tersebut secara terpisah
- Mudah mengomentari satu baris ini dan melihat apa hasilnya dengan menghapus satu langkah
- Setiap subproses dapat menambahkan lebih banyak informasi ke negara. Jika subproses seseorang perlu mengkomunikasikan sesuatu ke subproses tiga, itu semudah menambahkan kunci / nilai.
- Tidak ada boilerplate untuk mengekstraksi data yang Anda butuhkan dari negara hanya agar Anda dapat menyimpannya kembali. Cukup lewati seluruh negara bagian dan biarkan subproses menetapkan apa yang dibutuhkan.
Sekarang, kembali ke situasi saya: Saya mengambil pelajaran ini dan menerapkannya pada permainan saya. Artinya, hampir semua fungsi tingkat tinggi saya mengambil dan mengembalikan gameState
objek. Objek ini berisi semua data permainan. EG: Daftar badGuys, daftar menu, jarahan di tanah, dll. Berikut adalah contoh fungsi pembaruan saya:
update(gameState)
...
gameState = handleUnitCollision(gameState)
...
gameState = handleLoot(gameState)
...
Yang ingin saya tanyakan di sini adalah, sudahkah saya membuat beberapa kekejian yang memutarbalikkan ide yang hanya praktis dalam bahasa pemrograman fungsional? JavaScript tidak berfungsi secara idiomatis (meskipun dapat ditulis seperti itu) dan sangat sulit untuk menulis struktur data yang tidak dapat diubah. Satu hal yang mengkhawatirkan saya adalah dia berasumsi bahwa masing-masing subproses itu murni. Mengapa asumsi itu perlu dibuat? Jarang ada fungsi saya yang murni (maksud saya, mereka sering memodifikasi gameState
. Saya tidak punya efek samping rumit lain selain itu). Apakah ide-ide ini berantakan jika Anda tidak memiliki data abadi?
Saya khawatir bahwa suatu hari saya akan bangun dan menyadari bahwa seluruh desain ini palsu dan saya benar-benar baru saja menerapkan anti-pola Big Ball Of Mud .
Sejujurnya, saya telah mengerjakan kode ini selama berbulan-bulan dan ini sangat bagus. Saya merasa seperti saya mendapatkan semua keuntungan yang diklaimnya. Kode saya sangat mudah untuk saya pikirkan. Tapi saya tim satu orang jadi saya memiliki kutukan pengetahuan.
Memperbarui
Saya telah mengkode 6+ bulan dengan pola ini. Biasanya saat ini saya lupa apa yang telah saya lakukan dan di situlah "apakah saya menulis ini dengan cara yang bersih?" ikut bermain. Jika tidak, saya akan benar-benar berjuang. Sejauh ini, saya tidak berjuang sama sekali.
Saya mengerti bagaimana set mata lain diperlukan untuk memvalidasi perawatannya. Yang bisa saya katakan adalah saya peduli tentang rawatan pertama dan terutama. Saya selalu menjadi penginjil paling keras untuk kode bersih di mana pun saya bekerja.
Saya ingin membalas langsung kepada mereka yang sudah memiliki pengalaman pribadi yang buruk dengan cara pengkodean ini. Saya tidak mengetahuinya saat itu, tetapi saya pikir kita benar-benar berbicara tentang dua cara penulisan kode yang berbeda. Cara saya melakukannya tampaknya lebih terstruktur daripada apa yang orang lain alami. Ketika seseorang memiliki pengalaman pribadi yang buruk dengan "Semuanya adalah peta" mereka berbicara tentang betapa sulitnya mempertahankan karena:
- Anda tidak pernah tahu struktur peta yang membutuhkan fungsi
- Fungsi apa pun dapat mengubah input dengan cara yang tidak pernah Anda harapkan. Anda harus melihat seluruh basis kode untuk mencari tahu bagaimana kunci tertentu masuk ke peta atau mengapa itu menghilang.
Bagi mereka yang memiliki pengalaman seperti itu, mungkin basis kode adalah, "Semuanya membutuhkan 1 dari N jenis peta." Milik saya adalah, "Semuanya membutuhkan 1 dari 1 jenis peta". Jika Anda tahu struktur 1 tipe itu, Anda tahu struktur segalanya. Tentu saja, struktur itu biasanya tumbuh seiring waktu. Itu sebabnya ...
Ada satu tempat untuk mencari implementasi referensi (yaitu: skema). Implementasi referensi ini adalah kode yang digunakan game agar tidak ketinggalan zaman.
Adapun poin kedua, saya tidak menambahkan / menghapus kunci ke peta di luar implementasi referensi, saya hanya bermutasi apa yang sudah ada. Saya juga memiliki serangkaian besar tes otomatis.
Jika arsitektur ini akhirnya runtuh karena bobotnya sendiri, saya akan menambahkan pembaruan kedua. Kalau tidak, anggap semuanya berjalan dengan baik :)