สัญญารหัส / ยืนยัน: สิ่งที่มีการตรวจสอบซ้ำกัน?


10

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

สถานการณ์ตัวอย่าง: ฉันเขียนฟังก์ชันต่อไปนี้ก่อน

void DoSomething( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  //code using obj
}

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

void DoSomethingElse( object obj )
{
  //no Requires here: DoSomething will do that already
  DoSomething( obj );
  //code using obj
}

ปัญหาที่ชัดเจน: DoSomethingElseตอนนี้ขึ้นอยู่กับDoSomethingการตรวจสอบว่า obj ไม่เป็นโมฆะ ดังนั้นควรDoSomethingตัดสินใจว่าจะไม่ตรวจสอบอีกต่อไปหรือถ้าฉันตัดสินใจใช้ฟังก์ชันอื่น obj อาจไม่ได้รับการตรวจสอบอีกต่อไป ซึ่งทำให้ฉันเขียนการใช้งานนี้หลังจากทั้งหมด:

void DoSomethingElse( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  DoSomething( obj );
  //code using obj
}

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

การปฏิบัติที่พบบ่อยที่สุดสำหรับสถานการณ์เช่นนี้คืออะไร?


3
ArgumentBullException? นั่นคือใหม่ :)
CVn

lol @ ทักษะการพิมพ์ของฉัน ... ฉันจะแก้ไข
stijn

คำตอบ:


13

โดยส่วนตัวฉันจะตรวจสอบโมฆะในฟังก์ชั่นใด ๆ ที่จะล้มเหลวถ้ามันได้รับโมฆะและไม่ได้อยู่ในฟังก์ชั่นที่จะไม่

ดังนั้นในตัวอย่างข้างต้นของคุณถ้า doSomethingElse () ไม่จำเป็นต้องทำการค้นหาซ้ำดังนั้นฉันจะไม่ตรวจสอบ obj สำหรับค่าว่าง

หาก DoSomething () ทำการยกเลิกการอ้างอิงควรตรวจสอบค่าว่าง

หากทั้งสองฟังก์ชั่นไม่ลงรอยกันก็ควรตรวจสอบทั้งคู่ ดังนั้นถ้า DoSomethingElse dereferences obj ควรตรวจสอบ null แต่ DoSomething ยังควรตรวจสอบ null เนื่องจากอาจถูกเรียกจากพา ธ อื่น

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


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

2

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

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

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

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

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


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

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

(1) เห็นด้วย (2) ความสามารถในการอ่านเป็นการตั้งค่าส่วนตัวดังนั้นมันจึงไม่ใช่สิ่งที่อยู่ภายใต้การสนทนาของฉัน .. (3) การใช้ฟังก์ชัน 20 บรรทัดต้องกดปุ่ม F10 20 ครั้ง การผ่านฟังก์ชั่นที่มี 10 บรรทัดในฟังก์ชั่นแยกต้องใช้ตัวเลือกของฉันในการกดปุ่ม F10 11 ครั้งเท่านั้น ใช่ในกรณีแรกฉันสามารถวางเบรกพอยต์หรือเลือก 'jumpt to เคอร์เซอร์' แต่ยังคงมีความพยายามมากกว่ากรณีที่สอง
stijn

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