การแก้ปัญหาที่มาพร้อมกับฟังก์ชัน dyadic assertEquals (คาดว่าเป็นจริง)


10

หลังจากรหัสคาวบอยมาหลายปีฉันตัดสินใจที่จะรับหนังสือเกี่ยวกับวิธีการเขียนโค้ดคุณภาพดี ฉันอ่าน Clean Code ของ Robert Cecil Martin ในบทที่ 3 (ฟังก์ชั่น) มีส่วนในฟังก์ชั่น dyadic นี่คือข้อความที่ตัดตอนมาจากหนังสือ

แม้กระทั่งฟังก์ชั่น dyadic ที่เห็นได้ชัดassertEquals(expected, actual)ก็เป็นปัญหา คุณใส่ของจริงกี่ครั้งที่คาดว่าควรจะเป็น? ข้อโต้แย้งทั้งสองไม่มีการเรียงลำดับแบบเป็นธรรมชาติ การจัดลำดับที่คาดหวังและเกิดขึ้นจริงเป็นแบบแผนที่ต้องฝึกปฏิบัติเพื่อเรียนรู้

ผู้เขียนทำให้เป็นจุดที่น่าสนใจ ฉันทำงานในการเรียนรู้ของเครื่องและเจอสิ่งนี้ตลอดเวลา ตัวอย่างเช่นฟังก์ชันเมทริกทั้งหมดในไลบรารี sklearn (อาจเป็นไพ ธ อนไลบรารีที่ใช้มากที่สุดในฟิลด์) กำหนดให้คุณต้องระวังลำดับของอินพุต ในฐานะที่เป็นตัวอย่างsklearn.metrics.homogeneity_scoreจะเป็นปัจจัยการผลิตและlabels_true labels_predฟังก์ชั่นนี้ไม่เกี่ยวข้องมากเกินไปสิ่งที่เกี่ยวข้องก็คือถ้าคุณสลับลำดับของอินพุตจะไม่มีข้อผิดพลาดเกิดขึ้น ในความเป็นจริงการเปลี่ยนอินพุตนั้นเทียบเท่ากับการใช้ฟังก์ชันอื่นในไลบรารี

assertEqualsแต่หนังสือเล่มนี้ไม่ได้ไปกล่าวต่อไปว่าการแก้ไขที่เหมาะสมสำหรับการทำงานเช่น ฉันไม่สามารถนึกถึงการแก้ไขสำหรับassertEqualsหรือฟังก์ชั่นที่ฉันมักจะเจออย่างที่อธิบายไว้ข้างต้น แนวปฏิบัติที่ดีในการแก้ไขปัญหานี้คืออะไร

คำตอบ:


11

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

มีวิธีการระดับภาษาเพื่อป้องกันความสับสนเกี่ยวกับการสั่งซื้อพารามิเตอร์: อาร์กิวเมนต์ที่มีชื่อ ขออภัยที่ไม่รองรับในหลายภาษาที่มีรูปแบบ C-style เช่น Java หรือ C ++ แต่ใน Python ทุกอาร์กิวเมนต์สามารถเป็นอาร์กิวเมนต์ที่กำหนดชื่อไว้ได้ แทนการเรียกฟังก์ชั่นdef foo(a, b)ในฐานะที่เราสามารถทำได้foo(1, 2) foo(a=1, b=2)ภาษาสมัยใหม่จำนวนมากเช่น C # มีไวยากรณ์ที่คล้ายกัน ตระกูลภาษา Smalltalk ใช้ชื่ออาร์กิวเมนต์ที่สุดแล้ว: ไม่มี args ตำแหน่งใด ๆ และทุกอย่างมีชื่อ สิ่งนี้สามารถนำไปสู่รหัสที่อ่านใกล้เคียงกับภาษาธรรมชาติมาก

ทางเลือกที่มีประโยชน์มากกว่าคือสร้าง API ที่จำลองอาร์กิวเมนต์ที่มีชื่อ เหล่านี้อาจเป็น API ได้อย่างคล่องแคล่วหรือฟังก์ชั่นผู้ช่วยที่สร้างการไหลตามธรรมชาติ assertEquals(actual, expected)ชื่อเป็นความสับสน ทางเลือกบางอย่างที่ฉันได้เห็น:

  • assertThat(actual, is(equalTo(expected))): โดยการตัดอาร์กิวเมนต์บางตัวในประเภทผู้ช่วยฟังก์ชั่นการตัดจะทำหน้าที่เป็นชื่อพารามิเตอร์อย่างมีประสิทธิภาพ ในกรณีที่เฉพาะเจาะจงของการยืนยันการทดสอบหน่วยเทคนิคนี้ถูกใช้โดยHamcrest matchersเพื่อให้ระบบการยืนยันที่ขยายได้และเรียงความได้ ข้อเสียของที่นี่คือคุณได้รับการซ้อนจำนวนมากและจำเป็นต้องนำเข้าฟังก์ชั่นตัวช่วยมากมาย นี่คือเทคนิคการไปสู่ของฉันใน C ++

  • expect(actual).to.be(expected): API ได้อย่างคล่องแคล่วที่คุณใช้ฟังก์ชั่นการโทรด้วยกัน ขณะนี้หลีกเลี่ยงการทำรังเป็นพิเศษ แต่ก็ไม่สามารถขยายได้มาก ในขณะที่ฉันพบว่า API ที่คล่องแคล่วอ่านได้ดีมากการออกแบบ API ที่คล่องแคล่วดีนั้นมีความพยายามอย่างมากในประสบการณ์ของฉันเพราะคุณต้องใช้คลาสเพิ่มเติมสำหรับสถานะที่ไม่ใช่เทอร์มินัลในสายการโทร ความพยายามนี้จ่ายจริง ๆ เท่านั้นในบริบทของการ autocompleting IDE ที่สามารถแนะนำการเรียกใช้วิธีการต่อไปที่ได้รับอนุญาต


4

มีหลายวิธีในการหลีกเลี่ยงปัญหานี้ วิธีหนึ่งที่ไม่บังคับให้คุณเปลี่ยนวิธีที่คุณโทรหา:

ค่อนข้างมากกว่า

assertEquals( 42, meaningOfLife() ); 

ใช้

expected = 42;
actual = meaningOfLife();
assertEquals(expected, actual);

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

หากคุณสามารถเปลี่ยนวิธีการที่เรียกว่าคุณสามารถใช้ระบบการพิมพ์เพื่อบังคับให้ใช้งานง่ายต่อการอ่าน

assertThat( meaningOfLife(), is(42) );

บางภาษาให้คุณหลีกเลี่ยงปัญหานี้เนื่องจากพวกเขาตั้งชื่อพารามิเตอร์:

assertEquals( expected=42, actual=meaningOfLife() );

คนอื่นไม่ทำเช่นนั้นคุณจำลองพวกเขา:

assertEquals().expected(42).actual( meaningOfLife() );

สิ่งที่คุณหาวิธีทำให้ชัดเจนซึ่งถูกต้องเมื่ออ่าน อย่าทำให้ฉันเดาว่าการประชุมคืออะไร แสดงให้ฉันดู.

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