การคืนค่า null ออกแบบไม่ดีหรือไม่ [ปิด]


127

ฉันได้ยินเสียงพูดว่าการตรวจสอบค่า Null ที่คืนกลับมาจากวิธีการคือการออกแบบที่ไม่ดี ฉันต้องการฟังเหตุผลบางอย่างสำหรับเรื่องนี้

pseudocode:

variable x = object.method()
if (x is null) do something

13
รายละเอียดเพิ่มเติม: คนเหล่านี้อยู่ที่ไหนที่บอกว่าไม่ดี? การเชื่อมโยง?
jcollum

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

9
การเพิ่มข้อยกเว้นเพียงเพราะไม่มีข้อมูลที่จะส่งคืนเป็นที่น่ารำคาญอย่างไม่น่าเชื่อ โฟลว์โปรแกรมปกติไม่ควรโยนข้อยกเว้น
Thorarin

4
@ David: นั่นคือสิ่งที่ฉันพูดจริงๆ หากวิธีการใดควรส่งคืนข้อมูล แต่ไม่มีเลยนั่นหมายความว่ามีบางอย่างผิดปกติเช่นกัน นั่นไม่ใช่การไหลของโปรแกรมปกติ :)
Thorarin

2
@Thorarin: การไหลเวียนของโปรแกรม "ปกติ" ค่อนข้างเป็นแนวคิดที่ยืดหยุ่นได้: ไม่ใช่พื้นฐานที่มั่นคงสำหรับการโต้แย้ง
Tomislav Nakic-Alfirevic

คำตอบ:


206

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

ตัวอย่างเช่นถ้าฉันต้องกำหนดวิธีการใน Java ที่ส่งคืนคอลเลกชันฉันมักจะต้องการคืนคอลเลกชันที่ว่างเปล่า (เช่นCollections.emptyList()) แทนที่จะเป็นโมฆะเพราะมันหมายความว่ารหัสลูกค้าของฉันสะอาด เช่น

Collection<? extends Item> c = getItems(); // Will never return null.

for (Item item : c) { // Will not enter the loop if c is empty.
  // Process item.
}

... ซึ่งสะอาดกว่า:

Collection<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}

6
ใช่วิธีที่ดีกว่าการคืนค่า Null และหวังว่าลูกค้าจะจำได้ว่าต้องจัดการกับคดีนี้
djna

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

2
+1 สำหรับ Null Object Pattern สำหรับฉันเช่นกัน นอกจากนี้เมื่อฉันต้องการกลับ null จริงฉันได้รับการตั้งชื่อวิธีเช่น getCustomerOrNull () เพื่อให้ชัดเจน ฉันคิดว่าวิธีการมีชื่อดีเมื่อผู้อ่านไม่ต้องการดูการใช้งาน
Mike Valenty

4
ความคิดเห็น '// สองเส้นทางรหัสที่เป็นไปได้ตอนนี้' ไม่ถูกต้อง คุณมีสองเส้นทางรหัสในทางใดทางหนึ่ง อย่างไรก็ตามด้วยวัตถุคอลเลกชัน null ในตัวอย่างแรกของคุณเส้นทางรหัส 'null' จะสั้นกว่า คุณยังมีสองเส้นทางแม้ว่าและคุณยังต้องทดสอบสองเส้นทาง
Frerich Raabe

1
@MSalters - เนื้อหาทุกตัวแปรในภาษา OO ที่มีnull"คอนเทนเนอร์" ซึ่งมีศูนย์หรือหนึ่งพอยน์เตอร์ไปยังวัตถุ (แน่นอนว่ามันเป็นแบบอย่างที่ชัดเจนของ Haskell Maybeในกรณีเหล่านั้นและเป็นสิ่งที่ดีกว่าสำหรับการทำเช่นนั้น)
Andrzej Doyle

71

นี่คือเหตุผล

