เป็นการดีกว่าที่จะส่งคืนค่า NULL หรือค่าว่างจากฟังก์ชัน / เมธอดที่ไม่มีค่าส่งคืนใช่หรือไม่


135

ฉันกำลังมองหาคำแนะนำที่นี่ ฉันกำลังดิ้นรนกับว่ามันจะดีกว่าที่จะกลับเป็นโมฆะหรือค่าที่ว่างเปล่าจากวิธีการเมื่อไม่มีค่าตอบแทนหรือไม่สามารถหาได้

ใช้สองวิธีต่อไปนี้เป็นตัวอย่าง:

string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID)    // finds a Person with a matching personID.

ในReverseString()ฉันจะพูดกลับสตริงที่ว่างเปล่าเพราะประเภทกลับเป็นสตริงดังนั้นผู้โทรคาดหวังว่า ด้วยวิธีนี้ผู้โทรจะไม่ต้องตรวจสอบเพื่อดูว่ากลับเป็นโมฆะ

ในการFindPerson()คืนค่า NULL ดูเหมือนว่าเหมาะสมดีกว่า ไม่ว่า NULL หรือวัตถุ Person ว่างเปล่า ( new Person()) จะถูกส่งคืนหรือไม่ผู้โทรจะต้องตรวจสอบเพื่อดูว่าวัตถุบุคคลนั้นว่างเปล่าหรือว่างเปล่าก่อนที่จะทำอะไรกับมัน (เช่นการโทรUpdateName()) ดังนั้นทำไมไม่เพียงแค่ส่งคืนค่า NULL ที่นี่จากนั้นผู้โทรต้องตรวจสอบค่า NULL เท่านั้น

ใครบ้างที่ต่อสู้กับสิ่งนี้? ความช่วยเหลือหรือข้อมูลเชิงลึกใด ๆ ที่ชื่นชม


2
มันจะแตกต่างกันไป คุณสามารถยืนยันว่าวิธีการควรหยุดค่าที่ไม่ถูกต้องล่วงหน้าตัวอย่างเช่นพารามิเตอร์ stringToReverse ของคุณจะถูกส่งผ่านเป็นว่างเปล่าหรือเป็นโมฆะ หากคุณทำการตรวจสอบนั้น (โยนกลับยกเว้น) มันจะส่งคืนสิ่งที่ไม่ใช่ null
Aaron McIver

พรานล่า POEAA - หน้า 496 รูปแบบพื้นฐาน - กรณีพิเศษ (วัตถุ null) Bloch Effective Java รุ่นที่ 2 รายการ 43: ส่งคืนอาร์เรย์หรือคอลเลกชันที่ว่างเปล่าไม่ใช่ null
Boris Treukhov

1
@ Boris ฉันไม่เห็นความคิดเห็นของคุณ ที่จริงฉันเพิ่งโพสต์กรณีพิเศษเป็นคำตอบ
โธมัสโอเวนส์

@Thomas ฉันไม่เห็นปัญหาใด ๆ ในนั้น :) สำหรับคำถามมันเป็นเรื่องของสามัญสำนึกอยู่แล้ว
Boris Treukhov

ความคิดเห็นที่น่าสนใจคือในฐานข้อมูล Oracle NULL และสตริงว่างเปล่าเหมือนกัน
WuHoUnited

คำตอบ:


77

StackOverflow มีการสนทนาที่ดีเกี่ยวกับหัวข้อที่แน่นอนในคำถาม & คำตอบนี้ ในคำถามที่ได้คะแนนสูงสุด kronoz ตั้งข้อสังเกต:

การส่งคืนค่า Null เป็นความคิดที่ดีที่สุดหากคุณต้องการระบุว่าไม่มีข้อมูล

วัตถุที่ว่างหมายถึงข้อมูลถูกส่งคืนในขณะที่การส่งคืนเป็นโมฆะอย่างชัดเจนบ่งชี้ว่าไม่มีสิ่งใดถูกส่งคืน

นอกจากนี้การคืนค่า null จะส่งผลให้เกิดข้อยกเว้นเป็นโมฆะหากคุณพยายามเข้าถึงสมาชิกในวัตถุซึ่งอาจเป็นประโยชน์ในการเน้นรหัสบั๊กกี้ - การพยายามเข้าถึงสมาชิกโดยไม่ทำสิ่งใด การเข้าถึงสมาชิกของวัตถุที่ว่างเปล่าจะไม่ล้มเหลวหมายความว่าบั๊กสามารถไม่ถูกค้นพบได้

โดยส่วนตัวแล้วฉันชอบส่งคืนสตริงว่างสำหรับฟังก์ชั่นที่คืนค่าสตริงเพื่อลดจำนวนการจัดการข้อผิดพลาดที่จำเป็นต้องใช้แทน อย่างไรก็ตามคุณจะต้องตรวจสอบให้แน่ใจว่ากลุ่มที่คุณทำงานด้วยจะปฏิบัติตามแบบแผนเดียวกันมิฉะนั้นประโยชน์ของการตัดสินใจนี้จะไม่สำเร็จ

อย่างไรก็ตามตามที่ผู้โพสต์ในคำตอบ SO ระบุไว้อาจเป็นโมฆะถ้าวัตถุถูกคาดหวังเพื่อให้มีข้อสงสัยเกี่ยวกับว่าข้อมูลจะถูกส่งกลับ

ในท้ายที่สุดไม่มีวิธีที่ดีที่สุดในการทำสิ่งต่าง ๆ การสร้างฉันทามติของทีมในท้ายที่สุดจะผลักดันแนวทางปฏิบัติที่ดีที่สุดของทีม


