การทำสำเนารหัสมายา


56

สัญชาตญาณปกติคือการลบการทำสำเนารหัสใด ๆ ที่คุณเห็นในรหัส แต่ผมพบว่าตัวเองอยู่ในสถานการณ์ที่การทำสำเนาเป็นภาพลวงตา

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

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

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

ปัญหาคือ:

  • ความแตกต่างอยู่ในนาทีที่โค้ดมีลักษณะเหมือนกันเกือบทุกมุมมอง
  • มีความแตกต่างมากมายที่เมื่อคุณดูรายละเอียดรหัสไม่เหมือนกัน

ฉันควรจัดการกับสถานการณ์นี้อย่างไร
การมีตรรกะหลักประกอบด้วยการโทรกลับเป็นโซลูชันที่ดีหรือไม่?
หรือฉันควรทำซ้ำรหัสและวางความซับซ้อนของรหัสตามโทรกลับ


38
ฉันมักจะพบว่ามีประโยชน์ในการปล่อยให้การทำสำเนาเริ่มต้น เมื่อฉันมีตัวอย่างเล็ก ๆ น้อย ๆ มันจะง่ายขึ้นมากที่จะเห็นว่ามีอะไรที่เหมือนกันและอะไรที่ไม่ได้เกิดขึ้นและมีวิธีการแบ่งปันส่วนที่ใช้ร่วมกัน
Winston Ewert

7
คำถามที่ดีมาก - สิ่งหนึ่งที่ควรพิจารณาไม่ใช่แค่การทำซ้ำทางกายภาพของรหัส แต่เป็นการทำซ้ำทางความหมาย หากการเปลี่ยนแปลงในรหัสชิ้นเดียวจำเป็นต้องหมายความว่าการเปลี่ยนแปลงเดียวกันจะถูกทำซ้ำในส่วนอื่น ๆ ดังนั้นส่วนนั้นอาจเป็นตัวเลือกสำหรับการปรับโครงสร้างหรือสิ่งที่เป็นนามธรรม บางครั้งคุณสามารถทำให้เป็นมาตรฐานได้ถึงจุดที่คุณดักจับตัวจริงดังนั้นฉันจะพิจารณาความหมายเชิงปฏิบัติสำหรับการทำซ้ำตามความหมายที่แตกต่างกัน - พวกเขาอาจถูกเปรียบเทียบกับผลที่ตามมาของการพยายามขจัดความซ้ำซ้อน
Ant P

จำไว้ว่าคุณสามารถใช้รหัสซ้ำที่ทำสิ่งเดียวกันได้เท่านั้น หากแอปของคุณทำสิ่งต่าง ๆ บนหน้าจอที่แตกต่างกันแอปนั้นจะต้องทำการโทรกลับ ไม่มีไอซีเอสและหรือเกี่ยวกับมัน
corsiKa

13
กฎง่ายๆของฉันคือ: ถ้าฉันทำการเปลี่ยนแปลงรหัสในที่เดียวมันเป็นข้อผิดพลาดหรือไม่ถ้าฉันไม่ทำการเปลี่ยนแปลงแบบเดียวกันทุกที่อื่น? ถ้าเป็นเช่นนั้นมันเป็นการทำซ้ำที่ไม่ดี หากฉันไม่แน่ใจให้ไปกับสิ่งที่อ่านได้มากขึ้นในตอนนี้ ในตัวอย่างของคุณความแตกต่างของพฤติกรรมเป็นแบบตั้งใจและไม่ถือว่าเป็นข้อบกพร่องดังนั้นการทำซ้ำบางอย่างจึงใช้ได้
Ixrec

คุณอาจสนใจอ่านเกี่ยวกับ Aspect Oriented Programming
เบ็คแจ็คสัน

คำตอบ:


53

ท้ายที่สุดคุณต้องทำการเรียกวิจารณญาณว่าจะรวมรหัสที่คล้ายกันเพื่อกำจัดความซ้ำซ้อนหรือไม่

ดูเหมือนว่ามีแนวโน้มที่จะโชคร้ายที่ใช้หลักการเช่น "อย่าพูดซ้ำตัวเอง" เป็นกฎที่ต้องปฏิบัติตามด้วยการท่องจำตลอดเวลา ในความเป็นจริงกฎเหล่านี้ไม่ใช่กฎสากล แต่เป็นแนวทางที่จะช่วยให้คุณคิดและพัฒนาการออกแบบที่ดี

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