ในClean Code ของ Robert Martinเขาเขียนว่าการคืนค่า Null เป็นการออกแบบที่ไม่ดีเมื่อคุณกลับมาพูดแทนอาเรย์ที่ว่างเปล่า เนื่องจากผลลัพธ์ที่คาดไว้เป็นอาร์เรย์ทำไมไม่ มันจะช่วยให้คุณสามารถวนซ้ำผลโดยไม่มีเงื่อนไขพิเศษใด ๆ ถ้าเป็นจำนวนเต็มอาจเป็น 0 พอหากเป็นแฮแฮเปล่าเปล่า เป็นต้น

หลักฐานคือการไม่บังคับให้มีการเรียกรหัสเพื่อจัดการกับปัญหาในทันที รหัสการโทรอาจไม่ต้องการเกี่ยวข้องกับตัวเอง นั่นเป็นเหตุผลว่าทำไมในหลาย ๆ กรณีข้อยกเว้นดีกว่าไม่มี


2
นี่คือรูปแบบวัตถุ Null ที่กล่าวถึงในคำตอบนี้: stackoverflow.com/questions/1274792/…
Scott

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

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

น่าเสียดายที่รหัสการโทรนั้นไม่ได้พิจารณาถึงกรณีของคอลเลกชันที่ว่างเปล่าเสมอไป นั่นอาจจะใช่หรือไม่ใช่ปัญหาก็ได้
Sridhar Sarnobat

38

ใช้กลับคืนเป็นโมฆะ:

  • ถ้า null เป็นผลลัพธ์การทำงานที่ถูกต้องตัวอย่างเช่น: FindFirstObjectThatNeedsProcessing () สามารถส่งคืน null ถ้าไม่พบและผู้โทรควรตรวจสอบตาม

การใช้ไม่ถูกต้อง: พยายามแทนที่หรือซ่อนสถานการณ์พิเศษเช่น:

  • catch (... ) และส่งคืน null
  • การเริ่มต้นการพึ่งพา API ล้มเหลว
  • พื้นที่ดิสก์ไม่เพียงพอ
  • พารามิเตอร์อินพุตไม่ถูกต้อง (ข้อผิดพลาดการเขียนโปรแกรมอินพุตต้องถูกทำให้บริสุทธิ์โดยผู้เรียก)
  • ฯลฯ

ในกรณีเหล่านั้นการโยนข้อยกเว้นมีความเพียงพอมากกว่าเนื่องจาก:

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

อย่างไรก็ตามไม่ควรใช้ข้อยกเว้นเพื่อจัดการกับเงื่อนไขการทำงานของโปรแกรมเช่น:

  • ชื่อผู้ใช้ / รหัสผ่านไม่ถูกต้อง (หรืออินพุตที่ผู้ใช้กำหนด)
  • ทำลายลูปหรือเป็น gotos ที่ไม่ใช่ในท้องถิ่น

7
แม้ว่า "ชื่อผู้ใช้ไม่ถูกต้อง" ดูเหมือนจะเป็นสาเหตุที่ดีสำหรับข้อยกเว้น
Thilo

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


2
ฉันจะบอกว่ามันขึ้นอยู่กับ: boolean login(String,String)ดูเหมือนดีและเป็นเช่นนั้นAuthenticationContext createAuthContext(String,String) throws AuthenticationException
sfussenegger

1
ในตัวอย่างของคุณหากไม่มีวัตถุแรกที่ต้องการการประมวลผลแล้วมีสองวิธีที่เหมาะสมในการจัดการกรณีนั้น อย่างแรกคือรูปแบบวัตถุ Null สร้างคลาสย่อยสุดท้ายที่แทนคลาสที่ไม่มีอยู่จริงของคลาส มันมีค่าเริ่มต้นที่เหมาะสมและส่งข้อยกเว้นเมื่อมีการร้องขอการกระทำที่ไร้สาระ เทคนิคอื่น ๆ คือตัวเลือก นี่คือสิ่งที่มีอยู่ใน Java8 และ Scala สิ่งเหล่านี้แสดงให้เห็นเจตนาของนักพัฒนา null ไม่สามารถแสดงเจตนาได้เนื่องจากมีความหมายที่เป็นไปได้มากเกินไป ** การแสดงเจตนา ** เป็นสิ่งสำคัญที่สุดของรหัส
กอตต์เอ็มการ์ดเนอร์

