Refactoring Switch Statement dan apakah ada kegunaan nyata untuk Switch Statement?

28

Saya membaca artikel ini dan bertanya-tanya, apakah kita menyingkirkan semua pernyataan switch dengan menggantinya dengan Kamus atau Pabrik sehingga tidak ada pernyataan beralih sama sekali dalam proyek saya.

Sesuatu tidak bertambah.

Pertanyaannya adalah, apakah pernyataan switch memiliki penggunaan nyata atau kita melanjutkan dan menggantinya dengan kamus atau metode pabrik (dalam menggunakan metode pabrik, tentu saja, akan ada penggunaan minimum pernyataan switch untuk membuat objek. menggunakan pabrik ... tapi itu saja).

Kanini
sumber
10
Dengan asumsi Anda menerapkan pabrik, bagaimana Anda memutuskan jenis objek yang akan dibuat?
CodeART
@CodeWorks: Saya akan, tentu saja, memiliki kondisi di suatu tempat, memutuskan implementasi konkret mana yang akan digunakan.
Kanini
@ CodeWorks: Dengan konstruktor virtual, jelas. (Dan jika Anda tidak dapat menerapkan pola pabrik seperti itu, Anda memerlukan bahasa yang lebih baik.)
Mason Wheeler

Jawaban:

44

Kedua switchpernyataan dan polimorfisme memiliki penggunaannya. Perhatikan bahwa opsi ketiga juga ada (dalam bahasa yang mendukung fungsi pointer / lambdas, dan fungsi tingkat tinggi): memetakan pengidentifikasi yang dipertanyakan ke fungsi handler. Ini tersedia dalam misalnya C yang bukan bahasa OO, dan C # yang *, tetapi belum (belum) di Jawa yang juga OO *.

Dalam beberapa bahasa prosedural (tidak memiliki polimorfisme atau fungsi tingkat tinggi) switch/ if-elsepernyataan adalah satu-satunya cara untuk menyelesaikan kelas masalah. Begitu banyak pengembang, setelah terbiasa dengan cara berpikir seperti ini, terus menggunakan switchbahkan dalam bahasa OO, di mana polimorfisme seringkali merupakan solusi yang lebih baik. Inilah sebabnya mengapa sering direkomendasikan untuk menghindari / switchmenolak pernyataan yang mendukung polimorfisme.

Bagaimanapun, solusi terbaik selalu tergantung pada kasus. Pertanyaannya adalah: opsi mana yang memberi Anda kode yang lebih bersih, lebih ringkas, dan lebih terpelihara dalam jangka panjang?

Pernyataan pergantian sering kali menjadi sulit, memiliki lusinan kasus, membuat pemeliharaannya sulit. Karena Anda harus menyimpannya dalam satu fungsi, fungsi itu dapat tumbuh besar. Jika demikian, Anda harus mempertimbangkan refactoring ke arah solusi berbasis peta dan / atau polimorfik.

Jika hal yang sama switchmulai muncul di banyak tempat, polimorfisme mungkin merupakan pilihan terbaik untuk menyatukan semua kasus ini dan menyederhanakan kode. Terutama jika lebih banyak kasus diharapkan akan ditambahkan di masa depan; semakin banyak tempat yang Anda butuhkan untuk memperbarui setiap kali, semakin banyak kemungkinan kesalahan. Namun, sering kali penangan kasus individu sangat sederhana, atau ada begitu banyak dari mereka, atau mereka sangat terkait, sehingga refactoring mereka ke dalam hirarki kelas polimorfik penuh adalah berlebihan, atau menghasilkan banyak kode duplikat dan / atau kusut, sulit untuk mempertahankan hierarki kelas. Dalam hal ini, mungkin lebih mudah menggunakan fungsi / lambdas saja (jika bahasa Anda memungkinkan Anda).

Namun, jika Anda memiliki switchdi satu tempat, dengan hanya beberapa kasus melakukan sesuatu yang sederhana, itu mungkin merupakan solusi terbaik untuk membiarkannya seperti itu.

* Saya menggunakan istilah "OO" secara longgar di sini; Saya tidak tertarik pada perdebatan konseptual tentang apa yang "nyata" atau "murni" OO.

