Katakanlah saya memiliki kelas MediaPlayer yang memiliki metode play () dan stop (). Apa strategi terbaik untuk digunakan ketika menerapkan metode berhenti jika metode bermain belum dipanggil sebelumnya. Saya melihat dua opsi: melempar pengecualian karena pemain tidak dalam kondisi yang sesuai, atau diam-diam mengabaikan panggilan ke metode berhenti.
Apa yang seharusnya menjadi aturan umum ketika suatu metode tidak seharusnya dipanggil dalam beberapa situasi, tetapi eksekusi itu tidak membahayakan program secara umum?
exceptions
methods
state
x2bool
sumber
sumber
Jawaban:
Tidak ada aturan. Ini sepenuhnya tergantung pada bagaimana Anda ingin membuat API "terasa".
Secara pribadi, dalam pemutar musik, saya pikir transisi dari negara
Stopped
keStopped
melalui metodeStop()
ini adalah transisi keadaan yang benar-benar valid. Ini tidak terlalu berarti, tetapi ini valid. Dengan mengingat hal ini, melempar pengecualian akan tampak seperti hal yang tidak masuk akal dan tidak adil. Itu akan membuat API terasa seperti sosial yang setara dengan berbicara dengan anak yang mengganggu di bus sekolah. Anda terjebak dengan situasi yang menjengkelkan, tetapi Anda bisa mengatasinya.Pendekatan yang lebih "ramah" adalah untuk mengakui bahwa transisi paling tidak berbahaya dan baik untuk pengembang Anda dengan mengizinkannya.
Jadi, jika itu terserah saya, saya akan melewatkan pengecualian.
sumber
MediaPlayer.stop()
metode yang membuangIllegalStateException
alih - alih menghabiskan berjam-jam kode debugging dan bertanya-tanya mengapa Neraka "tidak ada yang terjadi" (yaitu "itu tidak berfungsi").StopOrThrow()
terdengar mengerikan. Jika Anda turun lorong itu mengapa tidak menggunakan pola standarTryStop()
? Juga, secara umum, ketika perilaku API tidak jelas (karena kebanyakan dari mereka) pengembang tidak diharapkan untuk menebak atau bereksperimen, mereka diharapkan untuk melihat dokumentasi. Itu sebabnya saya sangat menyukai MSDN.Ada dua jenis tindakan yang mungkin ingin dilakukan:
Secara simultan menguji sesuatu dalam satu keadaan, dan mengubahnya ke yang lain.
Tetapkan sesuatu ke kondisi tertentu, tanpa memperhatikan kondisi sebelumnya.
Beberapa konteks memerlukan satu tindakan, dan beberapa memerlukan yang lain. Jika pemutar media yang mencapai akhir konten akan tetap dalam keadaan "pemutaran", tetapi dengan posisi dibekukan di akhir, maka metode yang secara bersamaan menyatakan bahwa pemutar berada dalam keadaan pemutaran sambil mengaturnya ke "berhenti" keadaan mungkin berguna jika kode ingin memastikan bahwa tidak ada yang menyebabkan pemutaran gagal sebelum permintaan berhenti (yang mungkin penting jika misalnya ada sesuatu yang merekam konten yang diputar sebagai sarana untuk mengubah media dari satu format ke format lain) . Namun, dalam kebanyakan kasus, yang penting adalah bahwa setelah operasi, pemutar media berada dalam kondisi yang diharapkan (yaitu dihentikan).
Jika pemutar media berhenti secara otomatis ketika mencapai akhir media, fungsi yang menyatakan bahwa pemutar sedang berjalan kemungkinan akan lebih menyebalkan daripada membantu, tetapi dalam beberapa kasus mengetahui apakah pemutar sedang berjalan pada saat itu berhenti dapat menjadi berguna. Mungkin pendekatan terbaik untuk memenuhi kedua kebutuhan adalah memiliki fungsi mengembalikan nilai yang menunjukkan keadaan pemain sebelumnya. Kode yang peduli tentang keadaan itu dapat memeriksa nilai kembali; kode yang tidak peduli bisa mengabaikannya.
sumber
bool TryStop()
danvoid Stop()
akan membuat ini jawaban yang sangat bagus.TryStop
akan menyiratkan bahwa pengecualian tidak boleh dilemparkan jika pemain tidak dapat dipaksa ke keadaan berhenti. Mencoba menghentikan pemain ketika sudah berhenti bukanlah kondisi yang luar biasa, tetapi merupakan kondisi yang mungkin menarik bagi penelepon.isPlaying()
.Tidak ada aturan umum. Dalam kasus khusus ini, niat pengguna API Anda adalah untuk menghentikan pemain dari memainkan media. Jika pemain tidak memainkan media,
MediaPlayer.stop()
mungkin tidak melakukan apa-apa dan tujuan pemanggil metode masih akan tercapai - media tidak bermain.Melempar pengecualian akan mengharuskan pengguna API untuk memeriksa apakah pemain saat ini bermain atau menangkap dan menangani pengecualian. Ini akan membuat API lebih dari tugas untuk digunakan.
sumber
Tujuan pengecualian bukan untuk memberi sinyal bahwa sesuatu yang buruk telah terjadi; itu menandakan itu
Jika mencoba penelepon untuk
Stop
sebuahMediaPlayer
yang sudah dalam keadaan berhenti, ini bukan masalah bahwaMediaPlayer
tidak bisa tekad (itu hanya dapat melakukan apa-apa.) Ini bukan masalah itu, jika melanjutkan, akan menyebabkan kerusakan atau data korupsi, karena itu dapat berhasil hanya dengan tidak melakukan apa pun. Dan itu sebenarnya bukan masalah yang lebih masuk akal untuk mengharapkan penelepon untuk dapat menyelesaikan daripadaMediaPlayer
.Karena itu, Anda tidak boleh melempar pengecualian dalam situasi ini.
sumber
Lihatlah seperti ini:
Jika klien memanggil Stop () ketika pemain tidak bermain, maka Stop () secara otomatis berhasil karena pemain saat ini dalam keadaan berhenti.
sumber
Aturannya adalah Anda melakukan apa yang diperlukan dalam kontrak metode Anda.
Saya bisa melihat banyak cara untuk mendefinisikan kontrak yang bermakna untuk
stop
metode semacam itu . Ini bisa sangat valid untukstop
metode untuk tidak melakukan apa pun jika pemain tidak bermain. Dalam hal ini, Anda menentukan API Anda dengan sasarannya. Anda ingin bertransisi kestopped
negara, danstop
metode melakukan itu - hanya dengan bertransisi daristopped
negara ke dirinya sendiri tanpa kesalahan.Apakah ada alasan mengapa Anda ingin melemparkan Pengecualian? Jika kode pengguna akan terlihat seperti ini pada akhirnya:
maka tidak ada untungnya memiliki pengecualian itu. Jadi pertanyaannya adalah, apakah pengguna akan peduli dengan fakta bahwa pemain sudah berhenti? Apakah dia punya wa lagi untuk menanyakannya?
Pemain mungkin diamati, dan mungkin sudah memberitahu pengamat tentang hal-hal tertentu - misalnya yang memberitahukan pengamat ketika transitons dari
playing
kestopping
kestopped
. Dalam hal ini, tidak perlu metode melempar pengecualian. Memanggil tidakstop
akan melakukan apa pun jika pemain sudah berhenti, dan memanggilnya saat tidak berhenti akan memberi tahu pengamat tentang transisi.Secara keseluruhan, itu tergantung di mana logika Anda akan berakhir. Tapi saya pikir ada pilihan desain yang lebih baik daripada melemparkan pengecualian.
Anda harus melemparkan pengecualian jika penelepon harus melakukan intervensi. Dalam hal ini ia tidak harus melakukannya, Anda bisa membiarkan pemainnya apa adanya dan terus bekerja.
sumber
Ada strategi umum sederhana yang membantu Anda membuat keputusan ini.
Pertimbangkan bagaimana pengecualian akan ditangani jika itu akan dilempar.
Jadi bayangkan pemutar musik Anda, klik pengguna berhenti dan kemudian berhenti lagi. Apakah Anda ingin menampilkan pesan kesalahan dalam skenario itu? Saya belum melihat pemain yang melakukannya. Apakah Anda ingin perilaku aplikasi berbeda dari hanya mengklik berhenti sekali (seperti mencatat acara atau mengirim laporan kesalahan di latar belakang)? Mungkin tidak.
Itu berarti pengecualian perlu ditangkap dan ditelan di suatu tempat. Dan itu berarti Anda lebih baik tidak melempar pengecualian di tempat pertama karena Anda tidak ingin mengambil tindakan yang berbeda .
Ini tidak hanya berlaku untuk pengecualian tetapi semua jenis percabangan: pada dasarnya jika tidak perlu ada perbedaan perilaku yang dapat diamati, tidak perlu bercabang.
Yang mengatakan, tidak ada salahnya mendefinisikan
stop()
metode Anda untuk mengembalikanboolean
atau nilai enum yang menunjukkan apakah berhenti itu "berhasil". Anda mungkin tidak akan pernah menggunakannya, tetapi nilai pengembalian bisa lebih mudah dan secara alami diabaikan daripada pengecualian.sumber
Berikut ini cara android melakukannya: MediaPlayer
Singkatnya,
stop
ketikastart
belum dipanggil bukan masalah, sistem tetap dalam keadaan berhenti, tetapi jika dipanggil pada pemain yang bahkan tidak tahu apa yang sedang diputar, pengecualian dilemparkan, karena tidak ada alasan yang baik untuk memanggil Berhenti disana.sumber
Saya melihat apa yang bisa menjadi kontradiksi kecil dalam pernyataan Anda yang mungkin membuat jawabannya jelas bagi Anda. Mengapa metode ini tidak seharusnya dipanggil dan eksekusi itu tidak membahayakan ?
Mengapa metode "tidak seharusnya disebut"? Itu tampak seperti pembatasan diri sendiri. Jika tidak ada "kerusakan" atau dampak pada API Anda, maka tidak ada pengecualian. Jika metode ini benar-benar tidak boleh dipanggil karena dapat membuat keadaan tidak valid atau tidak terduga, maka pengecualian harus dilemparkan.
Misalnya jika saya berjalan ke DVD player dan tekan "stop" sebelum menekan "start" dan itu crash, itu tidak masuk akal bagi saya. Seharusnya hanya duduk di sana atau, lebih buruk, mengakui "berhenti" di layar. Kesalahan akan mengganggu dan tidak membantu dengan cara apa pun.
Namun, dapatkah saya memasukkan kode alarm untuk mematikannya "" ketika sudah dimatikan (memanggil metode), yang dapat menyebabkannya memasuki keadaan yang akan mencegahnya mempersenjatai dengan benar nanti? Jika demikian, maka buang kesalahan meskipun, secara teknis, "tidak ada salahnya dilakukan" karena mungkin ada masalah nanti. Saya ingin tahu tentang ini bahkan jika itu tidak benar-benar masuk ke keadaan itu. Hanya kemungkinannya sudah cukup. Ini akan menjadi
IllegalStateException
.Dalam kasus Anda, jika mesin negara Anda dapat menangani panggilan tidak valid / tidak perlu maka abaikan saja, jika tidak lakukan kesalahan.
EDIT:
Perhatikan bahwa kompiler tidak meminta kesalahan jika Anda menetapkan variabel dua kali tanpa memeriksa nilainya. Ini juga tidak mencegah Anda menginisialisasi ulang variabel. Ada banyak tindakan yang dapat dianggap sebagai "bug" dari satu perspektif, tetapi hanya "tidak efisien", "tidak perlu", "tidak berguna", dll. Saya mencoba memberikan perspektif itu dalam jawaban saya - melakukan hal-hal ini umumnya tidak dianggap "bug" karena tidak menghasilkan sesuatu yang tidak terduga. Anda mungkin harus mendekati masalah Anda dengan cara yang sama.
sumber
Apa yang sebenarnya dilakukan
MediaPlayer.play()
danMediaPlayer.stop()
dilakukan? - apakah mereka pendengar acara untuk input pengguna atau apakah mereka benar-benar metode yang memulai semacam aliran media pada sistem? Jika mereka adalah pendengar untuk input pengguna, maka sangat masuk akal bagi mereka untuk tidak melakukan apa-apa (walaupun itu ide yang baik untuk setidaknya mencatatnya di suatu tempat). Namun, jika mereka memengaruhi model yang dikontrol UI Anda, maka mereka dapat melemparIllegalStateException
karena pemain harus memiliki semacamisPlaying=true
atauisPlaying=false
keadaan (tidak harus berupa bendera Boolean yang sebenarnya seperti yang ditulis di sini), dan ketika Anda meneleponMediaPlayer.stop()
kapanisPlaying=false
, Metode tidak dapat benar - benar "menghentikan" ituMediaPlayer
karena objek tidak dalam keadaan yang tepat untuk dihentikan - lihat deskripsijava.lang.IllegalStateException
kelas:Mengapa melempar pengecualian bisa bagus
Banyak orang tampaknya berpikir bahwa pengecualian pada umumnya buruk, karena mereka tidak boleh dibuang (lih. Joel tentang Perangkat Lunak ). Namun, katakan Anda memiliki
MediaPlayerGUI.notifyPlayButton()
panggilan manaMediaPlayer.play()
, danMediaPlayer.play()
memanggil banyak kode lain dan di suatu tempat di garis itu berinteraksi dengan misalnya PulseAudio , tetapi saya (pengembang) tidak tahu di mana karena saya tidak menulis semua kode itu.Kemudian, suatu hari saat mengerjakan kode, saya klik "main" dan tidak ada yang terjadi. Jika
MediaPlayerGUIController.notifyPlayButton()
mencatat sesuatu, setidaknya saya dapat melihat log dan memeriksa apakah klik tombol itu benar-benar terdaftar ... tetapi mengapa itu tidak diputar? Nah, katakanlah sebenarnya ada sesuatu yang salah dengan bungkus PulseAudio yangMediaPlayer.play()
memanggil. Saya klik tombol "play" lagi, dan kali ini saya mendapatIllegalStateException
:Dengan melihat jejak stack itu, saya dapat mengabaikan semuanya sebelum
MediaPlayer.play()
saat debugging dan menghabiskan waktu saya mencari tahu mengapa misalnya tidak ada pesan yang diteruskan ke PulseAudio untuk memulai aliran audio.Di sisi lain, jika Anda tidak melempar pengecualian, saya tidak akan memiliki apa pun untuk mulai bekerja selain: "Program bodoh tidak memainkan musik apa pun"; Meskipun tidak seburuk eksplisit kesalahan bersembunyi seperti benar-benar menelan pengecualian yang telah sebenarnya dilemparkan , Anda masih berpotensi membuang-buang waktu orang lain ketika ada sesuatu yang salah ... jadi bagus dan melemparkan sebuah pengecualian pada mereka.
sumber
Saya lebih suka merancang API dengan cara yang membuat lebih sulit atau tidak mungkin bagi konsumen untuk membuat kesalahan. Misalnya, alih-alih memiliki
MediaPlayer.play()
danMediaPlayer.stop()
, Anda dapat memberikanMediaPlayer.playToggle()
yang beralih antara "berhenti" dan "bermain". Dengan cara ini, metode ini selalu aman untuk dipanggil - tidak ada risiko masuk ke kondisi ilegal.Tentu saja, ini tidak selalu mungkin atau mudah dilakukan. Contoh yang Anda berikan sebanding dengan mencoba menghapus elemen yang sudah dihapus dari daftar. Anda bisa jadi
if (list.contains(x)) { list.remove(x) }
Anda hanya perlulist.remove(x)
). Tapi, itu juga bisa menyembunyikan bug.Jika menelepon
MediaPlayer.stop()
ketika sudah berhenti tidak ada salahnya dalam aplikasi Anda, maka saya akan membiarkannya berjalan diam-diam karena menyederhanakan kode dan saya punya sesuatu untuk metode idempoten. Tetapi jika Anda benar-benar yakin bahwaMediaPlayer.stop()
tidak akan pernah dipanggil dalam kondisi itu, maka saya akan membuat kesalahan karena mungkin ada bug di tempat lain dalam kode dan pengecualian akan membantu melacaknya.sumber
playToggle()
menjadi lebih mudah digunakan: Untuk mencapai efek yang diinginkan (pemain bermain atau tidak bermain), Anda akan diminta untuk mengetahui keadaan saat ini. Jadi, jika Anda mendapatkan input pengguna yang meminta Anda untuk menghentikan pemain, Anda harus terlebih dahulu menanyakan apakah pemain saat ini bermain, kemudian beralih statusnya atau tidak tergantung pada jawabannya. Itu jauh lebih rumit dari sekadar memanggilstop()
metode dan menyelesaikannya.playToggle
akan sangat rumit untuk digunakan. Tetapi jika pengguna juga diberi tombol sakelar, makaplayToggle
akan lebih mudah digunakan daripada bermain / berhenti.