8
โดยส่วนตัวแล้วฉันชอบส่งคืนสตริงว่างสำหรับฟังก์ชั่นที่คืนค่าสตริงเพื่อลดจำนวนการจัดการข้อผิดพลาดที่จำเป็นต้องใช้แทน ไม่เข้าใจอาร์กิวเมนต์นี้ การตรวจสอบค่า Null คืออะไรอย่างน้อย <10 ตัวอักษร? ทำไมเราถึงทำเช่นนี้เป็นงานดัดโค้ง?
Aaron McIver

19
ไม่มีใครพูดว่ามันเป็นงานดัดด้านหลัง แต่มันเป็นงานหนัก มันเพิ่มกรณีพิเศษให้กับรหัสซึ่งหมายถึงอีกหนึ่งสาขาในการทดสอบ นอกจากนี้ยังขัดขวางการไหลของรหัส for x in list_of_things() {...}เนื้อหานั้นเร็วกว่าที่จะคร่ำครวญl = list_of_things(); if l != null {...}
Bryan Oakley

5
@Bryan: มีความแตกต่างใหญ่ระหว่างรายการค่าว่างและสตริงว่าง สตริงที่ไม่ค่อยแสดงรายการของตัวละคร
วินไคลน์

3
@kevin cline: แน่นอน แต่ในกรณีของสตริงคุณอาจต้องการให้สตริงนั้นปรากฏในบันทึกว่าเป็นโมฆะหรือไม่ ต้องตรวจสอบว่ามันเป็นโมฆะเพื่อให้คุณสามารถพิมพ์สตริงว่าง obfuscates เจตนาที่แท้จริง หรืออาจเป็นฟิลด์ฐานข้อมูลที่มีเนื้อหาที่ผู้ใช้สนับสนุนซึ่งคุณต้องการเพิ่มลงในเว็บเพจ ทำได้ง่ายemit(user.hometown)กว่าif user.hometown == null { emit("") else {emit(user.hometown)}
Bryan Oakley

1
ไม่เพียง แต่จะพิมพ์พิเศษ (ไม่ใช่เรื่องใหญ่) แต่มันมีผลต่อความสามารถในการอ่าน ฉันค้นหารหัสด้วยพวงของการตรวจสอบโมฆะพวง
ToddR

82

ในรหัสทั้งหมดที่ฉันเขียนฉันหลีกเลี่ยงการส่งคืนnullจากฟังก์ชัน ผมอ่านที่อยู่ในรหัสสะอาด

ปัญหาเกี่ยวกับการใช้nullคือคนที่ใช้อินเทอร์เฟซไม่รู้ว่าnullเป็นผลลัพธ์ที่เป็นไปได้หรือไม่และต้องตรวจสอบหรือไม่เพราะไม่มีnot nullประเภทอ้างอิง

ใน F # คุณสามารถส่งคืนoptionประเภทซึ่งอาจเป็นsome(Person)หรือnoneดังนั้นจึงเป็นที่ชัดเจนสำหรับผู้โทรที่พวกเขาต้องตรวจสอบ

รูปแบบ C # แบบแอนะล็อก (ต่อต้าน -) เป็นTry...วิธีการ:

public bool TryFindPerson(int personId, out Person result);

ตอนนี้ฉันรู้ว่าคนบอกว่าพวกเขาเกลียดTry...รูปแบบเพราะมีพารามิเตอร์การแสดงผลแบ่งความคิดของฟังก์ชันบริสุทธิ์ แต่จริงๆไม่แตกต่างจาก:

class FindResult<T>
{
   public FindResult(bool found, T result)
   {
       this.Found = found;
       this.Result = result;
   }

   public bool Found { get; private set; }
   // Only valid if Found is true
   public T Result { get; private set;
}

public FindResult<Person> FindPerson(int personId);

... และตามจริงแล้วคุณสามารถสันนิษฐานได้ว่าโปรแกรมเมอร์. NET ทุกคนรู้เกี่ยวกับTry...รูปแบบเพราะมันถูกใช้ภายในโดย. NET Framework ซึ่งหมายความว่าพวกเขาไม่จำเป็นต้องอ่านเอกสารเพื่อทำความเข้าใจกับสิ่งที่มันทำซึ่งสำคัญสำหรับฉันมากกว่ายึดติดกับมุมมองของฟังก์ชั่นที่พิถีพิถัน (การเข้าใจว่าresultเป็นoutพารามิเตอร์ไม่ใช่refพารามิเตอร์)

ดังนั้นฉันจะไปด้วยTryFindPersonเพราะคุณดูเหมือนจะบ่งบอกว่ามันเป็นเรื่องปกติอย่างสมบูรณ์แบบที่จะไม่พบมัน

หากในอีกทางหนึ่งไม่มีเหตุผลเชิงตรรกะที่ผู้โทรจะให้personIdที่ไม่ได้มีอยู่ฉันอาจจะทำสิ่งนี้:

public Person GetPerson(int personId);

... แล้วฉันจะโยนข้อยกเว้นถ้ามันไม่ถูกต้อง Get...คำนำหน้าหมายความว่าผู้ที่โทรมารู้ว่ามันควรจะประสบความสำเร็จ


28
+1 ฉันจะตอบคำถามนี้โดยอ้างอิงถึง Clean Code หากคุณยังไม่ได้ดำเนินการ ฉันชอบมนต์: "หากฟังก์ชั่นของคุณไม่สามารถบอกได้ว่ามันเป็นชื่ออะไรแล้วโยนข้อยกเว้น" Muuuch ดีกว่าการตรวจสอบทุกสาย null โหลหรือดังนั้น ....
เกรแฮม

13
ฉันเลิกสมมติอะไรเกี่ยวกับความรู้ของผู้คน
CaffGeek

5
ปัญหาของการใช้ null คือผู้ใช้อินเทอร์เฟซไม่รู้ว่า null เป็นผลลัพธ์ที่เป็นไปได้หรือไม่และต้องตรวจสอบหรือไม่เพราะไม่มีประเภทอ้างอิง null เนื่องจากประเภทการอ้างอิงอาจเป็นค่าว่างคุณจึงไม่สามารถสันนิษฐานได้ว่าผลการทดสอบที่ได้จะเป็นค่าว่างหรือไม่
Tommy Carlier

4
หากฟังก์ชันส่งคืนพอยน์เตอร์ (C / C ++) หรือการอ้างอิงไปยังออบเจกต์ (Java) ฉันมักจะถือว่ามันอาจส่งคืน null
Giorgio

7
"ปัญหาของการใช้ null คือผู้ที่ใช้อินเตอร์เฟสไม่ทราบว่า null เป็นผลลัพธ์ที่เป็นไปได้หรือไม่และพวกเขาต้องตรวจสอบมันหรือไม่ [... ]" หากวิธีการหนึ่งอาจส่งคืน null ก็ควรจะชัดเจน กล่าวถึงเป็นส่วนหนึ่งของสัญญา API
Zsolt Török

28

คุณสามารถลองรูปแบบกรณีพิเศษของ Martin Fowler ได้จากPaterns of Enterprise Application Architecture :

Nulls เป็นสิ่งที่น่าอึดอัดใจในโปรแกรมเชิงวัตถุเพราะมันเอาชนะความหลากหลายได้ โดยปกติคุณสามารถเรียก foo ได้อย่างอิสระจากการอ้างอิงตัวแปรของประเภทที่กำหนดโดยไม่ต้องกังวลว่ารายการนั้นเป็นประเภทที่แน่นอนหรือเป็นคลาสย่อย ด้วยภาษาที่พิมพ์อย่างยิ่งคุณสามารถตรวจสอบคอมไพเลอร์ว่าการโทรนั้นถูกต้อง อย่างไรก็ตามเนื่องจากตัวแปรสามารถมีค่า Null ได้คุณอาจพบข้อผิดพลาดรันไทม์โดยเรียกใช้ข้อความเป็นค่า Null ซึ่งจะช่วยให้คุณติดตามสแต็กได้ง่ายและเป็นมิตร

หากเป็นไปได้ที่ตัวแปรจะเป็นโมฆะคุณต้องจำไว้ว่าต้องล้อมรอบด้วยรหัสทดสอบว่างดังนั้นคุณจะทำสิ่งที่ถูกต้องหากมีค่าว่าง บ่อยครั้งที่สิ่งที่ถูกต้องเหมือนกันในหลาย ๆ บริบทดังนั้นคุณจึงต้องเขียนโค้ดที่คล้ายกันในหลาย ๆ ที่ - ยอมรับบาปของการทำสำเนารหัส

Nulls เป็นตัวอย่างที่พบบ่อยของปัญหาดังกล่าว ในระบบตัวเลขคุณต้องจัดการกับอนันต์ซึ่งมีกฎพิเศษสำหรับสิ่งต่าง ๆ เช่นการเพิ่มที่ทำลายค่าคงที่ตามปกติของจำนวนจริง หนึ่งในประสบการณ์ครั้งแรกของฉันในซอฟต์แวร์ธุรกิจคือกับลูกค้ายูทิลิตี้ที่ยังไม่รู้จักอย่างสมบูรณ์ซึ่งเรียกว่า "ผู้ครอบครอง" สิ่งเหล่านี้บ่งบอกถึงการเปลี่ยนแปลงพฤติกรรมปกติของประเภท

แทนที่จะส่งคืนค่า Null หรือค่าคี่ให้ส่งคืน Case พิเศษที่มีอินเทอร์เฟซเดียวกับที่ผู้เรียกคาดหวัง


ฉันวิ่งเข้าไปในสถานการณ์นี้ แต่หลังจากเปิดตัวซอฟต์แวร์แล้วเท่านั้น ข้อมูลการทดสอบของฉันไม่เคยถูกเรียกมันดังนั้นจึงเป็นบิตจากสีน้ำเงินและความเจ็บปวดในการแก้ปัญหา แม้ว่าฉันจะไม่ได้แก้มันด้วยรูปแบบเคสพิเศษ แต่ก็เป็นเรื่องดีที่ควรระวัง
samis

14

ฉันคิดว่าReverseString()จะกลับสตริงตรงกันข้ามและมันจะโยนถ้าผ่านในIllegalArgumentExceptionNull

ฉันคิดว่าFindPerson()ควรทำตามรูปแบบ NullObjectหรือเพิ่มข้อยกเว้นที่ไม่ถูกตรวจสอบเกี่ยวกับการไม่พบบางสิ่งถ้าคุณควรจะสามารถค้นหาบางสิ่งได้เสมอ

ต้องจัดการกับNullเป็นสิ่งที่ควรหลีกเลี่ยง มีNullภาษาถูกเรียกว่าผิดพลาดพันล้านดอลลาร์โดยนักประดิษฐ์มัน!

ฉันเรียกว่าความผิดพลาดพันล้านดอลลาร์ของฉัน มันเป็นสิ่งประดิษฐ์ของการอ้างอิงโมฆะในปี 1965 ในขณะนั้นฉันกำลังออกแบบระบบการพิมพ์แบบครอบคลุมครั้งแรกสำหรับการอ้างอิงในภาษาเชิงวัตถุ (ALGOL W)

เป้าหมายของฉันคือเพื่อให้แน่ใจว่าการใช้การอ้างอิงทั้งหมดควรจะปลอดภัยอย่างยิ่งโดยการตรวจสอบดำเนินการโดยอัตโนมัติโดยคอมไพเลอร์ แต่ฉันไม่สามารถต้านทานสิ่งล่อใจที่จะใส่ในการอ้างอิงโมฆะเพียงเพราะมันง่ายที่จะใช้

สิ่งนี้นำไปสู่ข้อผิดพลาดมากมายช่องโหว่และระบบล่มซึ่งอาจทำให้เกิดความเจ็บปวดและความเสียหายนับพันล้านดอลลาร์ในช่วงสี่สิบปีที่ผ่านมา

ในช่วงไม่กี่ปีที่ผ่านมามีการใช้โปรแกรมวิเคราะห์จำนวนมากเช่น PREfix และ PREfast ใน Microsoft เพื่อตรวจสอบข้อมูลอ้างอิงและให้คำเตือนหากมีความเสี่ยงที่อาจไม่เป็นโมฆะ ภาษาการเขียนโปรแกรมล่าสุดเช่น Spec # ได้แนะนำการประกาศสำหรับการอ้างอิงที่ไม่เป็นนัล นี่คือทางออกซึ่งฉันปฏิเสธในปี 1965

Tony Hoare


11

กลับตัวเลือก ประโยชน์ทั้งหมดของการคืนค่าที่ไม่ถูกต้องที่แตกต่างกัน (เช่นความสามารถในการมีค่าว่างในคอลเลกชันของคุณ) โดยไม่มีความเสี่ยงของ NullPointerException


1
น่าสนใจ วิธีการใช้งานตัวเลือกใน Java? และใน C ++
Giorgio

1
@Giorgio สำหรับ Java, ห้องสมุดฝรั่งจาก Google ได้ระดับที่เรียกว่าเป็นตัวเลือก
Otavio Macedo

1
สำหรับ C ++ มีboost::optional<T>
MSalters

8

ฉันเห็นทั้งสองด้านของการโต้แย้งนี้และฉันรู้ว่าเสียงบางอย่างที่มีอิทธิพลมากกว่า (เช่น Fowler) ไม่สนับสนุนการส่งคืน null เพื่อรักษาโค้ดให้สะอาดหลีกเลี่ยงการบล็อกข้อผิดพลาดพิเศษ ฯลฯ

อย่างไรก็ตามฉันมักจะเข้าข้างกับผู้เสนอคืนโมฆะ ฉันพบว่ามีความแตกต่างที่สำคัญในการเรียกใช้วิธีการและมันตอบสนองกับฉันไม่มีข้อมูลใด ๆและมันตอบสนองกับฉันมีสตริงที่ว่างเปล่านี้

เนื่องจากฉันได้เห็นการสนทนาบางอย่างที่อ้างอิงคลาสบุคคลให้ลองพิจารณาสถานการณ์ที่คุณพยายามค้นหาตัวอย่างของคลาส หากคุณผ่านแอททริบิวต์ตัวค้นหาบางตัว (เช่น ID) ลูกค้าสามารถตรวจสอบค่าว่างได้ทันทีเพื่อดูว่าไม่พบค่าใด ๆ สิ่งนี้ไม่จำเป็นต้องพิเศษ (ดังนั้นจึงไม่จำเป็นต้องมีข้อยกเว้น) แต่ควรมีการบันทึกไว้อย่างชัดเจน ใช่มันต้องใช้ความเข้มงวดในส่วนของลูกค้าและไม่ฉันไม่คิดว่ามันเป็นสิ่งที่ไม่ดีเลย

ตอนนี้ให้พิจารณาทางเลือกที่คุณส่งคืนออบเจกต์ Person ที่ถูกต้อง ... ซึ่งไม่มีอะไรอยู่ในนั้น คุณใส่ค่านัลในค่าทั้งหมด (ชื่อ, ที่อยู่, FavoriteDrink) หรือคุณเติมข้อมูลเหล่านั้นด้วยวัตถุที่ถูกต้อง แต่ว่างเปล่าหรือไม่? ลูกค้าของคุณจะทราบได้อย่างไรว่าไม่พบบุคคลจริง พวกเขาจำเป็นต้องตรวจสอบว่าชื่อเป็นสตริงว่างเปล่าแทนที่จะเป็นค่าว่างหรือไม่? การเรียงลำดับของสิ่งนี้จริง ๆ แล้วจะนำไปสู่ความยุ่งเหยิงของรหัสและข้อความสั่งที่มีเงื่อนไขมากกว่านั้นหรือไม่มากกว่าที่เราจะตรวจสอบค่าว่างและย้ายไป

อีกครั้งมีจุดที่ทั้งสองด้านของการโต้แย้งนี้ฉันสามารถเห็นด้วยกับ แต่ฉันพบว่ามันทำให้รู้สึกถึงคนส่วนใหญ่ (ทำให้รหัสบำรุงรักษามากขึ้น)


ความแตกต่างคือ "ทำFindPersonหรือGetPersonคืนค่า null หรือส่งข้อยกเว้นถ้าไม่มีรหัสหรือไม่" ในฐานะผู้ใช้ API ฉันไม่รู้และต้องตรวจสอบเอกสาร ในทางกลับกันbool TryGetPerson(Guid key, out Person person)ฉันไม่จำเป็นต้องตรวจสอบเอกสารและฉันรู้ว่ามีข้อยกเว้นที่ไม่ได้เกิดขึ้นในสิ่งที่ทำให้เกิดสถานการณ์ที่ไม่ธรรมดา นั่นคือจุดที่ฉันพยายามทำในคำตอบของฉัน
Scott Whitlock

4
ฉันคิดว่าผู้คนมีความผูกพันกับข้อยกเว้นมากเกินไป ข้อยกเว้นไม่ดีและสิ้นเปลืองทรัพยากร ไม่พูดถึงคนที่จะลองและจับงบโดยไม่ต้องแก้ไขอะไร ข้อยกเว้นเป็นสัญญาณที่ชัดเจนว่ามีบางอย่างผิดปกติ ยกตัวอย่างเช่นการอ้างอิงโมฆะโยนข้อยกเว้นเพราะมันผิดอย่างชัดเจนในการอ้างอิงวัตถุโมฆะ มันผิดที่จะใช้เพื่อตรวจสอบว่ามีคนพบหรือไม่
Vladimir Kocjancic

1
@VladimirKocjancic: ฉันเห็นด้วยอย่างยิ่ง: ควรใช้ข้อยกเว้นสำหรับสถานการณ์พิเศษเช่นข้อบกพร่องซอฟต์แวร์ความล้มเหลวของฮาร์ดแวร์การกำหนดค่าระบบผิดพลาด หากคุณกำลังคำนวณฟังก์ชั่นบางส่วน (เช่นfindPerson) มันเป็นเรื่องปกติที่จะไม่ได้ผลลัพธ์ สิ่งนี้ไม่ควรนำไปสู่ข้อยกเว้น
Giorgio

6

ส่งคืน null ถ้าคุณจำเป็นต้องรู้ว่ามีรายการอยู่หรือไม่ มิฉะนั้นส่งคืนชนิดข้อมูลที่คาดหวัง โดยเฉพาะอย่างยิ่งหากคุณส่งคืนรายการ โดยทั่วไปถือว่าปลอดภัยหากผู้โทรต้องการรายการพวกเขาจะต้องการวนซ้ำรายการ หลายภาษา (มากที่สุด? ทุกภาษา) ล้มเหลวถ้าคุณพยายามที่จะทำซ้ำมากกว่าเป็นโมฆะ แต่ไม่เมื่อคุณทำซ้ำในรายการที่ว่างเปล่า

ฉันคิดว่ามันน่าหงุดหงิดที่จะลองใช้รหัสเช่นนี้เพื่อล้มเหลวเท่านั้น:

for thing in get_list_of_things() {
    do_something_clever(thing)
}

ฉันไม่ควรเพิ่มเคสพิเศษสำหรับเคสเมื่อไม่มีสิ่งใด


7
ในกรณีที่คาดว่าจะมีรายการหรือกลุ่มของค่าอื่นฉันมักจะเลือกที่จะส่งคืนรายการที่ว่างเปล่าด้วยเหตุผลนี้ การกระทำเริ่มต้น (วนซ้ำในรายการที่ว่างเปล่า) เกือบจะถูกต้องเสมอและหากไม่เป็นเช่นนั้นผู้โทรสามารถตรวจสอบได้อย่างชัดเจนเพื่อดูว่ารายการว่างเปล่าหรือไม่
TMN

5

มันขึ้นอยู่กับความหมายของวิธีการ คุณอาจระบุวิธีการของคุณยอมรับได้เฉพาะ "ไม่เป็นโมฆะ" ใน Java คุณสามารถประกาศว่าใช้คำอธิบายประกอบข้อมูลเมตา:

string ReverseString(@NotNull String stringToReverse)

คุณควรระบุค่าส่งคืน หากคุณประกาศคุณยอมรับเฉพาะค่า NotNull คุณจะถูกผูกไว้เพื่อส่งคืนสตริงว่าง (ถ้าอินพุตเป็นสตริงว่างเกินไป)

กรณีที่สองมีความซับซ้อนเล็กน้อยในความหมายของมัน หากวิธีนี้คือการส่งคืนบุคคลโดยเป็นคีย์หลัก (และคาดว่าจะมีอยู่) วิธีที่ดีกว่าคือการยกเว้น

Person FindPerson(int personID)

หากคุณค้นหาบุคคลด้วยรหัสที่เดาได้ (ซึ่งดูเหมือนว่าแปลกสำหรับฉัน) คุณควรประกาศว่าวิธีนี้เป็น @Nullable


3

ฉันอยากจะแนะนำให้ใช้รูปแบบ Null Object ทุกครั้งที่ทำได้ มันลดความซับซ้อนของรหัสเมื่อเรียกวิธีการของคุณและคุณจะไม่ได้อ่านโค้ดที่น่าเกลียดอย่างนี้

if (someObject != null && someObject.someMethod () != whateverValue)

ตัวอย่างเช่นหากวิธีการส่งคืนคอลเลกชันของวัตถุที่เหมาะสมกับรูปแบบบางอย่างแล้วการส่งคืนคอลเลกชันที่ว่างเปล่านั้นสมเหตุสมผลกว่าการกลับมาnullและการวนซ้ำคอลเลกชันที่ว่างเปล่านี้จะไม่มีประสิทธิภาพในการปรับ อีกกรณีหนึ่งจะเป็นวิธีการที่ส่งกลับเช่นชั้นที่ใช้ในการบันทึกข้อมูลกลับวัตถุ Null มากกว่าเป็นที่นิยมในความคิดของฉันที่มันไม่ได้บังคับให้ผู้ใช้ตรวจสอบเสมอถ้าอ้างอิงกลับมาเป็นnullnull

ในกรณีที่การกลับมาnullทำให้รู้สึก (เรียกfindPerson ()วิธีการเช่น) ฉันพยายามอย่างน้อยให้วิธีการที่ส่งกลับถ้าวัตถุที่มีอยู่ ( personExists (int personId)ตัวอย่าง) อีกตัวอย่างจะเป็นcontainsKey ()วิธีการMapในจาวา) มันทำให้รหัสของผู้โทรสะอาดขึ้นเนื่องจากคุณสามารถเห็นได้อย่างง่ายดายว่ามีความเป็นไปได้ที่วัตถุที่ต้องการอาจไม่พร้อมใช้งาน (ไม่มีคนไม่มีรหัสอยู่ในแผนที่) ตรวจสอบอย่างต่อเนื่องว่าการอ้างอิงnullทำให้งงงวยรหัสในความคิดของฉัน


3

null เป็นสิ่งที่ดีที่สุดที่จะส่งคืนถ้าหากมีเงื่อนไขใด ๆ ต่อไปนี้:

  • ผล null เป็นที่คาดว่าจะดำเนินการตามปกติ อาจเป็นไปได้ว่าคุณอาจไม่สามารถค้นหาบุคคลในบางกรณีที่เหมาะสมดังนั้น findPerson () ที่ส่งคืนค่า null นั้นใช้ได้ อย่างไรก็ตามถ้ามันเป็นความล้มเหลวที่ไม่คาดคิดอย่างแท้จริง (เช่นในฟังก์ชั่นที่เรียกว่า saveMyImportantData ()) ดังนั้นคุณควรจะโยนข้อยกเว้น มีคำใบ้ในชื่อ - ข้อยกเว้นสำหรับสถานการณ์พิเศษ!
  • null ถูกนำมาใช้เพื่อหมายถึง"ไม่พบ / ไม่มีค่า" หากคุณหมายถึงอย่างอื่นแล้วส่งคืนสิ่งอื่น! ตัวอย่างที่ดีคือการดำเนินการจุดลอยตัวที่ส่งคืนอินฟินิตี้หรือน่าน - นี่คือค่าที่มีความหมายเฉพาะดังนั้นมันจะสับสนมากถ้าคุณส่งคืนโมฆะที่นี่
  • ฟังก์ชันนี้มีจุดประสงค์เพื่อส่งคืนค่าเดียวเช่น findPerson () ถ้ามันถูกออกแบบมาเพื่อส่งคืนคอลเลกชันเช่น findAllOldPeople () คอลเลกชันที่ว่างเปล่านั้นดีที่สุด ควันหลงของเรื่องนี้ก็คือฟังก์ชั่นซึ่งผลตอบแทนคอลเลกชันที่ไม่ควรกลับ null

นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณบันทึกข้อเท็จจริงที่ว่าฟังก์ชั่นนั้นสามารถคืนค่าว่างได้

ถ้าคุณทำตามกฎเหล่านี้โมฆะส่วนใหญ่จะไม่เป็นอันตราย โปรดทราบว่าถ้าคุณลืมตรวจสอบค่าว่างคุณจะได้รับ NullPointerException ทันทีหลังจากนั้นซึ่งเป็นข้อผิดพลาดที่ค่อนข้างง่ายในการแก้ไข วิธีการที่ล้มเหลวอย่างรวดเร็วนั้นดีกว่าการมีค่าส่งคืนปลอม (เช่นสตริงว่าง) ซึ่งแพร่กระจายอย่างเงียบ ๆ รอบระบบของคุณอาจทำให้ข้อมูลเสียหาย

สุดท้ายถ้าคุณใช้กฎเหล่านี้กับฟังก์ชั่นทั้งสองที่ระบุไว้ในคำถาม:

  • FindPerson - มันจะเหมาะสมสำหรับฟังก์ชั่นนี้เพื่อกลับเป็นโมฆะถ้าไม่พบบุคคล
  • ReverseString - ดูเหมือนว่าจะไม่ล้มเหลวในการค้นหาผลลัพธ์หากผ่านสตริง (เนื่องจากสตริงทั้งหมดสามารถย้อนกลับได้รวมถึงสตริงว่าง) ดังนั้นจึงไม่ควรกลับเป็นโมฆะ หากมีบางอย่างผิดปกติ (หน่วยความจำไม่เพียงพอ) ก็ควรมีข้อยกเว้น

1

ในความคิดของฉันมีความแตกต่างระหว่างการคืนค่า NULL คืนผลลัพธ์เปล่าบางอย่าง (เช่นสตริงว่างหรือรายการว่าง) และส่งข้อยกเว้น

ฉันมักจะใช้วิธีการดังต่อไปนี้ ฉันพิจารณาฟังก์ชั่นหรือวิธีการ f (v1, ... , vn) การโทรเป็นแอปพลิเคชันของฟังก์ชั่น

f : S x T1 x ... x Tn -> T

โดยที่ S คือ "สถานะของโลก" T1, ... , Tn เป็นประเภทของพารามิเตอร์อินพุตและ T คือชนิดส่งคืน

ฉันพยายามกำหนดฟังก์ชั่นนี้ก่อน ถ้าฟังก์ชั่นเป็นบางส่วน (เช่นมีค่าอินพุตบางอย่างที่ไม่ได้กำหนดไว้) ฉันกลับ NULL เพื่อส่งสัญญาณนี้ นี่เป็นเพราะฉันต้องการให้การคำนวณสิ้นสุดลงตามปกติและบอกฉันว่าฟังก์ชั่นที่ฉันร้องขอไม่ได้ถูกกำหนดไว้ในอินพุตที่กำหนด การใช้เช่นสตริงว่างเปล่าเป็นค่าส่งคืนนั้นคลุมเครือเพราะอาจเป็นไปได้ว่าฟังก์ชันถูกกำหนดไว้ในอินพุตและสตริงว่างเป็นผลลัพธ์ที่ถูกต้อง

ฉันคิดว่าการตรวจสอบพิเศษสำหรับตัวชี้ NULL ในรหัสการโทรเป็นสิ่งจำเป็นเพราะคุณใช้ฟังก์ชั่นบางส่วนและมันเป็นงานของวิธีการที่เรียกว่าจะบอกคุณว่าฟังก์ชั่นถ้าไม่ได้กำหนดไว้สำหรับการป้อนข้อมูลที่กำหนด

ฉันต้องการใช้ข้อยกเว้นสำหรับข้อผิดพลาดที่ไม่อนุญาตให้ดำเนินการคำนวณ (เช่นไม่สามารถหาคำตอบได้)

ตัวอย่างเช่นสมมติว่าฉันมีลูกค้าระดับและฉันต้องการใช้วิธีการ

Customer findCustomer(String customerCode)

เพื่อค้นหาลูกค้าในฐานข้อมูลแอปพลิเคชันด้วยรหัส ในวิธีนี้ฉันจะ

  • ส่งคืนออบเจ็กต์ของลูกค้าระดับถ้าแบบสอบถามประสบความสำเร็จ
  • ส่งคืน null ถ้าแบบสอบถามไม่พบลูกค้าใด ๆ
  • ส่งข้อยกเว้นหากไม่สามารถเชื่อมต่อกับฐานข้อมูลได้

การตรวจสอบพิเศษสำหรับโมฆะเช่น

Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
    ...
}

