หากคุณใช้คำหลัก 'คงที่' โดยไม่มีคำหลัก 'สุดท้าย' นี่ควรเป็นสัญญาณที่จะพิจารณาอย่างรอบคอบถึงการออกแบบของคุณ แม้แต่การปรากฏตัวของ 'ขั้นสุดท้าย' ก็ไม่ผ่านฟรีเนื่องจากวัตถุสุดท้ายที่ไม่แน่นอนอาจกลายเป็นอันตรายได้
ฉันจะประมาณบางแห่งประมาณ 85% ของเวลาที่ฉันเห็น 'คงที่' โดยไม่ต้อง 'สุดท้าย' มันผิด บ่อยครั้งที่ฉันจะพบวิธีแก้ปัญหาที่แปลกประหลาดเพื่อปกปิดหรือซ่อนปัญหาเหล่านี้
โปรดอย่าสร้างการเปลี่ยนแปลงแบบคงที่ โดยเฉพาะคอลเลกชัน โดยทั่วไปคอลเล็กชันควรเริ่มต้นได้เมื่อวัตถุที่บรรจุประกอบด้วยการเตรียมใช้งานและควรได้รับการออกแบบเพื่อให้พวกเขาถูกรีเซ็ตหรือลืมเกี่ยวกับเมื่อลืมวัตถุที่ประกอบด้วย
การใช้สถิตศาสตร์สามารถสร้างข้อบกพร่องที่ละเอียดอ่อนมากซึ่งจะทำให้วิศวกรเจ็บปวดอย่างยาวนาน ฉันรู้เพราะฉันทั้งคู่สร้างและติดตามข้อผิดพลาดเหล่านี้
หากคุณต้องการรายละเอียดเพิ่มเติมโปรดอ่านต่อ ...
ทำไมไม่ใช้ Statics?
มีปัญหามากมายเกี่ยวกับสถิตศาสตร์รวมถึงการทดสอบการเขียนและการดำเนินการเช่นเดียวกับข้อบกพร่องที่ลึกซึ้งที่ไม่ชัดเจนทันที
รหัสที่ใช้กับวัตถุสแตติกไม่สามารถทดสอบได้อย่างง่ายดายและสถิตไม่สามารถเยาะเย้ยได้ง่าย (ปกติ)
หากคุณใช้สถิตศาสตร์คุณไม่สามารถสลับการนำคลาสไปใช้เพื่อทดสอบส่วนประกอบระดับสูงขึ้น ตัวอย่างเช่นลองนึกภาพ CustomerDAO แบบคงที่ที่ส่งคืนวัตถุลูกค้าที่โหลดจากฐานข้อมูล ตอนนี้ฉันมี CustomerFilter ซึ่งจำเป็นต้องเข้าถึงออบเจ็กต์ของลูกค้า ถ้า CustomerDAO เป็นแบบสแตติกฉันไม่สามารถเขียนทดสอบสำหรับตัวกรองลูกค้าโดยไม่ต้องเริ่มต้นฐานข้อมูลของฉันก่อนและเติมข้อมูลที่มีประโยชน์
และประชากรฐานข้อมูลและการเริ่มต้นใช้เวลานาน และจากประสบการณ์ของฉันเฟรมเวิร์กการกำหนดค่าเริ่มต้น DB ของคุณจะเปลี่ยนแปลงตลอดเวลาซึ่งหมายถึงข้อมูลที่จะแปรเปลี่ยนและการทดสอบอาจแตก IE ลองนึกภาพลูกค้า 1 เคยเป็น VIP แต่เปลี่ยนกรอบการกำหนดค่าเริ่มต้นของ DB และตอนนี้ลูกค้า 1 ไม่ใช่ VIP อีกต่อไป แต่การทดสอบของคุณยากที่จะโหลดลูกค้า 1 ...
วิธีที่ดีกว่าคือสร้างอินสแตนซ์ของ CustomerDAO และส่งต่อไปยัง CustomerFilter เมื่อสร้าง (วิธีการที่ดียิ่งขึ้นคือการใช้ Spring หรือกรอบการควบคุมผกผันของการควบคุมอื่น
เมื่อคุณทำเช่นนี้คุณสามารถเยาะเย้ยหรือตอออก DAO สำรองใน CustomerFilterTest ของคุณได้อย่างรวดเร็วช่วยให้คุณสามารถควบคุมการทดสอบได้มากขึ้น
หากไม่มี DAO แบบคงที่การทดสอบจะเร็วขึ้น (ไม่มีการกำหนดค่าเริ่มต้น db) และเชื่อถือได้มากกว่า (เพราะจะไม่ล้มเหลวเมื่อรหัสการเริ่มต้น db เปลี่ยนแปลง) ตัวอย่างเช่นในกรณีนี้การทำให้มั่นใจว่าลูกค้า 1 เป็นและจะเป็นวีไอพีเสมอตลอดจนการทดสอบที่เกี่ยวข้อง
ดำเนินการทดสอบ
Statics ทำให้เกิดปัญหาจริงเมื่อเรียกใช้ชุดการทดสอบหน่วยร่วมกัน (เช่นกับเซิร์ฟเวอร์การรวมอย่างต่อเนื่องของคุณ) ลองนึกภาพแผนที่คงที่ของวัตถุซ็อกเก็ตเครือข่ายที่ยังคงเปิดอยู่จากการทดสอบหนึ่งไปยังอีก การทดสอบครั้งแรกอาจเปิดซ็อกเก็ตที่พอร์ต 8080 แต่คุณลืมลบแผนที่เมื่อการทดสอบขาดลง ตอนนี้เมื่อการทดสอบครั้งที่สองเปิดตัวมันมีโอกาสที่จะเกิดข้อผิดพลาดเมื่อพยายามสร้างซ็อกเก็ตใหม่สำหรับพอร์ต 8080 เนื่องจากพอร์ตยังคงอยู่ ลองนึกภาพด้วยว่าการอ้างอิงซ็อกเก็ตในการรวบรวมแบบคงที่ของคุณจะไม่ถูกลบออกและ (ยกเว้น WeakHashMap) จะไม่มีสิทธิ์ถูกรวบรวมขยะทำให้หน่วยความจำรั่ว
นี่เป็นตัวอย่างที่เห็นได้ทั่วไป แต่ในระบบขนาดใหญ่ปัญหานี้จะเกิดขึ้นตลอดเวลา ผู้คนไม่คิดว่าการทดสอบหน่วยเริ่มต้นและหยุดซอฟต์แวร์ซ้ำ ๆ ใน JVM เดียวกัน แต่เป็นการทดสอบการออกแบบซอฟต์แวร์ที่ดีของคุณและถ้าคุณมีแรงบันดาลใจต่อความพร้อมใช้งานสูงมันเป็นสิ่งที่คุณต้องระวัง
ปัญหาเหล่านี้มักเกิดขึ้นกับวัตถุเฟรมเวิร์กเช่นการเข้าถึงฐานข้อมูลแคชการส่งข้อความและเลเยอร์การบันทึก หากคุณกำลังใช้ Java EE หรือเฟรมเวิร์กที่ดีที่สุดบางอย่างพวกเขาอาจจัดการสิ่งนี้ให้คุณมากมาย แต่ถ้าอย่างฉันที่คุณกำลังจัดการกับระบบดั้งเดิมคุณอาจมีเฟรมเวิร์กที่กำหนดเองจำนวนมากเพื่อเข้าถึงเลเยอร์เหล่านี้
หากการกำหนดค่าระบบที่นำไปใช้กับส่วนประกอบเฟรมเวิร์กเหล่านี้มีการเปลี่ยนแปลงระหว่างการทดสอบหน่วยและกรอบการทดสอบหน่วยจะไม่ทำลายและสร้างส่วนประกอบขึ้นมาใหม่การเปลี่ยนแปลงเหล่านี้จะไม่มีผลบังคับใช้และเมื่อการทดสอบอาศัยการเปลี่ยนแปลงเหล่านั้น .
แม้ส่วนประกอบที่ไม่ใช่กรอบก็อาจมีปัญหานี้ ลองนึกภาพแผนที่แบบคงที่ที่เรียกว่า OpenOrders คุณเขียนการทดสอบหนึ่งรายการที่สร้างคำสั่งซื้อที่เปิดอยู่สองสามรายการและตรวจสอบเพื่อให้แน่ใจว่าทั้งหมดนั้นอยู่ในสถานะที่ถูกต้องจากนั้นการทดสอบจะสิ้นสุดลง ผู้พัฒนารายอื่นเขียนการทดสอบครั้งที่สองซึ่งทำให้คำสั่งซื้อที่ต้องการลงในแผนที่ OpenOrders จากนั้นยืนยันว่าจำนวนการสั่งซื้อนั้นถูกต้อง เรียกใช้ทีละรายการการทดสอบเหล่านี้จะผ่าน แต่เมื่อทำงานร่วมกันในห้องชุด
ที่แย่กว่านั้นความล้มเหลวอาจขึ้นอยู่กับลำดับที่การทดสอบรัน
ในกรณีนี้โดยการหลีกเลี่ยงสถิตศาสตร์คุณหลีกเลี่ยงความเสี่ยงในการคงอยู่ของข้อมูลในอินสแตนซ์การทดสอบเพื่อให้มั่นใจถึงความน่าเชื่อถือในการทดสอบที่ดีขึ้น
ข้อบกพร่องที่ลึกซึ้ง
หากคุณทำงานในสภาพแวดล้อมที่มีความพร้อมใช้งานสูงหรือที่ใดก็ตามที่เธรดอาจเริ่มต้นและหยุดทำงานข้อกังวลเดียวกันที่กล่าวถึงข้างต้นกับชุดทดสอบหน่วยสามารถใช้เมื่อรหัสของคุณกำลังทำงานอยู่เช่นกัน
เมื่อจัดการกับเธรดแทนที่จะใช้วัตถุแบบสแตติกเพื่อเก็บข้อมูลจะเป็นการดีกว่าถ้าใช้วัตถุที่เริ่มต้นในระหว่างระยะเริ่มต้นของเธรด วิธีนี้ทุกครั้งที่มีการเริ่มเธรดอินสแตนซ์ใหม่ของวัตถุ (ด้วยการกำหนดค่าที่อาจเกิดขึ้นใหม่) จะถูกสร้างขึ้นและคุณหลีกเลี่ยงข้อมูลจากอินสแตนซ์หนึ่งของเธรดที่มีเลือดออกผ่านไปยังอินสแตนซ์ถัดไป
เมื่อเธรดตายไปวัตถุคงไม่ได้รับการรีเซ็ตหรือเก็บขยะ ลองนึกภาพคุณมีเธรดที่เรียกว่า "EmailCustomers" และเมื่อมันเริ่มมันจะรวบรวมคอลเลกชันสตริงแบบคงที่ด้วยรายการที่อยู่อีเมลจากนั้นเริ่มส่งอีเมลแต่ละที่อยู่ ให้บอกว่าเธรดถูกขัดจังหวะหรือยกเลิกอย่างใดดังนั้นเฟรมเวิร์กความพร้อมใช้งานสูงของคุณจะรีสตาร์ทเธรด จากนั้นเมื่อเธรดเริ่มขึ้นใหม่จะโหลดรายชื่อลูกค้า แต่เนื่องจากการรวบรวมเป็นแบบคงที่จึงอาจเก็บรายการที่อยู่อีเมลจากการรวบรวมก่อนหน้า ตอนนี้ลูกค้าบางคนอาจได้รับอีเมลซ้ำ
Anide: Static สุดท้าย
การใช้“ สแตติกสุดท้าย” นั้นเทียบเท่าจาวาของ C #define ได้อย่างมีประสิทธิภาพแม้ว่าจะมีความแตกต่างของการใช้งานด้านเทคนิค AC / C ++ #define ถูกสับเปลี่ยนจากโค้ดโดยตัวประมวลผลล่วงหน้าก่อนการคอมไพล์ จาวา "คงสุดท้าย" จะจบลงที่หน่วยความจำในกองซ้อน ด้วยวิธีนี้มันจะคล้ายกับตัวแปร "คงที่ const" ใน C ++ มากกว่าที่จะเป็น #define
สรุป
ฉันหวังว่าสิ่งนี้จะช่วยอธิบายเหตุผลเบื้องต้นบางประการว่าทำไมสถิตศาสตร์จึงเป็นปัญหา หากคุณกำลังใช้เฟรมเวิร์ก Java ที่ทันสมัยเช่น Java EE หรือ Spring เป็นต้นคุณอาจไม่พบกับสถานการณ์เหล่านี้หลายอย่าง แต่หากคุณทำงานกับรหัสดั้งเดิมที่มีขนาดใหญ่