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


30

ฉันสงสัยว่าฉันควรป้องกันค่าตอบแทนการโทรด้วยวิธีการโดยตรวจสอบว่าพวกเขาตอบสนองความคาดหวังของฉันแม้ว่าฉันรู้ว่าวิธีการที่ฉันโทรจะตอบสนองความคาดหวังดังกล่าว

GIVEN

User getUser(Int id)
{
    User temp = new User(id);
    temp.setName("John");

    return temp;
}

ฉันควรทำหรือไม่

void myMethod()
{
    User user = getUser(1234);
    System.out.println(user.getName());
}

หรือ

void myMethod()
{
    User user = getUser(1234);

    // Validating
    Preconditions.checkNotNull(user, "User can not be null.");
    Preconditions.checkNotNull(user.getName(), "User's name can not be null.");

    System.out.println(user.getName());
}

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


ข้อสรุปของฉันจากคำตอบทั้งหมด (อย่าลังเลที่จะมาหาคุณ):

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

1
ตั้งเป้าเพื่อให้ครอบคลุมโค้ด% 100!
Jared Burrows

9
เหตุใดคุณจึงมุ่งเน้นที่ปัญหาเดียว ( Null) มันอาจจะมีปัญหาอย่างเท่าเทียมกันถ้ามีชื่อเป็น""(ไม่เป็นโมฆะ แต่สตริงว่างเปล่า) "N/A"หรือ ไม่ว่าคุณจะเชื่อผลได้หรือไม่ก็ต้องหวาดระแวงอย่างเหมาะสม
MSalters

3
ใน codebase ของเราเรามีรูปแบบการตั้งชื่อ: getFoo () สัญญาว่าจะไม่ส่งคืน null และ getFooIfPresent () อาจส่งคืน null
pjc50

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

คำตอบ:


42

ที่ขึ้นอยู่กับวิธีการที่มีแนวโน้ม getUser และ MyMethod มีการเปลี่ยนแปลงและที่สำคัญกว่าวิธีการที่พวกเขาจะมีแนวโน้มที่จะเปลี่ยนเป็นอิสระจากกัน

หากคุณทราบอย่างแน่นอนว่า getUser จะไม่เปลี่ยนแปลงตลอดไปในอนาคตใช่ว่าจะเป็นการเสียเวลาในการตรวจสอบความถูกต้องและเสียเวลาในการตรวจสอบความถูกต้องที่iมีค่าเท่ากับ 3 ทันทีหลังจากi = 3;คำสั่ง ในความเป็นจริงคุณไม่ทราบว่า แต่มีบางสิ่งที่คุณรู้:

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

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

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

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


2
นอกจากนี้: หากคุณต้องเปลี่ยนในgetUserภายหลังเพื่อที่จะได้กลับมาเป็นโมฆะการตรวจสอบที่ชัดเจนจะให้ข้อมูลที่คุณไม่ได้รับจาก "NullPointerException" และหมายเลขบรรทัดหรือไม่
253751

ดูเหมือนว่ามีสองสิ่งที่คุณสามารถตรวจสอบได้: (1) วิธีการทำงานอย่างถูกต้องเช่นเดียวกับที่ไม่มีข้อบกพร่อง (2) วิธีการปฏิบัติตามสัญญาของคุณ ฉันเห็นการตรวจสอบความถูกต้องของ # 1 ว่าเกินความเป็นจริง คุณควรมีการทดสอบทำ # 1 แทน นี่คือเหตุผลที่ฉันจะไม่ตรวจสอบ i = 3; ดังนั้นคำถามของฉันเกี่ยวข้องกับ # 2 ฉันชอบคำตอบของคุณสำหรับสิ่งนั้น แต่ในขณะเดียวกันก็เป็นคำตอบที่ใช้ในการตัดสินของคุณ คุณเห็นปัญหาอื่นใดที่เกิดขึ้นจากการตรวจสอบสัญญาของคุณอย่างชัดเจนแล้วเสียเวลาเขียนข้อความยืนยันหรือไม่?
Didier A.