เป็นส่วนหนึ่งของความหมายของสิ่งที่ฉันกำลังทำและฉันจะไม่เพียง "ข้ามพวกเขา" เพื่อให้รหัสอ่านดีขึ้น ฉันไม่คิดว่ามันเป็นวิธีปฏิบัติที่ดีในการลดความซับซ้อนของปัญหาในมือเพื่อลดความซับซ้อนของรหัส

แน่นอนเนื่องจากการตรวจสอบโมฆะเกิดขึ้นบ่อยมากมันจะดีถ้าภาษารองรับไวยากรณ์พิเศษบางอย่างสำหรับมัน

ฉันจะพิจารณาใช้รูปแบบวัตถุ Null (ตามที่ Laf แนะนำ) ตราบใดที่ฉันสามารถแยกวัตถุว่างของคลาสจากวัตถุอื่นทั้งหมดได้


0

เมธอดที่ส่งคืนคอลเลกชันควรคืนค่าว่าง แต่คนอื่นสามารถคืนค่าเป็นโมฆะได้เนื่องจากสำหรับคอลเลกชันมันอาจเกิดขึ้นได้ว่าคุณจะมีวัตถุ แต่ไม่มีองค์ประกอบอยู่ในนั้นดังนั้นผู้โทรจะตรวจสอบความถูกต้อง