โดยไม่ทราบรหัสเฉพาะของคุณสิ่งนี้ไม่ชัดเจน อาจมีวิธีที่สง่างามกว่าในการลบการทำซ้ำ (เช่นที่ LindaJeanne แนะนำ) หรืออาจมีการทำซ้ำไม่เพียงพอที่จะรับประกันความเป็นนามธรรม

การใส่ใจในการออกแบบไม่เพียงพอเป็นข้อผิดพลาด แต่ยังต้องระวังมากกว่าการออกแบบ


ความคิดเห็นของคุณเกี่ยวกับ "แนวโน้มที่โชคร้าย" และฉันก็ปฏิบัติตามแนวทางอย่างสุ่มสี่สุ่มห้าฉันคิดว่า
Mael

1
@Mael คุณกำลังบอกว่าถ้าคุณไม่รักษารหัสนี้ในอนาคตคุณจะไม่มีเหตุผลที่ดีที่จะได้รับการออกแบบที่เหมาะสม? (ไม่มีความผิดเพียงแค่ต้องการรู้ว่าคุณคิดอย่างไรเกี่ยวกับเรื่องนั้น)
เห็น

2
@Melel แน่นอนว่าเราสามารถพิจารณาได้ว่าเป็นเพียงวลีที่โชคร้าย! : D อย่างไรก็ตามฉันคิดว่าเราควรเข้มงวดกับตัวเองเหมือนกันกับคนอื่น ๆ เมื่อเขียนโค้ด (ฉันถือว่าตัวเองเป็นคนอื่นเมื่อฉันอ่านโค้ดของตัวเอง 2 สัปดาห์หลังจากเขียน)
เห็น

2
@ user61852 ดังนั้นคุณจะไม่ชอบThe Codeeless Codeมาก
RubberDuck

1
@ user61852 ฮ่าฮ่า - แต่ถ้ามันไม่ทั้งหมดขึ้น (ข้อมูลที่ไม่ได้รับในคำถาม)? มีบางสิ่งที่จะเป็นประโยชน์น้อยกว่าความมั่นใจที่มากเกินไป

43

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

ชิ้นส่วนของความรู้อาจเป็นข้อเท็จจริง ("ค่าเบี่ยงเบนสูงสุดที่อนุญาตจากค่าที่ตั้งใจคือ 0.1%") หรืออาจเป็นส่วนหนึ่งของกระบวนการของคุณ ("คิวนี้ไม่เคยมีมากกว่าสามรายการ") มันเป็นข้อมูลชิ้นส่วนที่เข้ารหัสในซอร์สโค้ดของคุณ

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


12
นี้! โฟกัสของแห้งคือการหลีกเลี่ยงการเปลี่ยนแปลงที่ซ้ำกัน
Matthieu M.

สิ่งนี้มีประโยชน์มาก

1
ฉันคิดว่าจุดสำคัญของ DRY คือเพื่อให้แน่ใจว่าไม่มีโค้ดสองบิตที่ควรประพฤติตนเหมือนกัน แต่ไม่ใช่ ปัญหาไม่ได้เป็นสองเท่าเพราะการเปลี่ยนแปลงรหัสจะต้องมีการใช้สองครั้งปัญหาที่แท้จริงคือเมื่อการเปลี่ยนแปลงรหัสต้องมีการใช้สองครั้ง แต่ไม่ใช่
gnasher729

3
@ gnasher729 ใช่นั่นคือจุด หากโค้ดสองชิ้นมีความรู้ซ้ำซ้อนคุณควรคาดหวังว่าเมื่อจำเป็นต้องเปลี่ยนรหัสอื่นก็จะต้องเปลี่ยนเช่นกันนำไปสู่ปัญหาที่คุณอธิบาย หากพวกเขามีการทำซ้ำโดยบังเอิญจากนั้นเมื่อหนึ่งต้องเปลี่ยนอื่น ๆ อาจต้องอยู่เหมือนกัน ในกรณีนี้หากคุณแยกวิธีการทั่วไป (หรืออะไรก็ตาม) ตอนนี้คุณมีปัญหาที่แตกต่างกันในการจัดการกับ
Ben Aaronson

