นี่เป็นรูปแบบที่ดีหรือไม่: แทนที่ฟังก์ชั่นที่ยาวนานด้วยแลมบ์ดาเป็นชุด?


14

ฉันเพิ่งทำงานในสถานการณ์ต่อไปนี้

class A{
public:
    void calculate(T inputs);
}

ประการแรกAแสดงถึงวัตถุในโลกทางกายภาพซึ่งเป็นข้อโต้แย้งที่แข็งแกร่งที่ไม่แยกชั้นเรียนขึ้น ตอนนี้calculate()กลายเป็นฟังก์ชั่นที่ค่อนข้างยาวและซับซ้อน ฉันเข้าใจโครงสร้างที่เป็นไปได้สามประการ:

  • เขียนเป็นกำแพงข้อความ - ข้อดี - ข้อมูลทั้งหมดอยู่ในที่เดียว
  • เขียนprivateฟังก์ชั่นยูทิลิตี้ในชั้นเรียนและใช้พวกเขาในcalculateร่างกาย - ข้อเสีย - ส่วนที่เหลือของชั้นไม่ทราบ / ดูแล / เข้าใจเกี่ยวกับวิธีการเหล่านั้น
  • เขียนcalculateวิธีดังต่อไปนี้:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }
    

สิ่งนี้ถือได้ว่าเป็นการปฏิบัติที่ดีหรือไม่ดี? วิธีนี้เปิดเผยปัญหาหรือไม่? โดยรวมแล้วฉันควรพิจารณาว่านี่เป็นแนวทางที่ดีเมื่อฉันต้องเผชิญกับสถานการณ์เดียวกันอีกครั้งหรือไม่?


แก้ไข:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

วิธีการเหล่านั้นทั้งหมด:

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

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

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


2
คุณคิดว่าการใช้ lambdas จะทำให้คุณรับฟังก์ชั่นการโทรได้หรือไม่?
Blrfl

1
Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.แท้จริงAหมายถึงข้อมูลเกี่ยวกับวัตถุที่สามารถอยู่ในโลกทางกายภาพ คุณสามารถมีอินสแตนซ์ที่Aไม่มีวัตถุจริงและวัตถุจริงโดยไม่มีตัวอย่างAดังนั้นการรักษาพวกเขาเหมือนพวกเขาเป็นหนึ่งและเหมือนกันเป็นเรื่องไร้สาระ
Doval

@Blrfl, encapsulation - ไม่มีใคร แต่calculate()จะรู้เกี่ยวกับฟังก์ชั่นย่อยเหล่านั้น
Vorac

หากการคำนวณทั้งหมดเหล่านี้เกี่ยวข้องกับแค่Aนั้นนั่นเป็นการทำให้สุดขีด
Blrfl

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

คำตอบ:


13

ไม่นี่ไม่ใช่รูปแบบที่ดีโดยทั่วไป

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

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

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

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

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

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

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

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

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}

ดังนั้นมันไม่ใช่ตัวเลขที่มีวัตถุประสงค์ แต่ฉันจะอ้างว่าการปรากฏตัวของแลมบ์ดาทำให้ฉันให้ความสำคัญกับโค้ดแต่ละบรรทัดมากขึ้นประมาณ 10 เท่าเพราะพวกมันมีศักยภาพมากมายที่จะได้รับความซับซ้อนอย่างรวดเร็วและเป็นอันตราย บรรทัดรหัสที่ดูไร้เดียงสา (เช่นcount++)
Cort Ammon

จุดที่ดี ฉันเห็นว่าเป็นข้อได้เปรียบเล็กน้อยของวิธีการที่มี lambdas - (1) โค้ดในพื้นที่ใกล้เคียงและมีขอบเขตภายใน (จะหายไปจากฟังก์ชั่นระดับไฟล์) (2) คอมไพเลอร์ทำให้แน่ใจว่าไม่มีการแชร์ตัวแปรท้องถิ่นระหว่างเซ็กเมนต์รหัส ดังนั้นข้อดีเหล่านั้นสามารถรักษาไว้ได้โดยแยกออกcalculate()เป็น{}บล็อกและประกาศข้อมูลที่ใช้ร่วมกันที่calculate()ขอบเขต ฉันคิดว่าการเห็นว่าลูกแกะไม่จับตัวผู้อ่านจะไม่ถูกขัดขวางด้วยพลังลูกแกะ
58

