ฟังก์ชันที่เรียกใช้ฟังก์ชันอื่นเท่านั้น นี่เป็นวิธีปฏิบัติที่ดีหรือไม่?


13

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

DataTable reportTable = new DataTable();
void RunReport()
{
    reportTable = DataClass.getReportData();
    largeReportProcessingFunction();
    outputReportToUser();
}

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

void largeReportProcessingFunction()
{
    processSection1HeaderData();
    calculateSection1HeaderAverages();
    formatSection1HeaderDisplay();
    processSection1SummaryTableData();
    calculateSection1SummaryTableTotalRow();
    formatSection1SummaryTableDisplay();
    processSection1FooterData();
    getSection1FooterSummaryTotals();
    formatSection1FooterDisplay();

    processSection2HeaderData();
    calculateSection1HeaderAverages();
    formatSection1HeaderDisplay();
    calculateSection1HeaderAverages();
    ...
}

หรือถ้าเราไปอีกขั้นหนึ่ง:

void largeReportProcessingFunction()
{
    callAllSection1Functions();

    callAllSection2Functions();

    callAllSection3Functions();
    ...        
}

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

คิด?


ในตอนท้ายของวัน ... มันสามารถอ่านได้มากขึ้น? ใช่มันเป็นทางออกที่ดีกว่า
sylvanaar

คำตอบ:


18

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

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

โปรดทราบว่านี่เป็นตัวเลือกที่ไม่ดีในการตั้งชื่อ:

void largeReportProcessingFunction() {
    callAllSection1Functions();
    callAllSection2Functions();
    callAllSection3Functions();
    ...        
}

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


6
ฉันเห็นด้วยกับอาจารย์ใหญ่ด้วยคำตอบนี้ยกเว้นฉันไม่เห็นว่า "processSection1" เป็นชื่อที่มีความหมายอย่างไร มันเทียบเท่ากับ "callAllSection1Functions" ทุกประการ
Eric King

2
@EricKing: การcallAllSection1Functionsรั่วไหลของรายละเอียดเกี่ยวกับการใช้งาน จากมุมมองภายนอกมันไม่สำคัญว่าprocessSection1งานของมันจะ "ฟังก์ชั่นส่วนที่ 1 ทั้งหมด" หรือไม่ว่ามันจะทำโดยตรงหรือว่ามันใช้ Pixie-dust เพื่อเรียกยูนิคอร์นที่จะทำงานจริง ทั้งหมดที่เป็นเรื่องจริงก็คือว่าหลังจากที่มีการเรียกร้องให้processSection1, section1สามารถพิจารณาการประมวลผล ฉันยอมรับว่าการประมวลผลเป็นชื่อสามัญ แต่ถ้าคุณมีการติดต่อกันสูง (ซึ่งคุณควรพยายามเสมอ) บริบทของคุณจะแคบพอที่จะทำให้เข้าใจผิด
back2dos

2
ทั้งหมดที่ฉันหมายถึงคือวิธีการที่เรียกว่า "processXXX" เป็นชื่อที่น่ากลัวและไร้ประโยชน์ ทุกสิ่ง 'โพรเซส' ของเมธอดดังนั้นการตั้งชื่อมันว่า 'processXXX' นั้นมีประโยชน์เกี่ยวกับการตั้งชื่อมันว่า 'doSomethingWithXXX' หรือ 'manipulateXXX' และที่คล้ายกัน มีชื่อที่ดีกว่าสำหรับวิธีการที่ชื่อว่า 'processXXX' เกือบทุกครั้ง จากมุมมองที่ใช้งานได้จริงมันมีประโยชน์ (ไร้ประโยชน์) เหมือนกับ 'callAllSection1Functions'
Eric King

4

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

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


3

มันขึ้นอยู่กับ. หากฟังก์ชั่นทั้งหมดที่คุณสร้างนั้นเป็นหน่วยงานเดียวจริง ๆ แล้วมันก็ใช้ได้ถ้าพวกมันเป็นแค่สาย 1-15, 16-30, 31-45 ของฟังก์ชั่นการโทรที่ไม่ดี ฟังก์ชั่นที่ยาวนานไม่ใช่ความชั่วร้ายหากพวกเขาทำสิ่งใดสิ่งหนึ่งถ้าคุณมีตัวกรอง 20 ตัวสำหรับรายงานของคุณที่มีฟังก์ชั่นตรวจสอบที่ตรวจสอบว่ายี่สิบตัวนั้นจะยาว แค่นี้ก็พอใช้แล้วการแยกโดยตัวกรองหรือกลุ่มของตัวกรองก็แค่เพิ่มความซับซ้อนเป็นพิเศษเพื่อประโยชน์ที่ไม่ได้เกิดขึ้นจริง

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


3
จากประสบการณ์ของฉันฟังก์ชั่นที่ยาวนานนั้นชั่วร้าย
เบอร์นาร์ด

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

3

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

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

พิจารณาการมีคลาส ReportBuilder ซึ่งคุณสามารถส่งผ่านข้อมูลรายงานได้ ถ้าคุณสามารถแยก ReportBuilder ออกเป็นคลาสอื่นให้ทำเช่นนี้


2
ขั้นตอนการให้บริการฟังก์ชั่นที่ดีอย่างสมบูรณ์
DeadMG

1

ใช่.

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

นอกจากนี้ยังช่วยทำให้วิธีการอ่านง่ายขึ้นถ้าใช้ชื่อวิธีการอธิบาย


1

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

processSection1HeaderData();
processSection2HeaderData();

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


นี่ไม่ใช่รหัสการผลิตจริง ๆ ดังนั้นชื่อฟังก์ชันเป็นเพียงตัวอย่าง (ในการผลิตชื่อฟังก์ชันนั้นมีความหมายมากกว่า) โดยทั่วไปไม่ไม่มีความคล้ายคลึงกันระหว่างส่วนต่าง ๆ (แม้ว่าเมื่อมีความคล้ายคลึงกันฉันพยายามที่จะใช้รหัสซ้ำให้มากที่สุด)
Eric C.

1

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

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


0

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

คุณไม่ได้พูดถึงเทคโนโลยีที่คุณใช้ในการพัฒนารายงานเหล่านี้ ดูเหมือนว่าคุณอาจใช้ C # ถูกต้องหรือไม่ ถ้าเป็นเช่นนั้นคุณอาจพิจารณา#region directiveเป็นทางเลือกหากคุณไม่ต้องการแยกฟังก์ชั่นของคุณออกให้เล็กลง


ใช่ฉันทำงานใน C # เป็นไปได้ว่าฉันจะสามารถนำฟังก์ชั่นบางอย่างมาใช้ใหม่ได้ แต่สำหรับรายงานเหล่านี้หลายส่วนส่วนต่าง ๆ มีข้อมูลและเลย์เอาต์ที่แตกต่างกันมากมาย (ความต้องการของลูกค้า)
Eric C.

1
+1 ยกเว้นการแนะนำภูมิภาค ฉันจะไม่ใช้ภูมิภาค
เบอร์นาร์ด

@Bernard - มีเหตุผลพิเศษไหม? ฉันสมมติว่าผู้ถามจะพิจารณาใช้ #regions หากเขาตัดการแบ่งฟังก์ชันออกไปแล้ว
Joshua Carmody

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

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