0

สำหรับฉันมีสองกรณีที่นี่ หากคุณส่งคืนรายการบางประเภทคุณควรกลับรายการทุกครั้งไม่ว่าจะเกิดอะไรขึ้นว่างเปล่าหากคุณไม่มีอะไรใส่

กรณีเดียวที่ฉันเห็นการอภิปรายคือเมื่อคุณส่งคืนรายการเดียว ฉันพบว่าตัวเองชอบที่จะคืนค่า null ในกรณีที่เกิดความล้มเหลวในสถานการณ์ดังกล่าวบนพื้นฐานของการทำให้สิ่งต่าง ๆ ล้มเหลวอย่างรวดเร็ว

หากคุณส่งคืนวัตถุว่างเปล่าและผู้โทรจำเป็นต้องมีวัตถุจริงพวกเขาอาจดำเนินการต่อไปและลองใช้วัตถุ null ที่มีพฤติกรรมที่ไม่คาดคิด ฉันคิดว่ามันจะดีกว่าถ้ากิจวัตรประจำวันจะบูมหากพวกเขาลืมที่จะรับมือกับสถานการณ์ หากมีอะไรผิดปกติฉันต้องการข้อยกเว้นโดยเร็ว คุณมีโอกาสน้อยที่จะส่งบั๊กด้วยวิธีนี้