Péter Török
sumber
4
+1. Juga, rekomendasi-rekomendasi itu cenderung diucapkan, " lebih memilih polimorfisme untuk beralih" dan itu adalah pilihan kata yang baik, sangat sesuai dengan jawaban ini. Ini mengakui bahwa ada keadaan di mana seorang pembuat kode yang bertanggung jawab mungkin membuat pilihan lain.
Carl Manaster
1
Dan ada saat-saat ketika Anda menginginkan pendekatan pengalihan bahkan jika ada banyak trilyun di dalam kode Anda. Saya memiliki sepotong kode dalam pikiran yang menghasilkan labirin 3D. Penggunaan memori akan naik jika sel-sel satu byte dalam array diganti dengan kelas.
Loren Pechtel
14

Di sinilah saya kembali menjadi dinosaurus ...

Ganti pernyataan tidak buruk dalam diri mereka sendiri, penggunaannya yang membuat mereka yang dipermasalahkan.

Yang paling jelas adalah pernyataan pergantian "sama" berulang-ulang melalui kode Anda yang buruk (pernah ada, melakukan itu, akan melakukan segala upaya untuk tidak lagi) - dan ini kasus terakhir yang mungkin dapat Anda tangani dengan menggunakan polimorfisme. Biasanya ada sesuatu yang cukup mengerikan tentang kasus bersarang (saya dulu memiliki monster absolut - tidak sepenuhnya yakin bagaimana saya menghadapinya sekarang selain "lebih baik").

Kamus sebagai sakelar Saya merasa lebih menantang - pada dasarnya ya jika sakelar Anda mencakup 100% kasing, tetapi jika Anda ingin memiliki bawaan atau tidak ada kasing, sakelar ini mulai sedikit lebih menarik.

Saya pikir ini pertanyaan menghindari pengulangan dan memastikan kita menyusun grafik objek kita di tempat yang tepat.

Tetapi ada juga argumen pemahaman (pemeliharaan) dan ini memotong kedua cara - setelah Anda memahami bagaimana semuanya bekerja (pola dan aplikasi di mana penerapannya) mudah ... tetapi jika Anda datang ke satu baris kode di mana Anda harus menambahkan sesuatu yang baru Anda kemudian harus melompati semua tempat untuk mencari tahu apa yang perlu Anda tambahkan / ubah.

Untuk semua itu, lingkungan pengembangan kami mampu secara besar-besaran. Saya masih merasa perlu memahami kode (seolah-olah) dicetak di atas kertas - dapatkah Anda mengikuti kode dengan jari Anda? Saya menerima bahwa sebenarnya tidak, dengan banyak latihan yang baik hari ini Anda tidak dapat dan karena alasan yang baik tetapi itu berarti untuk mempercepat dengan kode lebih sulit untuk memulai (atau mungkin saya hanya tua ...)

Murph
sumber
4
Saya memiliki seorang guru yang mengatakan bahwa idealnya kode harus ditulis "sehingga seseorang yang tidak tahu apa-apa tentang pemrograman (seperti mungkin ibumu) dapat memahaminya." Sayangnya itu ideal, tapi mungkin kita harus memasukkan ibu kita dalam ulasan kode!
Michael K
+1 untuk "tetapi jika Anda sampai pada satu baris kode di mana Anda harus menambahkan sesuatu yang baru, maka Anda harus melompati semua tempat untuk mencari tahu apa yang perlu Anda tambahkan / ubah"
cepat_now
8

Switch-statement vs subtype-polymorphism adalah masalah lama dan sering disebut dalam komunitas FP dalam diskusi tentang Masalah Ekspresi .

Pada dasarnya, kami memiliki tipe (kelas) dan fungsi (metode). Bagaimana cara kita membuat kode hal-hal sehingga mudah untuk menambahkan tipe baru atau metode baru yang terakhir?

Jika Anda memprogram dalam gaya OO, sulit untuk menambahkan metode baru (karena itu berarti refactoring semua kelas yang ada), tetapi sangat mudah untuk menambahkan kelas baru yang menggunakan metode yang sama seperti sebelumnya.

Di sisi lain, jika Anda menggunakan pernyataan switch (atau yang setara dengan OO-nya, Pola Observer) maka sangat mudah untuk menambahkan fungsi-fungsi baru tetapi sulit untuk menambahkan kasus / kelas baru.

Tidaklah mudah untuk memiliki ekstensibilitas yang baik di kedua arah, jadi saat menulis kode Anda, tentukan apakah akan menggunakan polimorfisme atau beralih pernyataan tergantung pada arah mana Anda lebih cenderung memperluasnya.

hugomg
sumber
4
kita menyingkirkan semua pernyataan switch dengan menggantinya dengan Kamus atau Pabrik sehingga tidak ada pernyataan beralih sama sekali di proyek saya.

Tidak. Mutlak seperti itu jarang merupakan ide yang bagus.