29

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

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

ตรวจสอบการโพสต์บล็อกนี้สำหรับคำอธิบายรายละเอียด: http://www.yegor256.com/2014/05/13/why-null-is-bad.html เพิ่มเติมในหนังสือของฉันวัตถุสุดหรูมาตรา 4.1


2
ฉันเห็นด้วยอย่างยิ่ง ฉันไม่ชอบรูปแบบวัตถุ null ซึ่งดูเหมือนว่าเป็นวิธีการ 'ล้างบาป' ล่าช้า ไม่ว่าวิธีการของคุณจะสำเร็จหรือไม่ ถ้ามันควรจะประสบความสำเร็จจากนั้นก็โยน ถ้ามันอาจจะไม่ประสบความสำเร็จแล้วออกแบบวิธีการเพื่อให้ผู้บริโภครู้เช่นbool TryGetBlah(out blah)หรือหรือFirstOrNull() MatchOrFallback(T fallbackValue)
ลุค Puplett

ฉันก็ไม่ชอบผลตอบแทนที่เป็นโมฆะ คุณกำลังแยกสาเหตุที่แท้จริงออกจากอาการทำให้การแก้จุดบกพร่องยากขึ้น อาจโยนข้อยกเว้น (ล้มเหลวเร็ว) หรือเรียกใช้วิธีตรวจสอบที่ส่งกลับบูลีนก่อน (เช่นisEmpty()) และเฉพาะในกรณีที่มันเป็นจริงแล้วเรียกวิธีการ ผู้คนโต้แย้งกับคนที่ 2 ว่ามันแย่กว่าประสิทธิภาพ - แต่อย่างที่ Unix Philosophy บอกว่า "ให้คุณค่ากับเวลาของมนุษย์ในช่วงเวลาของเครื่องจักร" (เช่นประสิทธิภาพที่ช้ากว่าเล็กน้อยทำให้เสียเวลาน้อยกว่านักพัฒนาที่ดีบั๊ก
Sridhar Sarnobat

22

ใครบอกว่านี่คือการออกแบบที่ไม่ดี?

การตรวจสอบ nulls เป็นวิธีปฏิบัติทั่วไปที่ได้รับการสนับสนุนไม่เช่นนั้นคุณจะเสี่ยงต่อ NullReferenceExceptions ทุกที่ มันจะดีกว่าที่จะจัดการกับข้อผิดพลาดอย่างสง่างามกว่าการโยนข้อยกเว้นเมื่อคุณไม่ต้องการ


11
+1 รหัสที่ส่งข้อยกเว้นสำหรับปัญหาที่สามารถลดลงได้ในจุดที่ทำให้ฉันเศร้า
jkeys

6
คุณรู้สึกเศร้าแค่ไหนเมื่อผู้เขียนโค้ดลืมตรวจสอบโมฆะแล้วได้รับข้อยกเว้นตัวชี้โมฆะลึกลับ? ตรวจสอบข้อยกเว้นที่คอมไพเลอร์เตือนผู้ใช้ว่าพวกเขาไม่ได้จัดการกับเงื่อนไขข้อผิดพลาดหลีกเลี่ยงการเรียนข้อผิดพลาด
djna

3
@djna: ฉันคิดว่ามันเป็นเรื่องน่าเศร้า แต่ฉันก็พบว่าประเภทเดียวกันของ coders ที่ "ลืม" เพื่อตรวจสอบ nulls เป็นคนที่เมื่อจัดการกับข้อยกเว้นที่ตรวจสอบบ่อยที่สุดท้ายกลืนพวกเขา
แรนดี้สนับสนุนโมนิกา

5
ง่ายต่อการมองเห็นสถานะของบล็อก catch ที่ว่างเปล่ามากกว่าการไม่มีเช็คว่าง
Preston

20
@Preston: ฉันไม่เห็นด้วยอย่างยิ่ง ไม่มีการตรวจสอบเป็นโมฆะคุณจะพบทันทีเมื่อเกิดปัญหา ข้อยกเว้นกลืนกินสามารถทำการแพร่กระจายข้อผิดพลาดลึกลับและบอบบางสำหรับปี ...
Beska

18

จากสิ่งที่คุณพูดจนถึงตอนนี้ฉันคิดว่ามีข้อมูลไม่เพียงพอ

การคืนค่า null จากเมธอด CreateWidget () นั้นไม่ดี

กลับมาเป็นโมฆะจากวิธี FindFooInBar ()


4
คล้ายกับการประชุมของฉัน: คำสั่งเดี่ยวรายการ - Create...ผลตอบแทนอินสแตนซ์ใหม่หรือพ่น ; Get...ผลตอบแทนที่เป็นตัวอย่างที่มีอยู่คาดว่าจะได้หรือพ่น ; GetOrCreate...ส่งคืนอินสแตนซ์ที่มีอยู่หรืออินสแตนซ์ใหม่หากไม่มีอยู่จริงหรือส่งออกไป Find...ผลตอบแทนที่เป็นตัวอย่างที่มีอยู่ถ้ามันมีอยู่แล้วหรือ nullสำหรับข้อความค้นหาการรวบรวม - Get...ส่งคืนคอลเล็กชันเสมอซึ่งจะว่างถ้าไม่พบรายการที่ตรงกัน
Johann Gerell

ที่ไม่ดีเป็นโมฆะด้วยเหตุผลที่กำหนดใน yegor256 และคำตอบของ hakuinin กลับที่ถูกต้อง แต่ที่ว่างเปล่าช่วยลดความยุ่งยากวัตถุเหตุผล Overal
Rob11311


10

ขึ้นอยู่กับภาษาที่คุณใช้ หากคุณอยู่ในภาษาอย่าง C # ซึ่งเป็นวิธีที่บ่งบอกถึงการขาดค่าคือการคืนค่า null การคืนค่า null นั้นเป็นการออกแบบที่ดีถ้าคุณไม่มีค่า อีกทางเลือกหนึ่งในภาษาเช่น Haskell ซึ่งใช้บางทีอาจเป็น monad สำหรับกรณีนี้การส่งคืน null จะเป็นการออกแบบที่ไม่ดี (ถ้าเป็นไปได้)


2
+1 สำหรับการกล่าวถึงอาจ monad ฉันพบว่าคำจำกัดความของnullภาษาเช่น C # และ Java มักจะโหลดมากเกินไปและให้ความหมายบางอย่างในโดเมน หากคุณค้นหาnullคำหลักในข้อกำหนดภาษามันก็หมายถึง "ตัวชี้ที่ไม่ถูกต้อง" น่าจะหมายความว่าไม่มีอะไรในโดเมนที่มีปัญหา
MattDavey

ปัญหาคือคุณไม่รู้ว่ามันเป็น "ค่าที่หายไป" nullหรือ "ไม่เริ่มต้น"null
CervEd

5

หากคุณอ่านคำตอบทั้งหมดคำตอบของคำถามนี้จะชัดเจนขึ้นอยู่กับประเภทของวิธีการ

ประการแรกเมื่อมีสิ่งผิดปกติเกิดขึ้น (IOproblem ฯลฯ ) ข้อยกเว้นเชิงตรรกะจะถูกโยน เมื่อบางสิ่งบางอย่างที่พิเศษอาจเป็นสิ่งที่แตกต่างสำหรับหัวข้อ ...

เมื่อใดก็ตามที่วิธีการที่คาดว่าจะไม่มีผลลัพธ์มีสองหมวดหมู่:

  • หากเป็นไปได้ที่จะคืนค่าเป็นกลางให้ทำเช่นนั้น
    enumrables ว่างสตริง ฯลฯ เป็นตัวอย่างที่ดี
  • ถ้าเช่นเป็นค่ากลางไม่ได้อยู่null ควรจะกลับ
    ดังที่ได้กล่าวมาแล้ววิธีการนี้จะถือว่าไม่มีผลดังนั้นจึงไม่ได้เป็นข้อยกเว้นดังนั้นจึงไม่ควรทำการยกเว้น ไม่สามารถใช้ค่าที่เป็นกลางได้ (ตัวอย่าง: 0 ไม่ได้เป็นผลที่เป็นกลางโดยเฉพาะขึ้นอยู่กับโปรแกรม)

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

ฉันยังไม่ตกลงกับชื่ออย่างเต็มที่ แต่ก็ไม่สามารถคิดอะไรดีขึ้นได้ ดังนั้นตอนนี้ฉันก็วิ่งไปด้วย


4

ฉันมีการประชุมในพื้นที่นี้ที่ให้บริการฉันดี

สำหรับการค้นหารายการเดียว:

  • Create...ส่งคืนอินสแตนซ์ใหม่หรือส่ง
  • Get...ส่งคืนอินสแตนซ์ที่มีอยู่ที่คาดไว้หรือส่งกลับ
  • GetOrCreate...ส่งคืนอินสแตนซ์ที่มีอยู่หรืออินสแตนซ์ใหม่หากไม่มีอยู่หรือส่ง
  • Find...ส่งคืนอินสแตนซ์ที่มีอยู่หากมีอยู่หรือnull

สำหรับข้อความค้นหาคอลเลกชัน:

  • Get... ส่งคืนคอลเล็กชันเสมอซึ่งจะว่างถ้าไม่พบรายการ [1] ที่ตรงกัน

[1] ให้เกณฑ์บางอย่างชัดเจนหรือโดยนัยกำหนดในชื่อฟังก์ชั่นหรือเป็นพารามิเตอร์


ทำไม GetOne และ FindOne กลับต่างกันถ้าไม่พบ
Vladimir Vukanac

ฉันอธิบายความแตกต่าง ฉันใช้Getเมื่อฉันคาดหวังว่ามันจะอยู่ที่นั่นเพื่อที่ว่าถ้ามันไม่ได้อยู่ที่นั่นแล้วมันเป็นข้อผิดพลาดและฉันโยน - ฉันไม่จำเป็นต้องตรวจสอบค่าตอบแทน ฉันใช้Findถ้าฉันไม่รู้ว่ามันอยู่ที่นั่นจริงหรือเปล่า - ฉันต้องตรวจสอบค่าส่งคืน
Johann Gerell

3

ข้อยกเว้นสำหรับข้อยกเว้น al สถานการณ์

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


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

3

ไม่เป็นไรที่จะคืนค่าว่างถ้าการทำเช่นนั้นมีความหมายในทางใดทางหนึ่ง:

public String getEmployeeName(int id){ ..}

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

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

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}