0

การเพิ่มสิ่งที่ผู้คนพูดถึงเกี่ยวกับตัวMaybeสร้างประเภท:

ประโยชน์ที่ยิ่งใหญ่อีกประการหนึ่งนอกเหนือจากการไม่เสี่ยงต่อ NPE คือความหมาย ในโลกการทำงานที่เราจัดการกับฟังก์ชั่นบริสุทธิ์เราชอบที่จะพยายามที่จะทำให้ฟังก์ชั่นที่เมื่อนำมาใช้เป็นว่าเทียบเท่ากับค่าตอบแทนของพวกเขา พิจารณากรณีของString.reverse(): นี่คือฟังก์ชั่นที่กำหนดสตริงที่แน่นอนแสดงถึงรุ่นที่กลับรายการของสตริงนั้น เรารู้ว่าสตริงนี้มีอยู่เพราะทุกสตริงสามารถย้อนกลับได้ (โดยทั่วไปสตริงจะเรียงตามชุดอักขระ)

ตอนนี้เกี่ยวกับfindCustomer(int id)อะไร ฟังก์ชันนี้แสดงถึงลูกค้าที่มีรหัสที่กำหนด นี่คือสิ่งที่อาจมีหรือไม่มีอยู่ แต่จำไว้ว่าฟังก์ชั่นมีค่าตอบแทนเดียวดังนั้นคุณไม่สามารถจริงๆพูดว่า "ฟังก์ชั่นนี้ส่งกลับลูกค้าที่มีรหัสที่ระบุหรือnullจะส่งกลับ

