คุณควรยืนยันว่าไม่ได้เป็นโมฆะกับคำสั่งยืนยันในรหัสการผลิต? [ปิด]


32

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

ตัวอย่าง:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

ตอนนี้คิดว่าฉันใช้มันเช่นนี้:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

ฉันบอกว่าฉันควรลบassert, ว่าพวกเขาไม่ควรใช้ในรหัสการผลิต, ใช้ในการทดสอบเท่านั้น มันเป็นเรื่องจริงเหรอ?


19
ตามค่าเริ่มต้นการยืนยันจะถูกปิดใช้งาน คุณต้องเปิดใช้งานอย่างชัดเจนขณะใช้งานผ่าน-eaหรือเทียบเท่า
jarmod

คำถามที่เกี่ยวข้องกับวิศวกรรมซอฟต์แวร์ : softwareengineering.stackexchange.com/q/137158/187318 (แม้ว่าจะค่อนข้างเก่าดังนั้นคำตอบที่ได้รับการยอมรับยังคงแนะนำห้องสมุดภายนอกซึ่งไม่จำเป็นอีกต่อไปตั้งแต่การแนะนำObjects.requireNonNullใน Java 8)
ฮัลค์

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

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

คำตอบ:


19

ใช้Objects.requireNonNull(Object)สำหรับการที่

ตรวจสอบว่าการอ้างอิงวัตถุที่ระบุไม่ใช่โมฆะ วิธีนี้ถูกออกแบบมาเพื่อทำการตรวจสอบความถูกต้องของพารามิเตอร์ในวิธีการและตัวสร้าง [... ]

ในกรณีของคุณสิ่งนี้จะเป็น:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

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

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

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

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

ตรวจสอบอย่างเป็นทางการเอกสารassertรายละเอียดเกี่ยวกับ


14
ใครและเมื่อใดที่ประกาศว่าเป็นมรดก? ลิงค์ใด ๆ / การอ้างอิง? ฉันนึกภาพไม่ออกว่านักพัฒนาสติสัมปชัญญะใด ๆ ที่นิยาม "มรดก" เครื่องมือที่เปิดใช้งานการตรวจสอบ zero-footprint ที่สามารถเปิดใช้งานได้ง่ายในการทดสอบและปิดการใช้งานในการผลิต
Dmitry Pisklov

ระบุว่าคุณสามารถตัดสินใจที่รันไทม์ว่า assert ทำงานหรือไม่ แต่คุณไม่สามารถระบุได้ว่ารันไทม์ว่า NPE ถูกโยนโดย requireNonNull หรือไม่คุณหมายถึงอะไรโดย 'คุณมีการควบคุมมากกว่าด้วย assert'
Pete Kirkham

@DmitryPisklov คุณพูดถูกฉันได้แก้ไขแล้ว มันเป็นเครื่องมือที่ยอดเยี่ยมสำหรับการทดสอบ
akuzminykh

2
@PeteKirkham คุณพูดถูกการควบคุมนั้นเป็นคำที่ผิด ฉันพูดถึงความยืดหยุ่นในไวยากรณ์มากขึ้น นอกจากนี้: นี่เป็นเหตุผลที่พบได้ว่าทำไมคุณต้องการรหัสเพื่อโยน NPE
akuzminykh

1
"อย่าใช้มันสำหรับรหัสการผลิตมันอาจทำให้แอปพลิเคชันช้าลง" ได้ แต่อาจคุ้มค่า Java มีการตรวจสอบรันไทม์ในตัวเพื่อให้แน่ใจว่าคุณจะไม่บุกรุกอาร์เรย์มากเกินไป สิ่งเหล่านี้ถูกเปิดใช้งานแม้ในการปรับใช้งานจริง เราไม่ได้เลือกแม้แต่เรื่องนี้ การตรวจสอบรันไทม์ในรหัสการผลิตไม่ผิดเสมอไป
Max Barraclough

22

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

สำหรับความเข้ากันได้แบบย้อนหลัง JVM จะปิดใช้งานการตรวจสอบยืนยันตามค่าเริ่มต้น พวกเขาจะต้องเปิดใช้งานอย่างชัดเจนโดยใช้อาร์กิวเมนต์บรรทัดคำสั่ง -enableassertions หรือ shorthand -ea ของมัน:

java -ea com.whatever.assertion.Assertion

ดังนั้นจึงไม่ใช่วิธีที่ดีที่จะพึ่งพาพวกเขา

ตามค่าเริ่มต้นการยืนยันจะไม่ถูกเปิดใช้งานคุณจะไม่สามารถสันนิษฐานได้ว่าจะถูกเรียกใช้งานเมื่อใช้ในรหัส ดังนั้นคุณควรตรวจสอบค่า Null และ Optionals ที่ว่างอยู่เสมอหลีกเลี่ยงการใช้ assertions เพื่อตรวจสอบอินพุตเป็นวิธีสาธารณะและใช้ข้อยกเว้นที่ไม่ถูกตรวจสอบแทน ... โดยทั่วไปจะทำการตรวจสอบทั้งหมดราวกับว่าไม่มีการยืนยัน