"ฉันว่าการเห็นลูกแกะไม่ได้จับผู้อ่านจะไม่ได้รับอิทธิพลจากพลังลูกแกะ" อันที่จริงแล้วเป็นคำพูดที่ยุติธรรม แต่เป็นที่ถกเถียงกันซึ่งกระทบกับหัวใจของภาษาศาสตร์ คำมักจะมีความหมายแฝงที่ขยายเกิน denotations ของพวกเขา ไม่ว่านัยยะของฉันlambdaจะไม่ยุติธรรมหรือหากคุณกำลังบังคับให้คนอื่นทำตามคำสั่งที่เข้มงวดไม่ใช่คำถามง่าย ๆ ที่จะตอบ ในความเป็นจริงมันอาจเป็นที่ยอมรับโดยสิ้นเชิงสำหรับคุณที่จะใช้lambdaวิธีนี้ใน บริษัท ของคุณและไม่สามารถยอมรับได้โดยสิ้นเชิงที่ บริษัท ของฉันและไม่มีใครผิดพลาดจริง ๆ !
Cort Ammon

ความคิดเห็นของฉันเกี่ยวกับความหมายของแลมบ์ดาเกิดขึ้นจากความจริงที่ว่าฉันเติบโตขึ้นใน C ++ 03 ไม่ใช่ C + 11 ฉันใช้เวลาหลายปีในการพัฒนาความขอบคุณสำหรับสถานที่เฉพาะที่ C ++ ได้รับบาดเจ็บจากการขาดlambdaเช่นfor_eachฟังก์ชั่น ดังนั้นเมื่อฉันเห็นสิ่งlambdaที่ไม่ตรงกับหนึ่งในกรณีที่ง่ายต่อการมองเห็นข้อสันนิษฐานแรกที่ฉันมาถึงคือมันน่าจะใช้สำหรับการเขียนโปรแกรมเชิงฟังก์ชั่นเพราะมันไม่ต้องการเป็นอย่างอื่น สำหรับนักพัฒนาหลาย ๆ คนการเขียนโปรแกรมที่ใช้งานได้นั้นเป็นแนวคิดที่แตกต่างอย่างสิ้นเชิงจากขั้นตอนหรือการเขียนโปรแกรม OO
Cort Ammon

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

20

ฉันคิดว่าคุณทำสมมติฐานที่ไม่ดี:

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

ฉันไม่เห็นด้วยกับสิ่งนี้ ตัวอย่างเช่นถ้าฉันมีชั้นเรียนที่แสดงถึงรถยนต์ฉันต้องการแยกมันออกแน่นอนเพราะฉันต้องการชั้นเรียนขนาดเล็กเพื่อเป็นตัวแทนของยางรถยนต์

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

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

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

เพื่อสรุปผมไม่คิดว่านี่เป็นรูปแบบที่ดี

UPDATE

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


ฟังดูสมเหตุสมผล แต่มันยากสำหรับฉันที่จะจินตนาการถึงการนำไปใช้ คลาสขอบเขตไฟล์ที่มีวิธีการคงที่? คลาสที่นิยามไว้ข้างในA(อาจเป็น functor กับวิธีอื่นทั้งหมดเป็นส่วนตัว) ชั้นประกาศและนิยามไว้ข้างในcalculate()(ดูเหมือนเป็นตัวอย่างแลมบ์ดาของฉัน) การชี้แจงcalculate()เป็นหนึ่งในตระกูลของวิธีการ ( calculate_1(), calculate_2()ฯลฯ ) ซึ่งทั้งหมดนั้นง่ายเพียงแค่อันนี้คือ 2 หน้าจอของสูตร
Vorac

@Vorac: ทำไมcalculate()นานกว่าวิธีอื่น ๆ ?
Kevin

@Vorac มันยากมากที่จะช่วยโดยไม่เห็นรหัสของคุณ คุณสามารถโพสต์ได้หรือไม่
Gábor Angyal

@ เควินสูตรสำหรับทุกสิ่งที่กำหนดไว้ในข้อกำหนด รหัสโพสต์
Vorac

4
ฉันจะลงคะแนนให้ 100 ครั้งถ้าทำได้ "การสร้างแบบจำลองวัตถุในโลกแห่งความจริง" เป็นจุดเริ่มต้นของเกลียวมรณะการออกแบบวัตถุ มันเป็นธงสีแดงขนาดยักษ์ในทุกรหัส
Fred the Magic Wonder Dog
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.