นี่คือปัญหาหลักของฉันทั้งที่มีการส่งคืนnullและส่งคืนวัตถุที่ไม่มีค่า พวกเขาทำให้เข้าใจผิด nullไม่ใช่ลูกค้าและไม่ใช่ "การขาดลูกค้า" จริงๆ มันไม่มีอะไรเลย มันเป็นเพียงตัวชี้ที่พิมพ์ดีดเพื่ออะไร ระดับต่ำมากและน่าเกลียดมากและเกิดข้อผิดพลาดได้ง่าย ฉันคิดว่าวัตถุที่ไม่ดียังทำให้เข้าใจผิดที่นี่ ลูกค้าที่มีค่าเป็นลูกค้า ไม่ใช่การไม่มีตัวตนไม่ใช่ลูกค้าที่มี ID ที่ขอดังนั้นการส่งคืนจึงเป็นสิ่งที่ผิด นี่ไม่ใช่ลูกค้าที่ฉันขอ แต่ยังคุณอ้างว่าคุณคืนลูกค้า มันมีรหัสผิดอย่างชัดเจนว่านี่เป็นข้อผิดพลาด มันแบ่งสัญญาที่แนะนำโดยชื่อวิธีการและลายเซ็น มันต้องมีรหัสไคลเอนต์เพื่อใช้ในการออกแบบหรืออ่านเอกสาร