"ปัญหา" อื่น ๆ เท่านั้นคือหากการตีความสัญญาของคุณผิดหรือผิดคุณต้องเปลี่ยนการตรวจสอบทั้งหมด แต่นั่นก็ใช้กับรหัสอื่นทั้งหมดด้วย
Ixrec

1
ไม่สามารถสู้กับฝูงชนได้ คำตอบนี้ถูกต้องที่สุดในการครอบคลุมหัวข้อของคำถามของฉัน ฉันต้องการที่จะเพิ่มที่จากการอ่านคำตอบทั้งหมดโดยเฉพาะ @MikeNakis ตอนนี้ฉันคิดว่าคุณควรยืนยันเงื่อนไขของคุณอย่างน้อยในโหมดการแก้ปัญหาเมื่อคุณมีอย่างใดอย่างหนึ่งทั้งสองสัญลักษณ์แสดงหัวข้อย่อยของ Ixrec หากคุณกำลังเผชิญหน้ากับสัญลักษณ์แสดงหัวข้อแรกมันอาจจะเกินความจริงที่จะยืนยันเงื่อนไขของคุณ ในที่สุดเมื่อได้รับสัญญาที่ชัดเจนเช่นเอกสารที่ถูกต้องความปลอดภัยของประเภทการทดสอบหน่วยหรือการตรวจสอบสภาพภายหลังคุณไม่ควรยืนยันเงื่อนไขเบื้องต้นของคุณ
Didier A.

22

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

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

คำขวัญของฉันคือ:

คำถามที่คุณควรถามตัวเองอยู่เสมอไม่ใช่ "ฉันควรยืนยันเรื่องนี้หรือไม่?" แต่ "มีอะไรฉันลืมยืนยัน?"

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

แต่มีข้อยกเว้นแม้แต่กฎนี้ อยากรู้อยากเห็นพอที่จะใช้ตัวอย่างของ Ixrec มีประเภทของสถานการณ์ที่มันเป็นที่พึงปรารถนาอย่างสมบูรณ์แบบที่จะปฏิบัติตามint i = 3;ด้วยการยืนยันที่iอยู่ในช่วงที่แน่นอน นี่คือสถานการณ์ที่iมีแนวโน้มที่จะมีการเปลี่ยนแปลงโดยคนที่กำลังพยายามทำสิ่งต่าง ๆ ที่เกิดขึ้นและรหัสที่ตามมานั้นขึ้นอยู่กับiการมีค่าอยู่ในช่วงที่แน่นอนและไม่ชัดเจนโดยทันทีเมื่อดูที่รหัสต่อไปนี้ ช่วงที่ยอมรับได้คือ ดังนั้นint i = 3; assert i >= 0 && i < 5;อาจจะเป็นครั้งแรกที่ดูเหมือนไร้สาระ แต่ถ้าคุณคิดเกี่ยวกับมันมันจะบอกคุณว่าคุณอาจจะเล่นด้วยiและก็ยังจะบอกคุณช่วงที่อยู่ในที่ที่คุณต้องอยู่ การยืนยันเป็นเอกสารประเภทที่ดีที่สุดเพราะมันถูกบังคับใช้โดยเครื่องดังนั้นจึงเป็นการรับประกันที่สมบูรณ์แบบและได้รับการปรับโครงสร้างใหม่พร้อมกับรหัสดังนั้นจึงยังคงเกี่ยวข้องกับมันอยู่เสมอ

