ฉันไม่รู้ว่าการยืนยันว่า "มันไม่ได้และมักไม่สามารถทำการวิเคราะห์แบบคงที่" มาจาก ส่วนแรกของการยืนยันผิดอย่างชัดเจน อย่างที่สองขึ้นอยู่กับสิ่งที่คุณหมายถึงโดย "มักจะ" ฉันอยากจะบอกว่าบ่อยครั้งที่มันทำการวิเคราะห์แบบสแตติกและไม่ค่อยจะล้มเหลว ในการประยุกต์ทางธุรกิจธรรมดาไม่ค่อยจะกลายเป็นมากใกล้ชิดกับไม่เคย
ดังนั้นที่นี่มาประโยชน์แรก:
ประโยชน์ที่ 1: การวิเคราะห์แบบคงที่
การยืนยันปกติและการตรวจสอบอาร์กิวเมนต์มีข้อเสียเปรียบ: จะถูกเลื่อนออกไปจนกว่าจะมีการใช้งานรหัส ในทางกลับกันสัญญาของรหัสแสดงให้เห็นถึงตัวเองในระดับก่อนหน้านี้มากทั้งในขั้นตอนการเข้ารหัสหรือในขณะที่รวบรวมแอปพลิเคชัน ยิ่งคุณพบข้อผิดพลาดก่อนหน้านี้เท่าใดก็ยิ่งมีราคาถูกลงเท่านั้น
ประโยชน์ที่ 2: เรียงลำดับเอกสารที่เป็นปัจจุบันเสมอ
สัญญารหัสนอกจากนี้ยังมีการจัดเรียงของเอกสารซึ่งเป็นเสมอถึงวันที่ หากความคิดเห็น XML ของวิธีการSetProductPrice(int newPrice)
บอกว่าnewPrice
ควรเหนือกว่าหรือเท่ากับศูนย์คุณอาจหวังว่าเอกสารนั้นเป็นข้อมูลล่าสุด แต่คุณอาจค้นพบว่ามีคนเปลี่ยนวิธีการดังกล่าวเพื่อให้เกิดการnewPrice = 0
โยนArgumentOutOfRangeException
แต่ไม่เคยเปลี่ยนแปลงเอกสารที่เกี่ยวข้อง เมื่อพิจารณาถึงความสัมพันธ์ระหว่างสัญญาโค้ดและโค้ดเองคุณไม่มีปัญหาเกี่ยวกับเอกสารที่ไม่ซิงค์กัน
การเรียงลำดับของเอกสารที่จัดทำโดยสัญญาโค้ดนั้นมีค่าเช่นกันซึ่งบ่อยครั้งที่ความคิดเห็น XML ไม่สามารถอธิบายค่าที่ยอมรับได้ดี ฉันสงสัยหลายครั้งว่าnull
หรือstring.Empty
หรือ\r\n
เป็นค่าที่ได้รับอนุญาตสำหรับวิธีการและความคิดเห็น XML เงียบในที่!
โดยสรุปหากไม่มีสัญญารหัสโค้ดจำนวนมากก็เป็นเช่นนั้น:
ฉันจะยอมรับค่าบางอย่าง แต่ไม่ใช่ค่าอื่น ๆ แต่คุณต้องเดาหรืออ่านเอกสารถ้ามี จริงๆแล้วอย่าอ่านเอกสาร: มันล้าสมัยแล้ว เพียงแค่วนรอบค่าทั้งหมดและคุณจะเห็นค่าที่ทำให้ฉันโยนข้อยกเว้น คุณต้องเดาช่วงของค่าที่อาจส่งคืนเพราะแม้ว่าฉันจะบอกคุณมากกว่านี้เกี่ยวกับค่าเหล่านั้นมันอาจไม่จริงเนื่องจากการเปลี่ยนแปลงหลายร้อยครั้งที่เกิดขึ้นกับฉันในปีที่ผ่านมา
ด้วยสัญญารหัสมันจะกลายเป็น:
อาร์กิวเมนต์หัวเรื่องสามารถเป็นสตริงที่ไม่ใช่ค่า null ที่มีความยาว 0..500 จำนวนเต็มซึ่งตามมาเป็นค่าบวกซึ่งสามารถเป็นศูนย์ได้เฉพาะเมื่อสตริงว่างเปล่า ในที่สุดฉันจะคืนIDefinition
วัตถุไม่เป็นโมฆะ
ประโยชน์ที่ 3: สัญญาการเชื่อมต่อ
ประโยชน์ประการที่สามคือสัญญาที่ให้อำนาจกับอินเตอร์เฟส สมมติว่าคุณมีสิ่งที่ชอบ:
public interface ICommittable
{
public ICollection<AtomicChange> PendingChanges { get; }
public void CommitChanges();
...
}
คุณจะใช้การยืนยันและข้อยกเว้นCommitChanges
เพียงอย่างเดียวPendingChanges
ได้อย่างไรรับประกันได้ว่าสามารถเรียกได้เฉพาะเมื่อไม่ว่างเปล่า คุณจะรับประกันได้อย่างไรว่าPendingChanges
ไม่เคยมีnull
?
ประโยชน์ที่ 4: บังคับใช้ผลลัพธ์ของวิธีการ
ในที่สุดประโยชน์ที่สี่คือการได้Contract.Ensure
ผลลัพธ์ จะเกิดอะไรขึ้นหากเมื่อเขียนวิธีที่ส่งกลับจำนวนเต็มฉันต้องการตรวจสอบให้แน่ใจว่าค่านั้นไม่ด้อยกว่าหรือเท่ากับศูนย์ รวมถึงห้าปีต่อมาหลังจากประสบกับความเปลี่ยนแปลงมากมายจากนักพัฒนามากมาย? ทันทีที่วิธีหนึ่งมีหลายจุดคืนค่าAssert
s กลายเป็นฝันร้ายในการบำรุงรักษา
พิจารณาสัญญารหัสไม่เพียง แต่เป็นวิธีการที่ถูกต้องของรหัสของคุณ แต่เป็นวิธีที่เข้มงวดในการเขียนรหัส ในทำนองเดียวกันคนที่ใช้ภาษาแบบไดนามิกโดยเฉพาะอาจถามว่าทำไมคุณถึงบังคับใช้ประเภทในระดับภาษาในขณะที่คุณสามารถทำสิ่งเดียวกันในการยืนยันเมื่อจำเป็น คุณสามารถ แต่การพิมพ์แบบสแตติกใช้งานง่ายกว่ามีข้อผิดพลาดน้อยกว่าเมื่อเทียบกับการยืนยันจำนวนมากและการจัดทำเอกสารด้วยตนเอง
ความแตกต่างระหว่างการพิมพ์แบบไดนามิกและการพิมพ์แบบสแตติกนั้นใกล้เคียงกับความแตกต่างระหว่างการโปรแกรมทั่วไปและการเขียนโปรแกรมตามสัญญา