Maybe Customerทั้งสองของปัญหาเหล่านี้จะแก้ไขได้โดย ทันใดนั้นเราไม่ได้พูดว่า "ฟังก์ชั่นนี้แสดงถึงลูกค้าด้วยรหัสที่กำหนด" เรากำลังพูดว่า "ฟังก์ชั่นนี้แสดงถึงลูกค้าด้วยรหัสที่กำหนดถ้ามันมีอยู่ตอนนี้มันชัดเจนมากและชัดเจนเกี่ยวกับสิ่งที่มันทำถ้าคุณขอให้ลูกค้าที่มี ID ที่ไม่มีอยู่คุณจะได้รับNothingสิ่งนี้ดีกว่า กว่าnull? ไม่เพียง แต่สำหรับความเสี่ยง NPE. แต่ยังเป็นเพราะชนิดของNothingไม่ได้เป็นเพียงMaybe. มันMaybe Customer. มันมีความหมาย. มันหมายถึงเฉพาะ "ตัวตนของลูกค้า" แสดงให้เห็นว่าลูกค้าดังกล่าวไม่ได้อยู่

ปัญหาอีกประการหนึ่งเกี่ยวกับ null คือแน่นอนว่ามันคลุมเครือ เป็นเพราะคุณไม่พบลูกค้าหรือมีข้อผิดพลาดในการเชื่อมต่อฐานข้อมูลหรือฉันไม่ได้รับอนุญาตให้เห็นลูกค้ารายนั้น