3
ได้. หากคุณมีเงื่อนไขข้อผิดพลาดโยนข้อยกเว้น
David Thornley

3
นี่เป็นกรณีหนึ่งที่จะได้รับการยกเว้น หากคุณกำลังขอชื่อของพนักงานที่ไม่มีอยู่สิ่งที่เห็นได้ชัดคือสิ่งที่ผิด
Thorarin

ฉันคิดว่ามันเป็นตัวอักษรที่แท้จริง Thorarin - คุณสามารถพูดเล่นกับตัวอย่างของฉันได้อย่างแน่นอน แต่ฉันแน่ใจว่าคุณสามารถจินตนาการตัวอย่างของฟังก์ชันที่ส่งคืนค่าการจับคู่หรือโมฆะหากไม่มีการจับคู่ วิธีการเกี่ยวกับการรับค่าจากคีย์ใน hashtable? (ควรนึกถึงตัวอย่างนั้นตั้งแต่แรก)
Steve B.

1
ตารางแฮชที่ส่งคืนค่า null สำหรับคีย์ที่ไม่มีอยู่นั้นไม่ดี การทำเช่นนั้นโดยอัตโนมัติหมายความว่าคุณไม่สามารถเก็บค่าว่างไว้เป็นค่าโดยไม่มีรหัสที่ไม่สอดคล้องกัน
RHSeeger

