Refactoring Switch Statement และมีการใช้งานจริงสำหรับงบ Switch ใด ๆ ?


28

ฉันอ่านบทความนี้และสงสัยว่าเราจะกำจัดงบการเปลี่ยนทั้งหมดโดยการแทนที่พวกเขาด้วยพจนานุกรมหรือโรงงานเพื่อไม่ให้มีการเปลี่ยนงบเลยในโครงการของฉัน

มีบางอย่างไม่ได้เพิ่มขึ้น

คำถามคือคำสั่ง switch มีการใช้งานจริงหรือเราจะไปข้างหน้าและแทนที่ด้วยพจนานุกรมหรือวิธีการจากโรงงาน (ในการใช้วิธีการจากโรงงานแน่นอนว่าจะมีการใช้คำสั่งสวิตช์อย่างน้อยสำหรับการสร้างวัตถุ ใช้โรงงาน ... แต่มันเกี่ยวกับมัน)


10
สมมติว่าคุณใช้โรงงานคุณจะตัดสินใจเลือกวัตถุประเภทใด
CodeART

2
มีความเกี่ยวข้องมาก: programmers.stackexchange.com/questions/146771/…
pdr

@CodeWorks: แน่นอนว่าฉันจะต้องมีเงื่อนไขบางแห่งเพื่อตัดสินใจว่าจะใช้งานรูปแบบใดอย่างเป็นรูปธรรม
Kanini

@CodeWorks: ด้วยตัวสร้างเสมือนอย่างชัดเจน (และถ้าคุณไม่สามารถใช้รูปแบบโรงงานวิธีการที่คุณจะต้องเป็นภาษาที่ดีกว่า.)
เมสันล้อ

คำตอบ:


44

switchข้อความทั้งสองและความหลากหลายมีการใช้งานของพวกเขา โปรดทราบว่ามีตัวเลือกที่สามเช่นกัน (ในภาษาที่รองรับตัวชี้ฟังก์ชัน / lambdas และฟังก์ชันลำดับสูงกว่า): การแมปตัวระบุที่เป็นปัญหากับฟังก์ชันตัวจัดการ มีให้ในภาษา C ซึ่งไม่ใช่ภาษา OO และ C # คือ * แต่ไม่ (ยัง) ใน Java ซึ่งเป็น OO ด้วย *

ในบางภาษาขั้นตอน (ไม่มีความหลากหลายและฟังก์ชั่นลำดับสูงกว่า) switch/ if-elseคำสั่งเป็นวิธีเดียวที่จะแก้ปัญหาในชั้นเรียน นักพัฒนาหลายคนคุ้นเคยกับวิธีคิดนี้อย่างต่อเนื่องswitchแม้จะใช้ในภาษา OO ซึ่งความแตกต่างมักจะเป็นทางออกที่ดีกว่า นี่คือเหตุผลว่าทำไมจึงมักแนะนำให้หลีกเลี่ยง / refactor switchงบเพื่อ polymorphism

ไม่ว่าจะด้วยวิธีใดก็ตามทางออกที่ดีที่สุดนั้นขึ้นอยู่กับแต่ละกรณี คำถามคือ: ตัวเลือกใดที่ช่วยให้คุณทำความสะอาดรหัสที่กระชับและบำรุงรักษาได้มากกว่าในระยะยาว?

คำสั่ง Switch มักจะเติบโตอย่างไม่น่าเชื่อมีหลายกรณีทำให้การบำรุงรักษาทำได้ยาก เนื่องจากคุณต้องเก็บไว้ในฟังก์ชั่นเดียวฟังก์ชั่นนั้นสามารถเติบโตได้อย่างมาก หากเป็นกรณีนี้คุณควรพิจารณาปรับโครงสร้างแผนที่และ / หรือ polymorphic

หากswitchเริ่มเดียวกันปรากฏขึ้นในหลายสถานที่ความหลากหลายอาจเป็นตัวเลือกที่ดีที่สุดในการรวมกรณีเหล่านี้ทั้งหมดและลดความซับซ้อนของรหัส โดยเฉพาะอย่างยิ่งหากคาดว่าจะมีกรณีเพิ่มเติมอีกในอนาคต สถานที่ที่คุณต้องการอัปเดตในแต่ละครั้งยิ่งมีโอกาสมากขึ้นสำหรับข้อผิดพลาด อย่างไรก็ตามบ่อยครั้งที่ตัวจัดการเคสส่วนบุคคลนั้นเรียบง่ายหรือมีจำนวนมากหรือมีความสัมพันธ์กันอย่างมากการปรับโครงสร้างให้เป็นลำดับชั้นของโพลีมอร์ฟิคเต็มรูปแบบนั้นมากเกินไปหรือส่งผลให้มีรหัสซ้ำกัน ยากที่จะรักษาลำดับชั้นของชั้นเรียน ในกรณีนี้อาจใช้ฟังก์ชั่น / lambdas แทน (ถ้าภาษาของคุณอนุญาตให้คุณ)