1
นอกจากนี้การทำสำเนาที่สำคัญและการทำซ้ำโดยไม่ตั้งใจดูDoppelgängerอุบัติเหตุใน Rubyและฉัน DRY-ed Up My Code และตอนนี้มันยากที่จะทำงานด้วย เกิดอะไรขึ้น? . ซ้ำกันอุบัติเหตุยังเกิดขึ้นในทั้งสองด้านของเขตแดนบริบท สรุป: เพียงผสานซ้ำกันถ้ามันทำให้รู้สึกสำหรับลูกค้าของพวกเขาสำหรับการอ้างอิงเหล่านี้เพื่อให้ได้รับการแก้ไขไปพร้อม ๆ กัน
Eric

27

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


5
ไม่ฉันไม่ได้พิจารณามัน ขอบคุณสำหรับคำแนะนำ จากการอ่านอย่างรวดเร็วเกี่ยวกับรูปแบบกลยุทธ์ดูเหมือนว่าสิ่งที่ฉันกำลังมองหา ฉันจะตรวจสอบเพิ่มเติมอย่างแน่นอน
Mael

3
มีรูปแบบวิธีการแม่แบบ คุณสามารถพิจารณาได้ว่า
Shakil

5

ศักยภาพในการเปลี่ยนแปลงมีอะไรบ้าง ตัวอย่างเช่นแอปพลิเคชันของเรามี 8 ส่วนธุรกิจที่แตกต่างกันซึ่งมีศักยภาพ 4 ประเภทผู้ใช้หรือมากกว่าสำหรับแต่ละพื้นที่ มุมมองที่กำหนดเองตามประเภทของผู้ใช้และพื้นที่

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

ตามที่ @ dan1111 พูดถึงมันสามารถลงมาสู่การพิจารณาคดี ในเวลาคุณอาจพบว่ามันใช้งานได้หรือไม่


2

ปัญหาหนึ่งอาจเกิดจากการที่คุณได้จัดเตรียมอินเทอร์เฟซ (อินเทอร์เฟซเชิงทฤษฎีไม่ใช่คุณสมบัติทางภาษา) ให้กับฟังก์ชันการทำงานระดับเดียวเท่านั้น:

A(a,b,c) //a,b,c are your callbacks or other dependencies

แทนที่จะมีหลายระดับขึ้นอยู่กับว่าต้องมีการควบคุมเท่าใด:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

เท่าที่ฉันเข้าใจคุณจะเปิดเผยเฉพาะอินเทอร์เฟซระดับสูง (A) ซึ่งซ่อนรายละเอียดการใช้งาน (สิ่งอื่น ๆ ที่นั่น)

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

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

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

ใช้วัตถุเดียวที่คุณต้องการควบคุมเพียงเล็กน้อย

ใช้ฟังก์ชั่นระดับต่ำสุดเมื่อความแปลกประหลาดบางอย่างเกิดขึ้น

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


1

มีคำตอบที่เป็นประโยชน์อื่น ๆ อยู่แล้ว ฉันจะเพิ่มของฉัน

การทำสำเนาไม่ดีเพราะ

  1. มัน clutters รหัส
  2. มันกระจุกรหัสของเรา แต่ที่สำคัญที่สุด
  3. เพราะถ้าคุณเปลี่ยนบางสิ่งที่นี่และคุณต้องเปลี่ยนบางสิ่งที่นั่นคุณสามารถลืม / แนะนำบั๊ก / .... และมันก็ยากที่จะไม่มีวันลืม

ดังนั้นประเด็นคือคุณไม่ได้กำจัดการทำซ้ำเพื่อประโยชน์ของมันหรือเพราะมีคนพูดว่ามันสำคัญ คุณทำเช่นนี้เพราะคุณต้องการลดข้อบกพร่อง / ปัญหา ในกรณีของคุณปรากฏว่าหากคุณเปลี่ยนบางสิ่งในมุมมองคุณอาจไม่จำเป็นต้องเปลี่ยนบรรทัดเดียวกันในมุมมองอื่นทั้งหมด เพื่อให้คุณมีการทำสำเนาชัดเจนทำซ้ำไม่ได้เกิดขึ้นจริง

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.