getUser(int id)ตัวอย่างของคุณไม่ใช่ญาติห่าง ๆ ของassign-constant-and-assert-in-rangeตัวอย่าง คุณจะเห็นข้อบกพร่องเกิดขึ้นเมื่อสิ่งต่าง ๆ แสดงพฤติกรรมซึ่งแตกต่างจากพฤติกรรมที่เราคาดไว้ จริงเราไม่สามารถยืนยันทุกอย่างดังนั้นบางครั้งจะมีการดีบัก แต่มันเป็นความจริงที่ได้รับการยอมรับอย่างดีในอุตสาหกรรมว่ายิ่งเราพบข้อผิดพลาดได้เร็วเท่าไหร่ก็จะยิ่งมีต้นทุนน้อยลงเท่านั้น คำถามที่คุณควรถามไม่เพียง แต่จะแน่ใจได้ว่าคุณเป็นอย่างนั้นgetUser()จะไม่มีวันส่งคืนค่าว่าง (และไม่ส่งคืนชื่อผู้ใช้ด้วยค่า null) แต่จะรวมถึงสิ่งเลวร้ายประเภทใดที่จะเกิดขึ้นกับส่วนที่เหลือของรหัส getUser()ความเป็นจริงในวันหนึ่งกลับมาบางสิ่งบางอย่างที่ไม่คาดคิดและวิธีการที่ง่ายก็คือได้อย่างรวดเร็วโดยมองในส่วนที่เหลือของรหัสที่จะรู้ว่าสิ่งที่ได้รับการคาดหวังของ

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

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


2
ใช่. ยืนยันมัน ฉันมาที่นี่เพื่อให้คำตอบนี้มากหรือน้อย ++
RubberDuck

2
@SteveJessop ... && sureness < 1)ฉันจะแก้ไขปัญหานี้ให้กับ
Mike Nakis

2
@MikeNakis: ที่เสมอฝันร้ายเมื่อเขียนการทดสอบหน่วยค่าส่งกลับว่าได้รับอนุญาตจากอินเตอร์เฟซ แต่เป็นไปไม่ได้ในการสร้างจริงจากปัจจัยใด ๆ ;-) อย่างไรก็ตามเมื่อคุณ 101% มั่นใจว่าคุณพูดถูกแล้วคุณแน่นอนไม่ถูกต้อง !
Steve Jessop

2
+1 สำหรับการชี้ให้เห็นสิ่งที่ฉันลืมพูดถึงในคำตอบของฉันอย่างสมบูรณ์
Ixrec

1
@DavidZ ถูกต้องคำถามของฉันถือว่าการตรวจสอบรันไทม์ แต่การบอกว่าคุณไม่เชื่อถือวิธีการตรวจแก้จุดบกพร่องและเชื่อมั่นในผลิตภัณฑ์นั้นเป็นมุมมองที่ดีในหัวข้อนี้ ดังนั้นการยืนยันสไตล์ C อาจเป็นวิธีที่ดีในการจัดการกับสถานการณ์นี้
Didier A.

8

ไม่แน่นอน

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

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

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


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

ดูเหมือนว่าคุณกำลังสนับสนุนการออกแบบตามสัญญามากกว่าการเขียนโปรแกรมป้องกัน ฉันคิดว่านี่เป็นคำตอบที่ดี หากวิธีการมีสัญญาที่เข้มงวดฉันสามารถเชื่อถือได้ ในกรณีของฉันมันไม่ได้ แต่ฉันสามารถเปลี่ยนให้ทำ บอกว่าฉันทำไม่ได้? คุณจะยืนยันว่าฉันควรเชื่อถือการใช้งานหรือไม่ แม้ว่ามันจะไม่ได้ระบุอย่างชัดเจนในเอกสารว่ามันไม่ส่งคืนค่าว่างหรือมีการตรวจสอบเงื่อนไขภายหลังจริงหรือไม่
Didier A.

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

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

5

ถ้า null เป็นค่าส่งคืนที่ถูกต้องของเมธอด getUser ดังนั้นโค้ดของคุณควรจัดการกับถ้าไม่ใช่ไม่ใช่ข้อยกเว้นมันไม่ใช่กรณีพิเศษ

