เขียน Pre-Post Conditions ด้วย .NET Code Contracts Library แล้ว Code อ่านง่ายและดีขึ้นได้อีกเยอะเลย!

แนะนำ Code Contracts Library

Code Contracts ช่วยให้เราใส่ rule เพื่อ validate pre-condition, post-condition และ object invariant เข้าไปใน code ของเราได้ง่ายขึ้น ผมจะแบ่งอธิบาย ไปที่ละ step พร้อมทั้งแสดงตัวอย่างวิธีใช้งาน Code Contracts Library ไปตามลำดับครับ

แต่ก่อนที่จะอธิบาย ไป load Code Contracts Library และติดตั้งกันก่อนครับที่นี่ Code Contracts Library (บทความนี้ใช้ตัว Premium Edition ครับ 😉 )

หลังจากที่คุณติดตั้งเรียบร้อยเสร็จแล้ว เพื่อให้ Code Contracts ทำงานหรือ injecting contract เข้าไปใน assembly ด้วย

ให้เปิด Visual Studio 2010 ขึ้นมา เปิด Projects ที่ต้องการใช้ Code Contracts ของคุณขึ้นมาแล้ว click ขวา เลือก Properties คุณจะเห็น tab Code Contracts แสดงอยู่(จะมีก็ต่อเมื่อเรา install plugin Code Contracts แล้ว)

ให้ click ที่ tab Code Contracts แล้ว check เลือก Perform Contract Checking ตามภาพข้างล่างครับ

เสร็จแล้วเราก็มาเริ่ม validate pre-condition, post-condition และ object invariant ด้วย Code Contracts กันได้แล้วครับ

Code Contracts กับการ validate pre-condition

การ Validate Pre-condition คือเมื่อเราต้องการตรวจสอบ Properties(State)ของ Object(Domain model) หรือ System(World) ให้ถูกต้องตาม Rule ของเราก่อนจะปฏิบัติการ หรือดำเนินกิจกรรมได้ การ validate Pre-condition จะทำงานโดยใช้ method

Contract.Requires(“กำหนดเงื่อน ที่ต้องการตรวสอบ เช่น number > 0”)

หรือ Contract.Requires<“Class Exception ที่ต้องการ Throw เมื่อเงื่อนไขไม่ผ่าน”>(“กำหนดเงื่อน ที่ต้องการตรวสอบ”)

ตัวอย่าง Code ที่ทำการตรวสอบความถูกต้อง(validate) ของ Pre-condition ก่อนอื่นต้อง include โดยการ using System.Diagnostics.Contracts เข้ามาใน projects ของเราซะก่อน Code ตัวอย่าง Pre-condition จะคล้ายๆแบบนี้ครับ

publicFingerScanReport GetDailyFingerScanReport(Int32 employeeId, DateTime dateForReport)

{

//Pre-condition ต้อง valid ทั้ง Rule1 และ Rule 2

Contract.Requires(employeeId > 0); //Rule 1

Contract.Requires<ArgumentOutOfRangeException>(dateForReport.Date == DateTime.Now.Date);//Rule 2

// ทำอะไรๆ ต่อไปหลังจาก ผ่าน Pre-condition แล้ว

var report = HRRepository.RetriveDailyFingerScanReport(employeeId);

return report;

}

Code ด้านบนอธิบายได้ว่า method GetDailyFingerScanReport จะ validate Pre-condition โดยตรวสอบ employeeId ต้องมีค่ามากกว่า 0 (employeeId > 0) เสมอ และตวจสอบวันที่ขอ report ต้องเป็นวันที่ปัจจุบันเสมอด้วย(dateForReport.Date == DateTime.Now.Date) โดยจะ throw ArgumentOutOfRangeException ออกไปถ้าไม่ผ่าน rule นี้

Code Contracts กับการ validate post-condition

เราจะ validate Post-condition เมื่อเราต้องการตรวจสอบ State ของ Method หลังจากดำเนินการสิ้นสุดแล้ว ด้วยการตรวจสอบ post-condition จะช่วยให้เรายืนยันได้ว่า ผลลัพย์(output) หลังการดำเนินการของเราแล้ว จะถูกต้องตามกฎของธุรกิจ หรือกฏธรรมชาติใน World ของเราเสมอ การ validate Post-condition จะทำงานโดยใช้ method Contract.Ensures() ในแบบ Simple จะแสดง contract แบบนี้ Contract.Ensures(this.F > 0) ครับ และในกรณีแบบ Special ใช้ method Contract.Result<> โดยกำหนด ชนิด output ให้กับ Result และกำหนดเงื่อนไขเพื่อตรวจสอบก่อนที่ result จะ return ออก และนี่คือตัวอย่าง code Post-condition

