ใช้ null ให้ถูกต้อง
null
มีวิธีการที่แตกต่างกันของการใช้เป็น วิธีที่พบมากที่สุดและถูกต้องทางความหมายคือการใช้เมื่อคุณอาจมีหรือไม่มีค่าเดียว ในกรณีนี้ค่าอาจเท่ากับnull
หรือเป็นสิ่งที่มีความหมายเช่นบันทึกจากฐานข้อมูลหรือบางสิ่งบางอย่าง
ในสถานการณ์เหล่านี้คุณส่วนใหญ่ใช้มันในลักษณะนี้ (ในรหัสหลอก):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
ปัญหา
และมันก็มีปัญหาใหญ่มาก ปัญหาคือตามเวลาที่คุณเรียกใช้doSomethingUseful
ค่าอาจไม่ได้รับการตรวจสอบnull
! หากไม่เป็นเช่นนั้นโปรแกรมจะเกิดข้อผิดพลาด และผู้ใช้อาจไม่เห็นข้อความแสดงข้อผิดพลาดใด ๆ ที่ถูกทิ้งไว้กับบางสิ่งเช่น "ข้อผิดพลาดที่น่ากลัว: ค่าที่ต้องการ แต่ได้รับค่าว่าง!" (หลังจากอัปเดต: แม้ว่าอาจมีข้อผิดพลาดที่ให้ข้อมูลน้อยลงเช่นSegmentation fault. Core dumped.
หรือแย่กว่านั้นก็ไม่มีข้อผิดพลาดและการจัดการที่ไม่ถูกต้องในค่า null ในบางกรณี)
การลืมตรวจสอบการเขียนnull
และการจัดการnull
สถานการณ์เป็นปัญหาที่พบบ่อยมาก นี่คือเหตุผลที่ Tony Hoare ผู้คิดค้นnull
กล่าวในการประชุมซอฟต์แวร์ชื่อ QCon London ในปี 2009 ว่าเขาทำผิดพลาดนับพันล้านดอลลาร์ในปี 1965:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- ผิดพลาดโทนี่โฮร์
หลีกเลี่ยงปัญหา
เทคโนโลยีและภาษาบางอย่างทำให้ตรวจสอบสิ่งที่null
เป็นไปไม่ได้ที่จะลืมในวิธีที่ต่างกันลดจำนวนข้อบกพร่อง
ตัวอย่างเช่น Haskell มีMaybe
monad แทนที่จะเป็น nulls สมมติว่าDatabaseRecord
เป็นประเภทที่ผู้ใช้กำหนด ใน Haskell ค่าชนิดMaybe DatabaseRecord
สามารถจะเท่ากับหรือมันอาจจะเท่ากับJust <somevalue>
Nothing
จากนั้นคุณสามารถใช้งานได้หลายวิธี แต่ไม่ว่าคุณจะใช้งานอย่างไรคุณไม่สามารถใช้การดำเนินการบางอย่างNothing
โดยที่ไม่รู้ตัว
เช่นฟังก์ชันนี้เรียกว่าzeroAsDefault
return x
for Just x
และ0
for Nothing
:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl กล่าวว่า C ++ 17 และ Scala มีวิธีการของตนเอง ดังนั้นคุณอาจต้องการลองดูว่าภาษาของคุณมีอะไรแบบนั้นและใช้มันไหม
Nulls ยังคงใช้งานได้อย่างกว้างขวาง
หากคุณไม่มีอะไรที่ดีกว่าการใช้null
ก็ดี เพียงแค่คอยระวัง พิมพ์ประกาศในฟังก์ชั่นจะช่วยให้คุณค่อนข้างอยู่แล้ว
นอกจากนี้อาจฟังดูไม่ค่อยดีนัก แต่คุณควรตรวจสอบว่าเพื่อนร่วมงานของคุณต้องการใช้null
หรืออย่างอื่น พวกเขาอาจจะอนุรักษ์และอาจไม่ต้องการใช้โครงสร้างข้อมูลใหม่ด้วยเหตุผลบางอย่าง ตัวอย่างเช่นการสนับสนุนภาษาที่เก่ากว่า ควรประกาศสิ่งเหล่านี้ในมาตรฐานการเข้ารหัสของโครงการและพูดคุยกับทีมอย่างเหมาะสม
ตามข้อเสนอของคุณ
คุณแนะนำให้ใช้ฟิลด์บูลีนแยกต่างหาก แต่คุณต้องตรวจสอบต่อไปและยังอาจลืมตรวจสอบ ดังนั้นจึงไม่มีอะไรชนะที่นี่ หากคุณสามารถลืมสิ่งอื่นได้เช่นอัปเดตค่าทั้งสองในแต่ละครั้งมันยิ่งแย่กว่าเดิม หากปัญหาในการลืมตรวจสอบnull
ไม่ได้รับการแก้ไขจะไม่มีประเด็น การหลีกเลี่ยงnull
เป็นเรื่องยากและคุณไม่ควรทำในลักษณะที่ทำให้แย่ลง
วิธีที่จะไม่ใช้ null
ในที่สุดก็มีวิธีการทั่วไปในการใช้null
อย่างไม่ถูกต้อง วิธีหนึ่งดังกล่าวคือการใช้แทนโครงสร้างข้อมูลที่ว่างเปล่าเช่นอาร์เรย์และสตริง อาเรย์ที่ว่างเปล่าเป็นอาเรย์ที่เหมาะสมเหมือนอย่างอื่น! มันเกือบจะสำคัญและมีประโยชน์สำหรับโครงสร้างข้อมูลที่สามารถใส่หลายค่าเพื่อให้สามารถว่างเปล่าได้เช่นมีความยาว 0
จากจุดยืนพีชคณิตสตริงว่างสำหรับสตริงนั้นมีค่าเท่ากับ 0 สำหรับตัวเลขนั่นคือเอกลักษณ์:
a+0=a
concat(str, '')=str
สตริงว่างเปล่าทำให้สตริงโดยทั่วไปกลายเป็น monoid:
https://en.wikipedia.org/wiki/Monoid
ถ้าคุณไม่เข้าใจมันไม่ใช่เรื่องสำคัญสำหรับคุณ
ตอนนี้เรามาดูกันว่าเหตุใดการเขียนโปรแกรมด้วยตัวอย่างนี้จึงมีความสำคัญ:
for (element in array) {
doSomething(element);
}
ถ้าเราผ่านอาร์เรย์ที่ว่างเปล่าในที่นี่รหัสจะทำงานได้ดี มันจะไม่ทำอะไรเลย อย่างไรก็ตามถ้าเราผ่านตรงnull
นี้เราก็จะได้รับความผิดพลาดด้วยข้อผิดพลาดเช่น "ไม่สามารถวนผ่านโมฆะขอโทษ" เราห่อได้if
แต่ก็สะอาดน้อยกว่าและอีกครั้งคุณอาจลืมตรวจสอบ
วิธีจัดการ null
สิ่งที่doSomethingAboutIt()
ควรทำและโดยเฉพาะอย่างยิ่งว่าควรจะมีข้อยกเว้นเกิดขึ้นเป็นปัญหาที่ซับซ้อนหรือไม่ ในระยะสั้นมันขึ้นอยู่กับว่าnull
เป็นค่าอินพุตที่ยอมรับได้สำหรับงานที่กำหนดและสิ่งที่คาดหวังในการตอบสนอง ข้อยกเว้นสำหรับเหตุการณ์ที่ไม่คาดคิด ฉันจะไม่ไปต่อในหัวข้อนั้น คำตอบนี้ยาวมากแล้ว
std::optional
Option
ในภาษาอื่นคุณอาจต้องสร้างกลไกที่เหมาะสมด้วยตัวคุณเองหรือคุณอาจหันไปใช้null
หรือสิ่งที่คล้ายกันเพราะมันเป็นสำนวนมากกว่า