3

ไม่จำเป็นต้องเป็นการออกแบบที่ไม่ดี - เช่นเดียวกับการตัดสินใจในการออกแบบจำนวนมาก

หากผลลัพธ์ของเมธอดเป็นสิ่งที่ไม่น่าจะมีผลดีในการใช้งานปกติการคืนค่าว่างจะไม่เป็นผล:

object x = GetObjectFromCache();   // return null if it's not in the cache

หากควรจะมีผลลัพธ์ที่ไม่เป็นโมฆะจริง ๆ ก็อาจจะดีกว่าที่จะโยนข้อยกเว้น:

try {
   Controller c = GetController();    // the controller object is central to 
                                      //   the application. If we don't get one, 
                                      //   we're fubar

   // it's likely that it's OK to not have the try/catch since you won't 
   // be able to really handle the problem here
}
catch /* ... */ {
}

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

หรือตัดข้อยกเว้นใน RuntimeException ที่มีข้อมูลการวินิจฉัยในสตริงข้อความ เก็บข้อมูลไว้ด้วยกัน
Thorbjørn Ravn Andersen

ตอนนี้ (ในปีพ. ศ. 2561) ฉันไม่เห็นด้วยและในกรณีเช่นนี้เราควรจะกลับมาอีกครั้งOptional<>
Piotr Müller