หาก null ไม่ใช่ค่าส่งคืนที่ถูกต้องของเมธอด getUser แสดงว่ามีข้อผิดพลาดใน getUser - เมธอด getUser ควรมีข้อยกเว้นหรือได้รับการแก้ไข

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


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

และถ้า getUser หรือ user.getName เปลี่ยนเป็นโยนข้อยกเว้น? คุณควรมีการตรวจสอบ แต่ไม่ได้เป็นส่วนหนึ่งของวิธีการใช้งานของผู้ใช้ ตัดเมธอด getUser - และแม้แต่คลาสผู้ใช้ - เพื่อบังคับใช้สัญญาของคุณหากคุณกังวลเกี่ยวกับการเปลี่ยนแปลงในอนาคตของการทำลายรหัส getUser สร้างการทดสอบหน่วยเพื่อตรวจสอบสมมติฐานของคุณเกี่ยวกับพฤติกรรมของชั้นเรียน
MatthewC

@didibus ในการแปลความหมายคำตอบของคุณ ... อิโมติคอนยิ้มไม่ใช่ค่าส่งคืนที่ถูกต้องของ getUser โดยการตรวจสอบ getUser ผมเห็นว่าการดำเนินการจะไม่กลับอิโมติคอนยิ้ม คำถามของฉันคือฉันควรเชื่อถือการตรวจสอบของฉันและไม่มีวิธีการตรวจสอบของฉันหรือฉันควรสงสัยการตรวจสอบของฉันและยังคงมีการตรวจสอบ นอกจากนี้ยังเป็นไปได้วิธีการที่มีการเปลี่ยนแปลงในอนาคตที่จะหมดความคาดหวังของฉันไม่ได้กลับมาเป็นอารมณ์ยิ้ม ไม่ใช่หน้าที่ของผู้โทรเพื่อยืนยันว่าฟังก์ชั่นที่เรียกใช้นั้นปฏิบัติตามสัญญา
Vince O'Sullivan

@ VinceO'Sullivan ดูเหมือนว่า "สัญญา" เป็นจุดศูนย์กลางของปัญหา และคำถามของฉันเกี่ยวกับสัญญาโดยนัยกับชัดเจน วิธีการที่ส่งกลับ int ฉันจะไม่ยืนยันว่าฉันไม่ได้รับสาย แต่ถ้าฉันต้องการแค่ ints เชิงบวกฉันควร? เป็นเพียงการบอกเป็นนัย ๆ ว่าวิธีการนั้นไม่ส่งคืน int เชิงลบเพราะฉันตรวจสอบแล้ว มันไม่ชัดเจนจากลายเซ็นหรือเอกสารที่ไม่ได้
Didier A.

1
ไม่สำคัญว่าสัญญาจะเป็นนัยหรือชัดเจน ไม่ใช่งานของรหัสการโทรเพื่อตรวจสอบว่าวิธีการที่เรียกคืนข้อมูลที่ถูกต้องเมื่อได้รับข้อมูลที่ถูกต้อง ในตัวอย่างของคุณคุณรู้ว่า inits เชิงลบไม่ใช่คำตอบที่ไม่ถูกต้อง แต่คุณรู้หรือไม่ว่า int เชิงบวกนั้นถูกต้อง? คุณต้องการการทดสอบกี่ครั้งเพื่อพิสูจน์ว่าวิธีการนี้ใช้งานได้จริง หลักสูตรที่ถูกต้องเพียงอย่างเดียวคือการคิดว่าวิธีการที่ถูกต้อง
Vince O'Sullivan

5

ฉันขอยืนยันว่าหลักฐานของคำถามของคุณปิดอยู่: ทำไมต้องใช้การอ้างอิงแบบ null เพื่อเริ่มต้นด้วย

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

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

