การมีเพศสัมพันธ์ของรหัสแนะนำโดย DRY และ OOD


14

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

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

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

หนึ่งจะสมดุลระหว่าง DRY และการมีเพศสัมพันธ์รหัสที่ไม่พึงประสงค์ได้อย่างไร


1
เป็นเรื่องที่ดีเสมอที่จะไม่ปฏิบัติตามกฎเกณฑ์อย่างสุ่มสี่สุ่มห้า แต่ให้ฝึกสมองก่อน
gnasher729

ยุติธรรมเพียงพอ - แนวทางปฏิบัติไม่ใช่กฎ
2549686

คุณได้อ่านหน้า Wiki บน DRY (หรือ OAOO ซึ่งเคยเป็นที่รู้จักกันในนาม) หรือไม่? wiki.c2.com/?OnceAndOnlyOnceนั่นคือสิ่งที่มีการพูดคุยครั้งแรกเกิดขึ้นมากมาย (ที่จริงแล้ว Ward คิดค้น Wiki เป็นพิเศษสำหรับการอภิปรายเกี่ยวกับรูปแบบและการปฏิบัติ)
Jörg W Mittag

คำตอบที่เกี่ยวข้องมากแม้ว่าคำถามจะไม่เหมือนซ้ำกัน: softwareengineering.stackexchange.com/questions/300043/…
Hulk

คำตอบ:


8

ช่วยบอกว่าฉัน refactored รหัสที่ซ้ำกันในลำดับชั้น "น้ำมัน" และลำดับชั้น "ไฟฟ้า" ทั้งสองลงมาจากลำดับชั้น "ยานพาหนะ" จนถึงตอนนี้ดีมาก

จากนั้น บริษัท ของฉันแนะนำรถยนต์ไฮบริดและไฮบริดกึ่งซึ่งจะต้องมีการเปลี่ยนแปลงหลักในลำดับชั้นดั้งเดิมของฉันเอง อาจจะต้องมี "องค์ประกอบ" ระหว่างน้ำมันเบนซินและลำดับชั้นไฟฟ้า

ฉันคิดว่านี่เป็นหนึ่งในเหตุผลหลักที่ผู้คนกำลังเคลื่อนไปสู่การจัดองค์ประกอบมากกว่าการสืบทอด

มรดกบังคับให้คุณเข้าสู่การปรับโครงสร้างครั้งใหญ่เมื่อคุณมีการเปลี่ยนแปลงแนวคิดเช่นเดียวกับที่คุณอธิบาย

เมื่อการปรับโครงสร้างนั้นใหญ่เกินไปหรือยากเกินไปผู้คนจะเขียนรหัสซ้ำเพื่อหลีกเลี่ยง

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

ไม่ว่าการออกแบบที่เกิดขึ้นจะเป็น OOP หรือไม่ก็เปิดให้มีการอภิปรายกันได้


1
+1 สำหรับการชี้ให้เห็นว่าการสืบทอดเป็นปัญหาไม่ใช่ DRY วิธีที่ง่ายที่สุดในการใช้รหัสซ้ำคือการลบการพึ่งพาที่ไม่จำเป็นออกและบ่อยครั้งที่ผู้คนมักพลาดความจริงที่ว่าระดับผู้ปกครอง (และผู้สืบทอด) เป็นสิ่งที่ต้องพึ่งพาทั้งหมดเมื่อใช้การสืบทอด เฉพาะเมื่อคุณลดหรือกำจัดการพึ่งพาคุณจะได้รับรหัสที่สามารถใช้ซ้ำได้
Greg Burghardt

3
" การออกแบบที่ได้นั้นเป็นผลของ OOP หรือไม่ก็เปิดให้มีการถกเถียงกัน " ทุกอย่างเปิดให้มีการอภิปราย คำถามที่ถามคือเปิดการอภิปรายที่สมเหตุสมผลหรือไม่? คำตอบคือไม่ ฉันคิดว่าย่อหน้าสุดท้ายของคุณเบี่ยงเบนจากคำตอบที่ดีมาก
David Arno

@davidarno ไม่ได้ติดตามคุณจริงๆฉันอ่าน OOD ในชื่อเรื่อง Object Orientated Design ใช่ไหม ดังนั้นฉันจึงตอบคำถามโดยไม่ต้องสืบค้นผ่านการอภิปราย
Ewan

1
@Ewan: ฉันอยู่ที่นี่กับ David Arno ฉันคิดว่ามันเป็นที่รู้จักกันดีมานานกว่า 2 ทศวรรษที่เชื่อว่า"ถ้าไม่มีมรดกแล้วมันไม่ใช่ OOP"คือการเข้าใจผิด
Doc Brown

jeeze นี่เป็นการสนทนาที่ฉันพยายามหลีกเลี่ยง มีมุมมองทั้งสองด้านไม่มีคำตอบที่ชัดเจน
386 Ewan

3

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

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