2

สำหรับบางสถานการณ์คุณต้องการที่จะสังเกตเห็นความล้มเหลวทันทีที่มันเกิดขึ้น

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

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


2

ทางเลือกใดที่คุณเห็นว่าส่งคืนค่าว่าง

ฉันเห็นสองกรณี:

  • findAnItem (id) สิ่งนี้ควรทำอย่างไรหากไม่พบรายการ

ในกรณีนี้เราสามารถ: คืนค่าว่างเปล่าหรือโยนข้อยกเว้น (ทำเครื่องหมาย) (หรืออาจจะสร้างไอเท็มและส่งคืน)

  • listItemsMatching (เกณฑ์) สิ่งนี้ควรส่งคืนสิ่งใดหากไม่พบสิ่งใด

ในกรณีนี้เราสามารถคืนค่า Null คืนค่าว่างหรือส่งข้อยกเว้น

ฉันเชื่อว่าผลตอบแทนที่เป็นโมฆะอาจน้อยกว่าทางเลือกเนื่องจากลูกค้าต้องการให้จำการตรวจสอบค่า Null, โปรแกรมเมอร์ลืมรหัสและ

x = find();
x.getField();  // bang null pointer exception

ใน Java การโยนข้อยกเว้นที่ตรวจสอบแล้ว RecordNotFoundException อนุญาตให้คอมไพเลอร์เตือนลูกค้าให้จัดการกรณีและปัญหา

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