เห็นได้ชัดว่าการปฏิบัติที่ไม่ดีจะพึ่งพาพวกเขา แต่มันเป็นการปฏิบัติที่ไม่ดีที่จะใช้โดยทั่วไปหรือไม่?
Big_Bad_E

3
@Big_Bad_E การยืนยันเป็นเครื่องมือตรวจแก้จุดบกพร่องที่มีอยู่เพื่อทำให้โปรแกรมล้มเหลวอย่างเร็วที่สุดเท่าที่จะเป็นไปได้ แล้วมันเป็นงานของชุดทดสอบเพื่อให้แน่ใจว่ามีวิธีที่โปรแกรมสามารถล้มเหลวไม่มีแม้จะยืนยัน แนวคิดคือเมื่อชุดทดสอบ (ได้รับความคุ้มครอง 100% ไม่ใช่หรือไม่?) ดำเนินการโดยไม่มีข้อผิดพลาดมันจะบันทึกเพื่อลบการยืนยันเนื่องจากไม่สามารถเรียกใช้งานได้ แน่นอนว่ามีเหตุผลในอุดมคติในเรื่องนี้ แต่นั่นเป็นความคิด
cmaster - คืนสถานะโมนิก้า

11

แน่นอนสิ่งที่คุณจะบอกว่าเป็นเรื่องโกหกที่เห็นได้ชัด นี่คือเหตุผล

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

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

สำหรับตัวอย่างที่น่าสนใจเกี่ยวกับวิธีการใช้ asserts ให้ดูที่นี่ - ท้ายไฟล์มีวิธีการsingleThreadedAccess()ที่เรียกจากคำสั่ง assert ในบรรทัด 201 และมีการเข้าถึงแบบมัลติเธรดที่มีศักยภาพในการทดสอบ


4

คำตอบอื่น ๆ ครอบคลุมเรื่องนี้ดีพอ แต่มีตัวเลือกอื่น ๆ

ตัวอย่างเช่น Spring มีวิธีการคงที่:

org.springframework.util.Assert.notNull(obj)

มีห้องสมุดอื่น ๆ ที่มีAssert.something()วิธีการของตัวเองเช่นกัน มันค่อนข้างง่ายในการเขียนของคุณเอง

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

ในกรณีของการบริการเว็บมักจะไม่ใช่ข้อผิดพลาดของเซิร์ฟเวอร์ภายในและไม่ควรเป็น 500 แต่ควรเป็น 400 ซึ่งเป็นคำขอที่ไม่ดี


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

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

อาชวากำหนดแน่นอนที่assertจะโยน น่าสนใจ รุ่น C / C ++ ไม่ได้ทำสิ่งนั้น มันจะยกสัญญาณทันทีที่ก) ฆ่ากระบวนการและข) สร้างดัมพ์หลัก ด้วยเหตุผลนี้: เป็นเรื่องง่ายที่จะแก้ไขข้อผิดพลาดในการยืนยันเนื่องจากคุณยังมีข้อมูลทั้งหมดเกี่ยวกับ call stack ที่พร้อมใช้งาน กำหนดassertให้โยนข้อยกเว้นแทนซึ่งอาจจะถูกจับ (un) โดยเจตนาทางโปรแกรมเอาชนะเป้าหมาย
cmaster - คืนสถานะโมนิก้า

เช่นเดียวกับที่มีสิ่งแปลก ๆ (หรือหลายอย่าง) ใน C และ C ++ มีบางอย่างใน Java Throwableหนึ่งในนั้นคือ พวกเขาสามารถถูกจับได้เสมอ ไม่ว่าจะเป็นสิ่งที่ดีหรือไม่ฉันไม่รู้ ฉันคิดว่ามันขึ้นอยู่กับ โปรแกรม Java หลายโปรแกรมเป็นบริการบนเว็บและจะไม่เป็นที่พึงปรารถนาที่จะเกิดความผิดพลาดไม่ว่าด้วยเหตุผลใดก็ตามดังนั้นทุกสิ่งจึงถูกจับและบันทึกไว้ หนึ่งในสิ่งที่ยอดเยี่ยมคือการติดตามสแต็กและปกติแล้วเพียงพอที่จะวินิจฉัยสาเหตุของข้อยกเว้นหรือข้อผิดพลาดด้วยตัวเอง
Christopher Schneider

3

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

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

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

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

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


2

คุณสามารถใช้ยืนยันได้ตลอดเวลา การอภิปรายมาถึงเมื่อใช้ ตัวอย่างในคำแนะนำ :

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

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