หากคุณมีกรณีข้อผิดพลาดหลายอย่างเช่นนี้ให้จัดการคุณสามารถโยนข้อยกเว้นสำหรับเวอร์ชันเหล่านั้นหรือ (และฉันชอบวิธีนี้) คุณสามารถส่งคืนEither CustomerLoadError Customerข้อผิดพลาดแทนสิ่งที่ผิดพลาดหรือลูกค้าที่ส่งคืน มันมีข้อดีทั้งหมดในMaybe Customerขณะที่ยังช่วยให้คุณระบุสิ่งที่สามารถไปผิด

สิ่งสำคัญที่ฉันตามมาคือการจับสัญญาของฟังก์ชั่นในลายเซ็นของมัน อย่าคิดในสิ่งต่าง ๆ อย่าพึ่งอนุสัญญาที่หายวับไปอย่างชัดเจน


0

1) หากความหมายของฟังก์ชั่นคือมันไม่สามารถส่งคืนอะไรโทรต้องทดสอบมันมิฉะนั้นเขาจะเช่น นำเงินของคุณไปมอบให้กับใคร

2) มันดีที่จะทำให้ฟังก์ชั่นที่ semantically กลับบางสิ่งบางอย่าง (หรือโยน) เสมอ

ด้วยตัวบันทึกจะทำให้การส่งคืนตัวบันทึกที่ไม่บันทึกถ้าไม่มีการกำหนดตัวบันทึก เมื่อส่งคืนคอลเล็กชันมันแทบไม่มีเหตุผล (ยกเว้นใน "ระดับต่ำพอ" ซึ่งคอลเล็กชันนั้นเป็นข้อมูลไม่ใช่สิ่งที่มีอยู่) เพื่อส่งคืนสิ่งใดเนื่องจากชุดว่างเปล่าเป็นชุดไม่ใช่อะไรเลย

ด้วยตัวอย่างบุคคลฉันจะไปทาง "จำศีล" (รับ vs load, IIRC แต่มันมีความซับซ้อนโดยวัตถุพร็อกซีสำหรับการโหลดขี้เกียจ) ของการมีสองฟังก์ชั่นหนึ่งที่ส่งคืนโมฆะและครั้งที่สองที่พ่น


-1
  • ควรส่งคืน NULL หากแอปพลิเคชันคาดว่าข้อมูลจะพร้อมใช้งาน แต่ไม่มีข้อมูล ตัวอย่างเช่นบริการที่ส่งคืน CITY ตามรหัสไปรษณีย์ควรส่งคืนค่าว่างหากไม่พบเมือง ผู้โทรสามารถตัดสินใจจัดการกับค่าว่างหรือระเบิด

  • รายการที่ว่างเปล่าควรถูกส่งคืนหากมีความเป็นไปได้สองอย่าง มีข้อมูลหรือไม่มีข้อมูล ตัวอย่างเช่นบริการที่ส่งคืน CITY หากประชากรมากกว่าจำนวนที่แน่นอน มันสามารถกลับรายการว่างเปล่าหากไม่มีข้อมูลที่ตรงตามเกณฑ์ที่กำหนด


-2

Return NULL คือการออกแบบที่แย่มากในโลกแห่งวัตถุ สรุปการใช้งาน NULL นำไปสู่:

  • การจัดการข้อผิดพลาดแบบเฉพาะกิจ (แทนข้อยกเว้น)
  • ความหมายคลุมเครือ
  • ช้าแทนที่จะล้มเหลวอย่างรวดเร็ว
  • การคิดคอมพิวเตอร์กับการคิดเชิงวัตถุ
  • วัตถุที่ไม่แน่นอนและไม่สมบูรณ์

ตรวจสอบโพสต์บล็อกนี้สำหรับคำอธิบายโดยละเอียด: http://www.yegor256.com/2014/05/13/why-null-is-bad.html

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.