3
การโยนข้อยกเว้นเพื่อระบุเงื่อนไขที่คาดว่าจะเกิดขึ้นในระหว่างการไหลปกติของโปรแกรมนำไปสู่รหัสที่น่าเกลียดจริงๆของการจัดเรียงลอง {x = find ()} catch (RecordNotFound e) {// do stuff}
quant_dev

การส่งคืนรายการเปล่าเป็นวิธีแก้ปัญหาที่ดี แต่เมื่อวิธีนั้นอยู่ในบริบทที่สามารถส่งคืนรายการได้ สำหรับสถานการณ์ "findById" คุณต้องคืนค่าว่าง ฉันไม่ชอบข้อยกเว้น RecordNotFound
Thilo

นั่นเป็นจุดเริ่มต้นของความแตกต่างในความเห็นของเรา ในสายตาของฉันความงามในระหว่าง x = find () ไม่แตกต่างกันมากนัก; ถ้า (x = null) {work} else {do ​​stuff} และลองจับ และถ้าจำเป็นฉันพร้อมที่จะเสียสละความงามเพื่อความถูกต้องของรหัส บ่อยครั้งในชีวิตของฉันฉันพบรหัสที่ไม่มีการตรวจสอบค่าส่งคืน
djna

1

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



1

แนวคิดพื้นฐานที่อยู่เบื้องหลังเธรดนี้คือการตั้งโปรแกรมป้องกัน นั่นคือรหัสต่อสิ่งที่ไม่คาดคิด มีอาร์เรย์ของคำตอบต่าง ๆ :

Adamskiแนะนำให้ดูที่รูปแบบวัตถุ Null โดยการตอบนั้นถูกโหวตให้กับคำแนะนำนั้น

Michael Valentyยังแนะนำการตั้งชื่อเพื่อบอกผู้พัฒนาถึงสิ่งที่คาดหวัง ZeroConceptแนะนำให้ใช้ Exception อย่างถูกต้องหากเป็นเหตุผลของ NULL และคนอื่น ๆ.

หากเราทำให้ "กฎ" ที่เราต้องการทำโปรแกรมป้องกันอยู่เสมอเราจะเห็นว่าคำแนะนำเหล่านี้ถูกต้อง

แต่เรามีสถานการณ์การพัฒนา 2 สถานการณ์

คลาส "ผู้สร้าง" โดยนักพัฒนา: ผู้แต่ง

คลาส "ถูกใช้" โดยผู้พัฒนารายอื่น (อาจ): ผู้พัฒนา

ไม่ว่าคลาสจะคืนค่า NULL สำหรับเมธอดที่มีค่าส่งคืนหรือไม่ก็ตามผู้พัฒนาจะต้องทดสอบว่าวัตถุนั้นถูกต้องหรือไม่

หากผู้พัฒนาไม่สามารถทำเช่นนี้แสดงว่าคลาส / วิธีการนั้นไม่ได้กำหนดไว้ นั่นคือถ้า "การเรียกเมธอด" เพื่อให้วัตถุไม่ได้ทำในสิ่งที่ "โฆษณา" (เช่น getEmployee) จะทำให้สัญญาเสียหาย

ในฐานะผู้เขียนชั้นเรียนฉันมักจะต้องการความใจดีและการป้องกัน (และกำหนดขึ้น) ตลอดเวลาเมื่อสร้างวิธีการ

ดังนั้นเมื่อใดก็ตามที่ NULL หรือ NULL OBJECT (เช่นถ้า (พนักงานเป็น NullEmployee.ISVALID)) จำเป็นต้องได้รับการตรวจสอบและอาจต้องเกิดขึ้นกับกลุ่มของพนักงานดังนั้นวิธีการ null จึงเป็นวิธีที่ดีกว่า

แต่ฉันก็ชอบคำแนะนำของMichael Valenty ในการตั้งชื่อวิธีที่ต้องส่งคืน null เช่น getEmployeeOrNull

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

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

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

บรรทัดล่างคือว่าผู้เขียนของคลาส / วิธีการต้องมีกลไกที่นักพัฒนาสามารถใช้ในการเขียนโปรแกรมการป้องกันของพวกเขา นั่นคือความตั้งใจที่ชัดเจนของวิธีการ

นักพัฒนาควรใช้การเขียนโปรแกรมป้องกันเพื่อทดสอบความถูกต้องของวัตถุที่ส่งคืนจากคลาส / วิธีอื่น

ความนับถือ

GregJF


0

ตัวเลือกอื่น ๆ สำหรับสิ่งนี้คือ: คืนค่าบางอย่างที่บ่งชี้ความสำเร็จหรือไม่ (หรือประเภทของข้อผิดพลาด) แต่ถ้าคุณเพียงแค่ต้องการค่าบูลีนที่จะบ่งชี้ความสำเร็จ / ล้มเหลวกลับมาเป็นโมฆะสำหรับความล้มเหลว ให้ถูกต้องน้อยลงจากนั้นส่งคืนจริง / เท็จและรับวัตถุผ่านพารามิเตอร์
วิธีอื่น ๆ ที่จะใช้ข้อยกเว้นเพื่อบ่งบอกถึงความล้มเหลว แต่ที่นี่ - มีอีกหลายเสียงที่บอกว่านี่เป็นแนวทางปฏิบัติที่ไม่ดี (การใช้ข้อยกเว้นอาจสะดวกและมีข้อเสียมากมาย)
ดังนั้นฉันเองไม่เห็นสิ่งใดที่ไม่ดีในการคืนค่า null เป็นตัวบ่งชี้ว่ามีบางอย่างผิดปกติและตรวจสอบในภายหลัง (เพื่อทราบว่าคุณประสบความสำเร็จจริงหรือไม่) นอกจากนี้สุ่มสี่สุ่มห้าคิดว่าวิธีการของคุณจะไม่ส่งกลับค่า NULL แล้วฐานรหัสของคุณบนมันอาจนำไปสู่อื่น ๆ บางครั้งยากที่จะหาข้อผิดพลาด (แม้ว่าในกรณีส่วนใหญ่มันก็จะผิดพลาดระบบของคุณ :) ตามที่คุณอ้างอิง 0x00000000 ไม่ช้าก็เร็ว)