ให้เราสมมติว่า A และ B นั้นไม่เกี่ยวข้องกันมาก่อนพวกเขาสามารถทำเวอร์ชันปล่อยและปรับใช้อย่างอิสระ โดยการใส่รหัสทั่วไปลงใน lib L ที่ใช้ร่วมกันอย่างไรก็ตามข้อกำหนดใหม่สำหรับ A อาจทำให้เกิดการเปลี่ยนแปลง L ซึ่งตอนนี้อาจทำให้เกิดการเปลี่ยนแปลง B ดังนั้นสิ่งนี้ทำให้เกิดความต้องการการทดสอบเพิ่มเติมและอาจเป็นรุ่นใหม่และปรับใช้วงจรสำหรับ B

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

  1. เก็บ A, B และ L ไว้เป็นส่วนหนึ่งของผลิตภัณฑ์เดียวกันโดยมีหมายเลขเวอร์ชันทั่วไปหนึ่งเดียวคือบิลด์ทั่วไปกระบวนการรีลีสและการปรับใช้พร้อมกับระบบอัตโนมัติระดับสูง

  2. หรือทำให้ L เป็นผลิตภัณฑ์ของตัวเองโดยมีหมายเลขรุ่นรอง (ไม่มีการเปลี่ยนแปลงที่เข้ากันไม่ได้) และหมายเลขรุ่นหลัก (อาจมีการเปลี่ยนแปลงที่แตกหัก) และให้ A และ B อนุญาตให้แต่ละคนอ้างอิงสายรุ่น L ที่แตกต่างกัน

  3. ทำให้ L เป็น SOLID มากที่สุดและดูแลความเข้ากันได้ย้อนหลัง ยิ่งโมดูลใน L สามารถนำกลับมาใช้ใหม่ได้โดยไม่ต้องมีการดัดแปลง (OCP) การเปลี่ยนแปลงที่เกิดขึ้นจะมีโอกาสน้อยลง และหลักการอื่น ๆ ใน "SOLID" กำลังช่วยสนับสนุนเป้าหมายนั้น

  4. ใช้การทดสอบอัตโนมัติโดยเฉพาะสำหรับ L แต่สำหรับ A และ B

  5. ระวังสิ่งที่คุณใส่ลงในตรรกะทางธุรกิจของแอลซึ่งควรมีอยู่ในที่เดียวเท่านั้นในระบบคือตัวเลือกที่ดี สิ่งที่ "ดูคล้าย" และอาจแตกต่างกันในอนาคตคือผู้สมัครที่ไม่ดี

หมายเหตุเมื่อ A และ B ได้รับการพัฒนาบำรุงรักษาและพัฒนาโดยทีมอื่นที่ไม่เกี่ยวข้องหลักการ DRY มีความสำคัญน้อยกว่า - DRY เกี่ยวข้องกับการบำรุงรักษาและการพัฒนา แต่การให้สองทีมที่แตกต่างกันทำให้การบำรุงรักษาแต่ละครั้งมีประสิทธิภาพมากกว่าการผูกผลิตภัณฑ์ ด้วยกันเพราะความคิดเล็กน้อย

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


1

DRY เป็นกฎโครงสร้างอีกข้อหนึ่ง นั่นหมายความว่าถ้าคุณนำมันไปให้สุดขั้วมันจะแย่เหมือนคุณเพิกเฉย นี่คืออุปมาที่จะพาคุณออกไปจากความคิดเชิงโครงสร้าง

รักฝาแฝดของคุณ ฆ่าโคลน

เมื่อคุณคัดลอกและวางเพื่อหลีกเลี่ยงการพิมพ์แป้นพิมพ์คุณสุ่มสร้างโคลน แน่นอนว่าพวกเขาทำในสิ่งที่คุณต้องการ แต่พวกเขาทำให้คุณรู้สึกแย่เพราะตอนนี้การเปลี่ยนพฤติกรรมนั้นแพงมาก

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

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

สมมติว่าคุณทำงานให้กับ บริษัท ABC มันบริหารงานโดย บริษัท สามแห่งคือ A, B และ C พวกเขาทุกคนต้องการให้คุณสร้างผลิตภัณฑ์ซอฟต์แวร์ แต่พวกเขาแต่ละคนมีแผนกของตัวเอง พวกเขาต้องการให้คุณเริ่มต้นเล็ก ๆ เพียงแค่ส่งข้อความอย่างง่ายไปก่อน คุณเขียนว่า "Hello World"

เกลียดมันต้องการให้คุณใส่ชื่อ บริษัท ที่นั่น

B รักมันอยากให้คุณทิ้งไว้คนเดียวและเพิ่มชื่อ บริษัท ลงในหน้าจอเริ่มต้น

C แค่ต้องการเครื่องคิดเลขและคิดว่าข้อความเป็นเพียงวิธีที่คุณสามารถเริ่มต้นได้

สิ่งหนึ่งที่คุณแน่ใจพวกเขามีแนวคิดที่แตกต่างกันมากเกี่ยวกับโครงการนี้ บางครั้งพวกเขาก็จะเห็นด้วย บางครั้งพวกเขาก็จะดึงคุณไปในทิศทางที่แตกต่างกัน

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

A, B และ C เป็นผู้เชี่ยวชาญที่แตกต่างกันซึ่งโค้ดของคุณต้องแสดง ไม่มีรหัสหนึ่งบรรทัดที่สามารถให้บริการได้มากกว่าหนึ่งต้นแบบเป็นเวลานาน


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