อย่างไรก็ตามหากคุณมีswitchสถานที่แห่งเดียวโดยมีเพียงไม่กี่กรณีที่ทำสิ่งที่ง่ายมันอาจเป็นทางออกที่ดีที่สุดที่จะปล่อยให้มันเป็นไปได้

* ฉันใช้คำว่า "OO" ที่นี่อย่างหลวม ๆ ฉันไม่สนใจการอภิปรายเชิงแนวคิดเกี่ยวกับ OO "ของจริง" หรือ "บริสุทธิ์"


4
+1 นอกจากนี้คำแนะนำเหล่านั้นมีแนวโน้มที่จะใช้คำว่า " ชอบความหลากหลายในการสลับ" และนั่นเป็นตัวเลือกคำที่ดีมากในการตอบสนองต่อคำตอบนี้ ยอมรับว่ามีสถานการณ์ที่ผู้ทำรหัสที่รับผิดชอบอาจเลือกตัวเลือกอื่น
Carl Manaster

1
และมีบางครั้งที่คุณต้องการวิธีการสวิตช์แม้ว่าจะมีจำนวนของพวกเขาในรหัสของคุณ ฉันมีรหัสในใจที่สร้างเขาวงกต 3 มิติ การใช้หน่วยความจำจะเพิ่มขึ้นหากเซลล์หนึ่งไบต์ในอาร์เรย์ถูกแทนที่ด้วยคลาส
Loren Pechtel

14

นี่คือที่ฉันกลับไปเป็นไดโนเสาร์ ...

งบสวิตช์ไม่ได้เลวร้ายในตัวมันเองการใช้งานที่ทำให้พวกเขาเป็นปัญหา

สิ่งที่ชัดเจนที่สุดคือคำสั่งสลับ "ซ้ำกัน" ซ้ำแล้วซ้ำอีกซ้ำ ๆ ผ่านรหัสของคุณที่ไม่ดี (เคยทำมาแล้วจะทำให้ทุกความพยายามไม่ทำอีก) - และในกรณีหลังนี้ซึ่งคุณสามารถจัดการได้ ด้วยการใช้ความหลากหลาย มักจะมีบางสิ่งที่ค่อนข้างน่าสยดสยองเช่นกันกับกรณีที่ซ้อนกัน (ฉันเคยมีสัตว์ประหลาดแน่นอน - ทั้งหมดไม่แน่ใจว่าฉันจัดการกับมันตอนนี้นอกเหนือจาก "ดีกว่า")

พจนานุกรมในฐานะสวิตช์ฉันพบความท้าทายมากขึ้น - พื้นฐานใช่ถ้าสวิตช์ของคุณครอบคลุม 100% ของคดี แต่ที่คุณต้องการให้มีค่าเริ่มต้นหรือไม่มีกรณีการกระทำมันเริ่มน่าสนใจขึ้นเล็กน้อย

ฉันคิดว่ามันเป็นคำถามของการหลีกเลี่ยงการทำซ้ำและทำให้แน่ใจว่าเรากำลังเขียนกราฟวัตถุของเราในสถานที่ที่เหมาะสม

แต่ยังมีข้อโต้แย้ง (การบำรุงรักษา) และการตัดทั้งสองวิธี - เมื่อคุณเข้าใจว่ามันทำงานอย่างไร (รูปแบบและแอพที่ใช้งาน) มันง่าย ... แต่ถ้าคุณมาที่โค้ดบรรทัดเดียวที่คุณ ต้องเพิ่มสิ่งใหม่ ๆ จากนั้นคุณต้องข้ามสถานที่เพื่อหาสิ่งที่คุณต้องการเพิ่ม / เปลี่ยน

สำหรับทุกสิ่งที่สภาพแวดล้อมการพัฒนาของเรามีความสามารถสูงฉันยังคงรู้สึกว่าเป็นที่พึงปรารถนาที่จะสามารถเข้าใจโค้ด (ราวกับว่ามันเป็น) ที่พิมพ์ออกมาบนกระดาษ - คุณสามารถทำตามโค้ดด้วยนิ้วของคุณได้หรือไม่? ฉันยอมรับว่าไม่จริงด้วยการฝึกฝนที่ดีมากมายในวันนี้คุณทำไม่ได้และด้วยเหตุผลที่ดี แต่นั่นหมายความว่าการเริ่มต้นใช้งานโค้ดที่เร็วขึ้นนั้นยากที่จะเริ่มต้น (หรืออาจเป็นเพราะฉันแก่)


