การปรับเปลี่ยนวิธีการแบบยาว: ออกตามที่เป็นเทียบกับการแยกออกเป็นวิธีการเปรียบเทียบกับการใช้ฟังก์ชันในเครื่อง


9

สมมติว่าฉันมีวิธียาวเช่นนี้

public void SomeLongMethod()
{
    // Some task #1
    ...

    // Some task #2
    ...
}

วิธีนี้ไม่มีชิ้นส่วนซ้ำ ๆ ที่ควรเคลื่อนย้ายไปยังวิธีอื่นหรือฟังก์ชั่นในเครื่อง

มีหลายคน (รวมถึงฉัน) ที่คิดว่าวิธีการที่ยาวนานนั้นเป็นกลิ่นรหัส นอกจากนี้ผมไม่ชอบความคิดของการใช้#region(s) ที่นี่มีคำตอบที่เป็นที่นิยมมากอธิบายว่าทำไมนี้ไม่ดี


แต่ถ้าฉันแยกรหัสนี้เป็นวิธีการ

public void SomeLongMethod()
{
    Task1();

    Task2();
}

private void Task1()
{
    // Some task #1
    ...
}

private void Task2()
{
    // Some task #1
    ...
}

ฉันเห็นปัญหาต่อไปนี้:

  • ขอบเขตนิยามคลาสที่ก่อให้เกิดมลพิษกับคำจำกัดความที่ใช้ภายในโดยวิธีการเดียวซึ่งหมายความว่าฉันควรบันทึกไว้ที่ใดที่หนึ่งTask1และTask2มีไว้สำหรับ internals ของSomeLongMethod(หรือทุกคนที่อ่านรหัสของฉันจะต้องอนุมานความคิดนี้)

  • การทำให้สมบูรณ์ IDE อัตโนมัติโดยสมบูรณ์ (เช่น Intellisense) ของวิธีการที่จะใช้เพียงครั้งเดียวในSomeLongMethodวิธีการเดียว


ดังนั้นถ้าฉันแยกรหัสวิธีการนี้เป็นฟังก์ชั่นท้องถิ่น

public void SomeLongMethod()
{
    Task1();

    Task2();

    void Task1()
    {
        // Some task #1
        ...
    }

    void Task2()
    {
        // Some task #1
        ...
    }
}

ถ้าอย่างนั้นก็ไม่ได้มีข้อเสียของวิธีการที่แยกจากกัน แต่วิธีนี้ไม่ได้ดูดีกว่า (อย่างน้อยสำหรับฉัน) กว่าวิธีดั้งเดิม


เวอร์ชันใดที่คุณSomeLongMethodสามารถบำรุงรักษาและอ่านได้มากขึ้นและทำไม



@gnat คำถามของฉันคือที่เฉพาะเจาะจงสำหรับC #ไม่จาวา ความกังวลหลักของฉันเกี่ยวกับวิธีคลาสสิกกับฟังก์ชั่นในท้องถิ่นไม่เพียง แต่แยกออกเป็นวิธีการหรือไม่
Vadim Ovchinnikov

@gnat นอกจากนี้ AFAIK Java (ไม่เหมือนกับ C #) ไม่รองรับฟังก์ชั่นที่ซ้อนกัน
Vadim Ovchinnikov

1
คำหลักส่วนตัวจะปกป้อง 'ขอบเขตการกำหนดคลาส' ของคุณอย่างแน่นอนหรือไม่
Ewan

1
เดี๋ยวก่อน ...... ชั้นเรียนของคุณใหญ่แค่ไหน?
Ewan

คำตอบ:


13

ควรย้ายงานที่อยู่ในตัวเองไปยังวิธีการที่มีในตัวเอง ไม่มีข้อเสียใหญ่พอที่จะแทนที่เป้าหมายนี้

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

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


ดังนั้นสำหรับวิธีการไม่ใช่ฟังก์ชั่นในท้องถิ่นใช่หรือไม่
Vadim Ovchinnikov

2
@VadimOvchinnikov ด้วย IDE การพับโค้ดที่ทันสมัยมีความแตกต่างเล็กน้อย ข้อได้เปรียบที่ยิ่งใหญ่ที่สุดของฟังก์ชั่นท้องถิ่นคือมันสามารถเข้าถึงตัวแปรที่คุณต้องผ่านเป็นพารามิเตอร์ได้ แต่ถ้ามีจำนวนมากแล้วงานที่ไม่ได้ขึ้นต้นด้วยตัวเองจริงๆ
Kilian Foth

6

ฉันไม่ชอบวิธีใดวิธีหนึ่ง คุณไม่ได้ให้บริบทที่กว้างขึ้น แต่ส่วนใหญ่ดูเหมือนว่า:

คุณมีคลาสที่ทำงานบางอย่างด้วยการรวมพลังของบริการหลายอย่างไว้ในผลลัพธ์เดียว

class SomeClass
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;

    public void SomeLongMethod()
    {
        _service1.Task1();
        _service2.Task2();       
    }
}

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

class Service1
{
    public void Task1()
    {
        // Some task #1
        ...
    }

    private void SomeHelperMethod() {}
}

class Service2
{
    void Task2()
    {
        // Some task #1
        ...
    }
}

บรรทัดล่างคือ: ถ้าคุณเริ่มสร้างภูมิภาคในรหัสของคุณเพื่อวิธีการ gorup มันเป็นเวลาที่จะเริ่มคิดเกี่ยวกับการห่อหุ้มตรรกะของพวกเขาด้วยบริการใหม่


2

ปัญหาในการสร้างฟังก์ชั่นภายในเมธอดคือมันไม่ลดความยาวของเมธอด

หากคุณต้องการระดับการป้องกันพิเศษ 'เป็นส่วนตัวกับวิธีการ' คุณสามารถสร้างคลาสใหม่ได้

public class BigClass
{
    public void SomeLongMethod();

    private class SomeLongMethodRunner
    {
        private void Task1();
        private void Task2();
    }
}

แต่ชั้นเรียนในชั้นเรียนมีขนาดใหญ่น่าเกลียด: /


2

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

สิ่งนี้พูดถึงการใช้ฟังก์ชันโลคัล


1
hmm แน่นอนใช้รหัส == แบ่งปันวิธีการในหมู่ผู้โทรจำนวนมาก ie การเพิ่มขอบเขตให้สูงสุด
Ewan

1

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

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

ท้องถิ่นฟังก์ชั่นหน้ากล่าวว่า:

'ฟังก์ชั่นท้องถิ่นทำให้เจตนาของรหัสของคุณชัดเจน ทุกคนที่อ่านโค้ดของคุณจะเห็นว่าวิธีการนี้ไม่สามารถเรียกได้ยกเว้นด้วยวิธีการที่มี สำหรับโครงการของทีมพวกเขายังทำให้ผู้พัฒนารายอื่นไม่สามารถเรียกวิธีการนี้โดยตรงจากที่อื่นในชั้นเรียนหรือ stuct '

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

โดยสรุปแล้วโดยทั่วไปฉันชอบแยกออกเป็นวิธีการ แต่ MS เขียน Local Function ด้วยเหตุผลดังนั้นฉันจะไม่พูดว่าอย่าใช้มัน

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