ทำไมอัลกอริทึมควรใส่ใจเกี่ยวกับค่า Null? ขั้นตอนควรเหมือนกัน มันง่ายและชัดเจนกว่าที่จะมีวัตถุว่างเปล่าที่คืนค่าศูนย์สตริงว่าง ฯลฯ

นี่คือตัวอย่างของการตรวจสอบโค้ดสำหรับ null:

User u = getUser();
String displayName = "";
if (u != null) {
  displayName = u.getName();
}
return displayName;

นี่คือตัวอย่างของรหัสโดยใช้วัตถุ null:

return getUser().getName();

ไหนดีกว่า ฉันเชื่อว่าส่วนที่สองคือ


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


Null เป็นเพียงตัวอย่างของสิ่งที่ฉันยืนยัน มันอาจจะดีถ้าฉันต้องการชื่อที่ไม่ว่างเปล่า คำถามของฉันจะยังคงใช้คุณจะตรวจสอบว่า? หรืออาจเป็นสิ่งที่คืนค่า int และฉันไม่สามารถมี int เป็นค่าลบหรือต้องอยู่ในช่วงที่กำหนด อย่าคิดว่ามันเป็นปัญหาที่ไม่มีประโยชน์ต่อการพูด
Didier A.

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

ลองนึกภาพฉันมี: int i = getCount; float x = 100 / i; ถ้าฉันรู้ว่า getCount ไม่ส่งคืน 0 เพราะฉันเขียนวิธีหรือตรวจสอบแล้วฉันควรข้ามการตรวจสอบเป็น 0 หรือไม่
Didier A.

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

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

1

โดยทั่วไปฉันจะบอกว่าส่วนใหญ่ขึ้นอยู่กับสามด้านต่อไปนี้:

  1. ความทนทาน: วิธีการโทรสามารถรับมือกับnullค่าได้หรือไม่ หากnullค่าอาจส่งผลให้RuntimeExceptionฉันขอแนะนำให้ตรวจสอบ - ยกเว้นความซับซ้อนต่ำและวิธีการโทร / โทรถูกสร้างขึ้นโดยผู้เขียนคนเดียวกันและnullไม่ได้คาดหวัง (เช่นทั้งprivateในแพคเกจเดียวกัน)

  2. ความรับผิดชอบของโค้ด: หากนักพัฒนา A รับผิดชอบต่อgetUser()วิธีการและนักพัฒนา B ใช้มัน (เช่นเป็นส่วนหนึ่งของห้องสมุด) ฉันขอแนะนำอย่างยิ่งให้ตรวจสอบความถูกต้องของค่า เพียงเพราะนักพัฒนา B อาจไม่ทราบเกี่ยวกับการเปลี่ยนแปลงที่ส่งผลให้มีnullค่าตอบแทนที่เป็นไปได้

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

นอกจากนี้ฉันขอแนะนำให้จัดทำเอกสารที่มีศักยภาพ nullค่าส่งคืนที่เป็นไปได้ในความคิดเห็น JavaDoc เนื่องจากการไฮไลต์ของคำอธิบาย JavaDoc ใน IDEs getUser()ส่วนใหญ่นี้อาจจะเป็นคำเตือนที่เป็นประโยชน์สำหรับใครก็ตามที่ใช้

/** Returns ....
  * @param: id id of the user
  * @return: a User instance related to the id;
  *          null, if no user with identifier id exists
 **/
User getUser(Int id)
{
    ...
}

-1

คุณไม่จำเป็นต้องทำอย่างแน่นอน

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


1
ฉันรู้ แต่เพียงเพราะฉันตรวจสอบหรือเขียนการใช้งานของวิธีอื่น สัญญาของวิธีการนี้ไม่ได้บอกอย่างชัดเจนว่าไม่สามารถทำได้ ตัวอย่างเช่นมันสามารถเปลี่ยนแปลงได้ในอนาคต หรืออาจมีข้อผิดพลาดที่ทำให้ null กลับแม้ว่าจะพยายามไม่
Didier A.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.