4
ฉันมีครูคนหนึ่งที่บอกว่าควรเขียนโค้ดที่ดีเลิศ "เพื่อคนที่ไม่รู้อะไรเลยเกี่ยวกับการเขียนโปรแกรม (อาจเป็นแม่ของคุณ) สามารถเข้าใจได้" น่าเสียดายที่มันเป็นอุดมคติ แต่บางทีเราควรรวมแม่ของเราไว้ในบทวิจารณ์โค้ดต่อไป!
Michael K

+1 สำหรับ "แต่ถ้าคุณมาที่บรรทัดเดียวของรหัสที่คุณต้องเพิ่มสิ่งใหม่คุณต้องข้ามไปทั่วสถานที่เพื่อหาสิ่งที่คุณต้องเพิ่ม / เปลี่ยน"
quick_now

8

สวิทช์งบเทียบกับชนิดย่อย polymorphism เป็นปัญหาเก่าและมักจะเรียกกันในชุมชน FP ในการอภิปรายเกี่ยวกับปัญหาการแสดงออก

โดยทั่วไปเรามีประเภท (คลาส) และฟังก์ชั่น (วิธีการ) เราจะเขียนโค้ดเพื่อให้ง่ายต่อการเพิ่มประเภทใหม่หรือวิธีการใหม่ในภายหลังได้อย่างไร

หากคุณโปรแกรมในรูปแบบ OO มันยากที่จะเพิ่มวิธีการใหม่ (เพราะนั่นจะหมายถึงการปรับโครงสร้างคลาสที่มีอยู่ทั้งหมด) แต่มันง่ายมากที่จะเพิ่มคลาสใหม่ที่ใช้วิธีการเดียวกับก่อนหน้านี้

ในทางกลับกันถ้าคุณใช้คำสั่ง switch (หรือเทียบเท่า OO, รูปแบบการสังเกตการณ์) แล้วมันง่ายมากที่จะเพิ่มฟังก์ชั่นใหม่ แต่มันยากที่จะเพิ่มเคส / คลาสใหม่

มันไม่ง่ายเลยที่จะมีความสามารถในการขยายที่ดีทั้งสองทิศทางดังนั้นเมื่อเขียนโค้ดของคุณให้ตรวจสอบว่าใช้ polymorphism หรือ switch statement ขึ้นอยู่กับทิศทางที่คุณมีแนวโน้มที่จะขยายต่อหรือไม่


4
เราจะกำจัดงบสวิตช์ทั้งหมดโดยแทนที่ด้วย Dictionary หรือ Factory เพื่อที่จะไม่มีคำสั่ง switch ในโครงการของฉัน

ไม่ได้ข้อสรุปดังกล่าวเป็นแนวคิดที่ไม่ค่อยดีนัก

สถานที่หลายแห่งการจัดส่งพจนานุกรม / ค้นหา / โรงงาน / polymorphic จะให้การออกแบบที่ดีกว่าคำสั่งสลับ แต่คุณยังคงต้องเติมพจนานุกรม ในบางกรณีที่จะปิดบังสิ่งที่เกิดขึ้นจริงและการมีคำสั่งสวิตช์ในบรรทัดนั้นสามารถอ่านได้และบำรุงรักษาได้มากกว่า


และถ้าคุณเปิดโรงงานและพจนานุกรมและการค้นหามันจะกลายเป็นคำสั่ง switch: ถ้า array [hash (ค้นหา)] แล้วเรียก array [hash (ค้นหา)] แม้ว่ามันจะสามารถขยายได้รันไทม์ซึ่งสวิตช์รวบรวมไม่ได้
Zan Lynx

@ZanLynx ตรงกันข้ามที่สมบูรณ์เป็นจริงอย่างน้อยกับ C # ถ้าคุณดูที่ IL ที่สร้างขึ้นสำหรับคำสั่งสวิตช์ที่ไม่น่าสนใจคุณจะเห็นคอมไพเลอร์เปลี่ยนมันเป็นพจนานุกรมมันเร็วกว่า
David Arno