0

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

ฟังก์ชั่นโมฆะหรือวิธีการที่มักจะใช้เป็นพฤติกรรมเริ่มต้นของฟังก์ชั่น revectorable หรือวิธีการแทนที่ในกรอบวัตถุ

Null_function @wikipedia


0

แน่นอนว่าขึ้นอยู่กับจุดประสงค์ของวิธีการ ... บางครั้งทางเลือกที่ดีกว่าคือการยกเว้น ทุกอย่างขึ้นอยู่กับกรณี


0

หากรหัสเป็นสิ่งที่ชอบ:

command = get_something_to_do()

if command:  # if not Null
    command.execute()

หากคุณมีวัตถุจำลองที่มีวิธีการ execute () ไม่ทำอะไรเลยและคุณคืนค่านั้นแทน Null ในกรณีที่เหมาะสมคุณไม่ต้องตรวจสอบกรณี Null และสามารถทำได้เพียง:

get_something_to_do().execute()

ดังนั้นที่นี่ปัญหาไม่ได้อยู่ระหว่างการตรวจสอบค่า NULL เทียบกับข้อยกเว้น แต่แทนที่จะเป็นระหว่างผู้โทรต้องจัดการกับกรณีที่ไม่พิเศษต่างกัน (ไม่ว่าในทางใด) หรือไม่


0

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


-3

G'day,

การส่งคืนค่า NULL เมื่อคุณไม่สามารถสร้างออบเจ็กต์ใหม่เป็นแบบฝึกหัดมาตรฐานสำหรับ API จำนวนมาก

ทำไมนรกมันออกแบบไม่ดีฉันไม่มีความคิด

แก้ไข:สิ่งนี้เป็นจริงของภาษาที่คุณไม่มีข้อยกเว้นเช่น C ซึ่งเป็นแบบแผนมาหลายปี

HTH

'Avahappy,


2
หากคุณไม่สามารถสร้างวัตถุได้คุณควรโยนข้อยกเว้นจริงๆ หากคุณไม่สามารถหาวัตถุที่ตรงกับการสืบค้นได้การส่งคืน null จะเป็นวิธีการดำเนินการ
Thilo

@Thilo แสดงวิธีการทำเช่นนั้นใน C และฉันจะสนใจมากที่สุด
Rob Wells

@RobWells C ไม่ใช่ภาษาเชิงวัตถุ แต่คำถามนี้ถูกแท็กเป็น "oop"
yegor256

@ yegor256 คุณพูดถูก และฉันพลาดแท็ก OOP ดั้งเดิม แต่ตามที่ BS กล่าวว่าคลาสใน C ++ เป็นเพียงโครงสร้างที่มีฟังก์ชั่นสมาชิกเพิ่มเติมบางส่วนและการจัดการหน่วยความจำที่แปลกใหม่เกิดขึ้นภายใน แต่ถ้า API มี suuposed ส่งคืน struct แล้วส่งคืน NUL เมื่อคุณไม่สามารถสร้าง struct ได้มักจะเป็นแบบแผน
Rob Wells
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.