Banyak tempat, pengiriman kamus / pencarian / pabrik / polimorfik akan memberikan desain yang lebih baik daripada pernyataan beralih tetapi Anda masih harus mengisi kamus. Dalam kasus-kasus tertentu, itu akan mengaburkan apa yang sebenarnya terjadi dan hanya memiliki pernyataan peralihan yang sejalan lebih mudah dibaca dan dikelola.

Telastyn
sumber
Dan sungguh, jika Anda membuka gulungan pabrik dan kamus dan pencarian, itu pada dasarnya akan menjadi pernyataan switch: jika array [hash (search)] maka panggil array [hash (search)]. Meskipun itu akan menjadi runtime-extended yang dikompilasi switch tidak.
Zan Lynx
@ ZanLynx, kebalikannya benar, setidaknya dengan C #. Jika Anda melihat IL yang diproduksi untuk pernyataan switch non-sepele, Anda akan melihat kompiler mengubahnya menjadi kamus. Karena itu lebih cepat.
David Arno
@ Davidvido: "kebalikan lengkap"? Cara saya membacanya kami mengatakan hal yang persis sama. Bagaimana bisa sebaliknya?
Zan Lynx
2

Melihat bagaimana ini bahasa-agnostik, kode fall-through paling baik digunakan dengan switchpernyataan:

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

Setara dengan if, dengan kasus default yang sangat canggung. Dan perlu diingat bahwa semakin banyak kegagalan yang terjadi, semakin lama daftar kondisi akan didapat:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(Namun, mengingat apa yang dikatakan beberapa jawaban lain, saya merasa saya telah melewatkan inti pertanyaan ...)

Izkata
sumber
Saya akan menganggap penggunaan pernyataan fall-through dalam switchberada pada level yang sama dengan a goto. Jika algoritma yang akan dilakukan memerlukan aliran kontrol yang sesuai dengan konstruksi pemrograman terstruktur, seseorang harus menggunakan konstruksi tersebut, tetapi jika algoritma tidak sesuai dengan konstruksi seperti itu, menggunakan gotomungkin lebih baik daripada mencoba menambahkan flag atau logika kontrol lainnya agar sesuai dengan struktur kontrol lainnya. .
supercat
1

Beralih pernyataan sebagai diferensiasi kasus memang memiliki kegunaan nyata. Bahasa pemrograman fungsional menggunakan sesuatu yang disebut pencocokan pola yang memungkinkan Anda untuk mendefinisikan fungsi secara berbeda tergantung pada input. Namun dalam bahasa berorientasi objek tujuan yang sama dapat dicapai lebih elegan dengan menggunakan polimorfisme. Anda cukup memanggil metode dan tergantung pada jenis objek yang sebenarnya, implementasi metode yang sesuai akan dieksekusi. Ini adalah alasan di balik gagasan itu, bahwa pernyataan switch adalah bau kode. Namun, bahkan dalam bahasa OO, Anda mungkin menemukan mereka berguna untuk menerapkan pola pabrik abstrak.

tl; dr - mereka berguna, tetapi cukup sering Anda dapat melakukan lebih baik.

scarfridge
sumber
2
Saya mencium beberapa bias di sana - mengapa polimorfisme lebih elegan daripada pencocokan pola? Dan apa yang membuat Anda berpikir polimorfisme tidak ada di FP?
Pelaku
@tdammers Pertama-tama saya tidak bermaksud mengatakan bahwa polimorfisme tidak ada di FP. Haskell mendukung polimorfisme ad hoc dan parametrik. Pencocokan pola masuk akal untuk tipe data aljabar. Tetapi untuk modul eksternal ini membutuhkan konstruktor yang terbuka. Jelas ini memecah enkapsulasi tipe data abstrak (direalisasikan dengan tipe data aljabar). Itu sebabnya saya merasa bahwa polimorfisme lebih elegan (dan mungkin di FP).
scarfridge
Anda melupakan polimorfisme melalui kacamata ketik dan mesin virtual.
Pelaku
Pernahkah Anda mendengar tentang Masalah Ekspresi ? OO dan FP lebih baik dalam situasi yang berbeda, jika Anda berhenti untuk memikirkannya.
hugomg
@tdammers Polimorfisme macam apa yang menurut Anda saya bicarakan ketika saya menyebutkan polimorfisme ad hoc? Polimorfisme ad hoc diwujudkan dalam Haskell melalui kelas tipe. Bagaimana Anda bisa mengatakan saya lupa? Sama sekali tidak benar.
scarfridge