@DavidArno: "ตรงกันข้ามกัน"? วิธีที่ฉันอ่านมันเราพูดเหมือนกันทุกประการ มันจะตรงกันข้ามได้อย่างไร?
Zan Lynx

2

การเห็นว่านี่เป็นวิธีที่ไม่เชื่อเรื่องภาษารหัสแบบหล่นผ่านทำงานได้ดีที่สุดกับswitchข้อความ:

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

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

เทียบเท่ากับifกับกรณีเริ่มต้นที่น่าอึดอัดใจมาก และโปรดจำไว้ว่ายิ่งมีการตกลงมามากเท่าไหร่รายการเงื่อนไขก็จะยิ่งยาวขึ้นเท่านั้น:

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

(อย่างไรก็ตามด้วยคำตอบอื่น ๆ ที่บอกว่าฉันรู้สึกว่าฉันพลาดประเด็นไปแล้ว ... )


ฉันจะถือว่าการใช้งานของฤดูใบไม้ร่วงที่ผ่านงบในฐานะที่เป็นอยู่ในระดับเดียวกับswitch gotoหากอัลกอริทึมที่จะดำเนินการจำเป็นต้องมีโฟลว์ควบคุมที่เหมาะสมกับโครงสร้างการเขียนโปรแกรมที่มีโครงสร้างคุณควรใช้โครงสร้างดังกล่าว แต่หากอัลกอริทึมไม่เหมาะกับโครงสร้างดังกล่าวการใช้gotoอาจดีกว่าการพยายามเพิ่มแฟล็กหรือตรรกะการควบคุมอื่น ๆ .
supercat

1

สลับคำแถลงเนื่องจากการแยกความแตกต่างของเคสมีการใช้งานจริง ภาษาโปรแกรมที่ใช้งานได้ใช้สิ่งที่เรียกว่าการจับคู่รูปแบบซึ่งช่วยให้คุณสามารถกำหนดฟังก์ชั่นที่แตกต่างกันขึ้นอยู่กับอินพุต อย่างไรก็ตามในภาษาเชิงวัตถุเป้าหมายเดียวกันนั้นสามารถเข้าถึงได้มากขึ้นอย่างงดงามโดยใช้ polymorphism คุณเพียงแค่เรียกเมธอดและขึ้นอยู่กับชนิดของวัตถุที่เกิดขึ้นจริงการใช้งานที่สอดคล้องกันของวิธีการจะถูกดำเนินการ นี่คือเหตุผลที่อยู่เบื้องหลังความคิดที่ว่างบเปลี่ยนเป็นกลิ่นรหัส อย่างไรก็ตามแม้ในภาษา OO คุณอาจพบว่ามีประโยชน์ในการใช้รูปแบบนามธรรมจากโรงงาน

tl; dr - มันมีประโยชน์ แต่บ่อยครั้งที่คุณสามารถทำได้ดีกว่า


2
ฉันได้กลิ่นอคติบางอย่างที่นั่น - ทำไมความแตกต่างหลากหลายมากกว่าการจับคู่รูปแบบ และอะไรทำให้คุณคิดว่า polymorphism ไม่มีอยู่ใน FP
tdammers

@tammers ก่อนอื่นฉันไม่ได้ตั้งใจจะแปลว่า polymorphism ไม่มีอยู่ใน FP Haskell รองรับ ad hoc และตัวแปรหลากหลาย การจับคู่รูปแบบเหมาะสมสำหรับประเภทข้อมูลเกี่ยวกับพีชคณิต แต่สำหรับโมดูลภายนอกสิ่งนี้จะต้องเปิดเผยคอนสตรัคเตอร์ เห็นได้ชัดว่านี่เป็นการแบ่ง encapsulation ของชนิดข้อมูลนามธรรม (รับรู้ด้วย datatypes พีชคณิต) นั่นคือเหตุผลที่ฉันรู้สึกว่า polymorphism นั้นสง่างามกว่า (และเป็นไปได้ใน FP)
scarfridge

คุณลืมความแตกต่างผ่าน typeclasses และอินสแตนซ์
tdammers

คุณเคยได้ยินปัญหาการแสดงออกหรือไม่ OO และ FP นั้นดีกว่าในสถานการณ์ต่าง ๆ ถ้าคุณหยุดคิด
hugomg

@tammers คุณคิดว่ามีความแตกต่างกันแบบไหนเมื่อพูดถึง ad hoc polymorphism? Ad Hoc polymorphism ได้รับการยอมรับใน Haskell ผ่านคลาสชนิด คุณจะบอกว่าฉันลืมได้อย่างไร เพียงแค่ไม่จริง
scarfridge
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.