อย่าใช้อย่างใดอย่างหนึ่งหากคุณสามารถหลีกเลี่ยงได้ ใช้ฟังก์ชั่นจากโรงงานที่ส่งคืนประเภทตัวเลือก tuple หรือผลรวมเฉพาะ กล่าวอีกนัยหนึ่งแทนค่าที่อาจเป็นค่าเริ่มต้นด้วยประเภทที่แตกต่างจากค่าที่ไม่ได้เป็นค่าเริ่มต้นที่รับประกัน
อันดับแรกให้เขียนรายการ desiderata จากนั้นคิดว่าเราจะแสดงสิ่งนี้ในสองสามภาษา (C ++, OCaml, Python) ได้อย่างไร
- ตรวจจับการใช้วัตถุเริ่มต้นในเวลารวบรวม
- ทำให้ชัดเจนว่าค่าที่กำหนดเป็นค่าเริ่มต้นที่อาจเกิดขึ้นหรือไม่ในขณะที่อ่านรหัส
- เลือกค่าเริ่มต้นของเราที่เหมาะสมถ้ามีหนึ่งครั้งต่อหนึ่งประเภท แน่นอนคุณไม่เลือกเริ่มต้นที่แตกต่างกันที่อาจเกิดขึ้นที่เว็บไซต์โทรทุก
- ทำให้เป็นเรื่องง่ายสำหรับเครื่องมือในการวิเคราะห์แบบคงที่หรือมนุษย์ที่มี
grep
เพื่อค้นหาข้อผิดพลาด
- สำหรับบางแอปพลิเคชันโปรแกรมควรดำเนินการตามปกติหากกำหนดค่าเริ่มต้นโดยไม่คาดคิด สำหรับแอปพลิเคชั่นอื่นโปรแกรมควรหยุดทันทีหากกำหนดค่าเริ่มต้นตามความเหมาะสมในลักษณะที่ให้ข้อมูล
ฉันคิดว่าความตึงเครียดระหว่าง Null Object Pattern และตัวชี้โมฆะมาจาก (5) หากเราสามารถตรวจพบข้อผิดพลาดเร็วพอแม้ว่า (5) จะกลายเป็นที่สงสัย
ลองพิจารณาภาษานี้ตามภาษา:
C ++
ในความคิดของฉันคลาส C ++ โดยทั่วไปควรจะเป็นแบบเริ่มต้นได้เนื่องจากมันลดการโต้ตอบกับไลบรารีและทำให้ง่ายต่อการใช้คลาสในคอนเทนเนอร์ นอกจากนี้ยังลดความซับซ้อนของการสืบทอดเนื่องจากคุณไม่ต้องคิดว่าตัวสร้างซูเปอร์คลาสใดที่จะเรียก
อย่างไรก็ตามหมายความว่าคุณไม่สามารถทราบได้อย่างแน่นอนว่าค่าประเภทMyClass
อยู่ใน "สถานะเริ่มต้น" หรือไม่ นอกจากนี้ยังวางอยู่ในbool nonempty
สนามหรือคล้ายกันกับพื้นผิวเริ่มต้น-Ness ที่รันไทม์ที่ดีที่สุดที่คุณสามารถทำได้คือการผลิตอินสแตนซ์ใหม่MyClass
ในทางที่บังคับให้ผู้ใช้ตรวจสอบมัน
ผมขอแนะนำให้ใช้ฟังก์ชั่นโรงงานที่ส่งกลับstd::optional<MyClass>
, std::pair<bool, MyClass>
หรือการอ้างอิง R-value ไปstd::unique_ptr<MyClass>
ถ้าเป็นไปได้
หากคุณต้องการให้ฟังก์ชั่นจากโรงงานของคุณคืนสถานะ "ตัวยึดตำแหน่ง" บางอย่างที่แตกต่างจากค่าเริ่มต้นที่สร้างMyClass
ให้ใช้std::pair
และให้แน่ใจว่าเอกสารที่ฟังก์ชั่นของคุณทำ
หากฟังก์ชั่นจากโรงงานมีชื่อที่ไม่ซ้ำกันมันจะง่ายgrep
สำหรับชื่อและค้นหาความเลอะเทอะ แต่ก็เป็นเรื่องยากที่จะgrep
สำหรับกรณีที่โปรแกรมเมอร์ควรจะใช้ฟังก์ชั่นโรงงาน แต่ไม่ได้
OCaml
หากคุณกำลังใช้ภาษาอย่าง OCaml คุณสามารถใช้option
ประเภท (ในไลบรารีมาตรฐานของ OCaml) หรือeither
ประเภท (ไม่ใช่ในไลบรารีมาตรฐาน แต่ง่ายต่อการม้วนของคุณเอง) หรือdefaultable
ประเภท (ฉันกำลังทำคำศัพท์defaultable
)
type 'a option =
| None
| Some of 'a
type ('a, 'b) either =
| Left of 'a
| Right of 'b
type 'a defaultable =
| Default of 'a
| Real of 'a
A defaultable
ดังที่แสดงไว้ข้างต้นดีกว่าคู่เนื่องจากผู้ใช้ต้องจับคู่รูปแบบเพื่อแยก'a
และไม่สามารถละเลยองค์ประกอบแรกของคู่ได้
ชนิดที่เทียบเท่าdefaultable
ดังที่แสดงด้านบนสามารถใช้ใน C ++ โดยใช้ a ที่std::variant
มีอินสแตนซ์สองประเภทเดียวกัน แต่บางส่วนของstd::variant
API ไม่สามารถใช้ได้หากทั้งสองประเภทเหมือนกัน นอกจากนี้ยังเป็นการใช้งานที่แปลกstd::variant
เนื่องจากตัวสร้างชนิดไม่มีชื่อ
หลาม
คุณไม่ได้รับการรวบรวมเวลาตรวจสอบใด ๆ สำหรับ Python แต่การพิมพ์แบบไดนามิกโดยทั่วไปจะไม่มีสถานการณ์ที่คุณต้องการอินสแตนซ์ตัวยึดตำแหน่งของบางประเภทเพื่อปิดปากคอมไพเลอร์
ฉันอยากจะแนะนำเพียงแค่โยนข้อยกเว้นเมื่อคุณจะถูกบังคับให้สร้างอินสแตนซ์เริ่มต้น
หากไม่เป็นที่ยอมรับฉันขอแนะนำให้สร้างสิ่งDefaultedMyClass
ที่สืบทอดมาMyClass
และส่งคืนจากฟังก์ชันโรงงานของคุณ นั่นเป็นการซื้อความยืดหยุ่นของคุณในแง่ของฟังก์ชั่น "ตัดออก" ในกรณีที่ผิดนัดหากคุณต้องการ