public FingerScanReport GetWeeklyFingerScanReport(Int32 employeeId)

{

//Post-condition ในแบบ Spacial ตรวจสอบ finger scan report ต้องมีค่าเสมอก่อน return ออกไป

Contract.Ensures(Contract.Result<FingerScanReport>() != null);

var report = HRRepository.RetriveFingerScanReportByEmpId(employeeId);

return report;

}

Code Contracts กับการ validate Object Invariant

การตรวจสอบ Object Invariant ก็คือการตรวจสอบเพื่อให้เราแน่ใจว่า สภาวะ(State) ของ object(หรือ domain model) ของเรานั้นยังคงถูกต้องเสมอเมื่อ state มีการเปลี่ยนแปลงไปแล้ว ถ้า state ของ object ที่เราได้ตกลงไว้(contract) ตามกฏ(rule) แล้วไม่ถูกต้องกระบวนการจะผิดพลาดและ มันก็คือปัญหาที่เราจะต้องจัดการ

ตัวอย่างสมมติว่าเรามี class FingerScanReport ที่ใช้แสดงรายงาน scan นิ้วเข้างานของพนักงานในบริษัทหนึ่ง ซึ่งมันถูกจัดการโดย class HRRepository โดยจะบันทึกเข้าระบบฐานข้อมูลบุคคลทุกๆครั้งที่พนักงาน scan นิวเข้า ออกงาน และเราต้องการที่จะตั้งกฎ(Rule) ว่าพนักงานบาง Role จะไม่ได้รับอนุญาติให้ชั่วโมงทำงาน > 40 ชั่วโมง ใน 1 week  พร้อมกับตั้งกฏชั่วโมงทำงานต้องไม่ < 0

ก่อนที่จะมี Code Contract Library นี้ถ้าเราต้องการใส่กฏข้างต้นดังกล่าวเข้าไปใน Class เราจำเป็นที่จะต้องเขียน method  วิธีการตรวจสอบ State ก่อน แล้วนำไปใส่ไว้ใน Validator เสร็จแล้วจึงนำตัว Validator นี้ไปใส่ในต่ำแหน่งที่มีการเปลี่ยนแปลง State เพื่อตรวจสอบว่ายังคงถูกต้องตาม rule ของเราอยู่มั้ย นี่เป็นวิธีการที่น่าเบื่อมาก และมีแนวโน้มที่จะทำให้เกิดข้อผิดพลาดขึ้นได้ง่ายมาก เพราะว่าเราต้องเพิ่ม code มากขึ้นเพื่อตรวจสอบ State หากมีการเปลี่ยนแปลงเกิดขึ้น

ด้วยวิธีการ Validate Object Invariant โดยใช้ Code Contract library นี้เราไม่จำเป็นต้องทำแบบเดิมอีกต่อไป และนี่ก็คือ invariant object โดยใช้ Code Contract library

[ContractInvariantMethod]

public void ObjectInvariant()

{

//ถ้า State EmployeeID<= 0 หรือ totalHours หลังจากคำนวณ ด้วย method TotalHours แล้ว < 0 หรือ > 40 เมื่อไร Invariant จะแจ้งสถาวะผิดปกตินี้ออกมาทันที ทั้งนี้ขึ้นอยู่กับการ config ในหน้า tab Code Contract ก่อน build assembly ของเราว่าจะให้เกิด exception ไปเลย หรือแค่แจ้งความผิดปกตินี้มาให้เราทราบเท่านั้น หรือเพิกเฉยไปเลยก็ได้แล้วแต่เรากำหนดได้เองครับ

Contract.Invariant(EmployeeID > 0);

var totalHours = TotalHours(); //คำนวณชั่วโมงทำงานรวม

Contract.Invariant(totalHours >= 0);

Contract.Invariant(totalHours <= 40);

}

อธิบาย  Code invariant method ได้ดังนี้ครับ

  • Attribute ContractInvariantMethod เป็นการประกาศว่า method นี้เป็น contract  invariant ของ Class นี้
  • เราต้องใช้ method Contract.Invariant เพื่อใส่ rule ของคุณเข้าไปเสมอ
  • ใน Class ของเรา มีได้เพียง 1 method เท่านั้นที่มี Attribute ContractInvariantMethod ได้
  • ตัว Code weaver จะเพิ่มการเรียกใช้ logic invariant ของเราเข้าไปในทุกที่ ที่ state ของ object(หรือ domain model) มีการเปลี่ยนแปลงให้เอง

ถ้าอยากรู้ว่าจะทำให้ Code อ่านง่ายและดีขึ้นได้อีกเยอะมั้ย คุณคงต้องลองกลับไปแก้ code หรือ refactor code เก่าของคุณดูนะครับ

ขอบคุณครับ

Advertisements