การตีความหลักการ DRY


10

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

createTrajectoryFromPoint(A a,B b,C c,boolean doesSomething,boolean doesSomething2)

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

ดังนั้นคำถามของฉันคือดีกว่าที่จะมีฟังก์ชั่นต่าง ๆ มากมายที่ใช้ตรรกะเดียวกันจำนวนมาก (ดังนั้นละเมิดหลักการ DRY) หรือฟังก์ชั่นหนึ่งที่ทำงานแตกต่างกันเล็กน้อยตามจำนวนพารามิเตอร์ แต่ทำให้ซับซ้อนมากขึ้น (แต่ รักษา DRY) หรือไม่


3
ตรรกะแบบแบ่งใช้ / ทั่วไปสามารถแยกออกเป็นฟังก์ชันส่วนตัวที่ฟังก์ชั่นสาธารณะที่แตกต่างกันcreateTrajectory...สามารถโทรได้หรือไม่
FrustratedWithFormsDesigner

อาจเป็นได้ แต่ฟังก์ชั่นส่วนตัวเหล่านั้นยังคงต้องรับพารามิเตอร์บูลีนเหล่านั้นอีกด้วย
Albinoswordfish

2
ฉันคิดว่าสิ่งนี้จะเป็นคำตอบที่ง่ายกว่ามากสำหรับตัวอย่างที่เป็นรูปธรรม ปฏิกิริยาของฉันทันทีคือการแบ่งขั้วที่คุณกำลังสื่อให้เห็นไม่ใช่ของจริงทั้งหมด - นั่นคือนั่นไม่ใช่สองทางเลือกเท่านั้น นอกจากนี้ฉันจะพิจารณาการใช้ a booleanเป็นพารามิเตอร์ที่น่าสงสัยที่สุด
Jerry Coffin


เอ่อทำไมคุณไม่แยกแยะสิ่งที่มีเงื่อนไขในการทำงานของตัวเอง?
Rig

คำตอบ:


19

ข้อโต้แย้งแบบบูลจะเรียกรหัสเส้นทางที่แตกต่างกันในฟังก์ชั่นเดียว / วิธีการเป็นกลิ่นรหัสสาหัส

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

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

โดยการละเว้นของคุณเองนี่คือการรวมกันแน่นเกินไป (ธงบูลีนทั้งหมดเป็นประเภทของการพึ่งพาของรัฐซึ่งเป็นหนึ่งในที่เลวร้ายที่สุด!) และมีความรับผิดชอบส่วนบุคคลมากเกินไป intermingled (ซับซ้อนเกินไป)

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

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


ตกลงดูเหมือนว่าจะเป็นวิธีที่ฉันเขียนไม่ดีมาก (ความสงสัยเริ่มต้นของฉัน) ฉันไม่แน่ใจจริงๆว่า 'Sprit of DRY' นี้คืออะไร
Albinoswordfish


4

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

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

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

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


3

ทำไมคุณไม่สร้างฟังก์ชั่นอื่นที่มีตรรกะทั้งหมดในฟังก์ชั่นของคุณก่อนที่คุณจะตัดสินใจทำบางสิ่งบางอย่างหรือบางสิ่งบางอย่าง 2 จากนั้นมีฟังก์ชั่นสามอย่างดังนี้:

createTrajectoryFromPoint(A a,B b,C c){...}

dosomething(A a, B b, C c){...}

dosomething2(A a, B b, C c){...}

และตอนนี้ด้วยการส่งพารามิเตอร์ชนิดเดียวกันสามชนิดไปยังฟังก์ชันที่แตกต่างกันสามฟังก์ชันคุณจะทำซ้ำตัวเองอีกครั้งดังนั้นคุณควรกำหนด struct หรือคลาสที่มี A, B, C

หรือคุณสามารถสร้างคลาสที่มีพารามิเตอร์ A, B, C และรายการการดำเนินการที่ต้องทำ เพิ่มการดำเนินการใด (บางสิ่งบางอย่าง 2) ที่คุณต้องการให้เกิดขึ้นกับพารามิเตอร์เหล่านี้ (A, B, C) โดยการลงทะเบียนการดำเนินการกับวัตถุ จากนั้นมีวิธีเรียกการดำเนินการที่ลงทะเบียนทั้งหมดบนวัตถุของคุณ

public class MyComplexType
{
    public A a{get;set;}
    public B b{get;set;}
    public C c{get;set;}

    public delegate void Operation(A a, B b, C c);
    public List<Operation> Operations{get;set;}

    public MyComplexType(A a, B b, C c)
    {
        this.a = a;
        this.b = b;
        this.c = c   
        Operations = new List<Operation>();
    }

    public CallMyOperations()
    {
        foreach(var operation in Operations)
        {
            operation(a,b,c);
        }
    }
}

ในการอธิบายถึงการรวมกันของค่าที่เป็นไปได้สำหรับ booleans dosomething และ dosomething2 คุณจะต้องมี 4 ฟังก์ชันไม่ใช่ 2 นอกเหนือจากฟังก์ชั่น createTrajectoryFromPoint พื้นฐาน วิธีการนี้ไม่ได้ปรับขนาดตามจำนวนตัวเลือกที่เพิ่มขึ้นและแม้แต่การตั้งชื่อฟังก์ชั่นก็น่าเบื่อ
JGWeissman

2

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


1

ฉันมักจะผ่านหลายขั้นตอนกับปัญหานี้หยุดเมื่อไม่สามารถหาวิธีการเพิ่มเติม

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

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

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

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


0

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

createTrajectoryFromPoint(A a,B b,C c,IX x,IY y)

ที่คุณมีการใช้งานของ IX และ IY ที่แสดงถึงค่าที่แตกต่างกันสำหรับ booleans ในร่างกายของฟังก์ชั่นทุกที่ที่คุณมี

if (doesSomething)
{
     ...
}
else
{
     ...
}

คุณแทนที่ด้วยการเรียกไปยังวิธีการที่กำหนดไว้ใน IX ด้วยการใช้งานที่มีบล็อกรหัสที่ละเว้น

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