ฉันจะจัดการกับการป้อนข้อมูลผู้ใช้ที่ไม่ถูกต้องได้อย่างไร?


12

ฉันคิดถึงเรื่องนี้มาระยะหนึ่งแล้วและอยากรู้อยากเห็นจากนักพัฒนาคนอื่น ๆ

ฉันมักจะมีรูปแบบการเขียนโปรแกรมที่ป้องกันมาก บล็อกหรือวิธีทั่วไปของฉันมีลักษณะดังนี้:

T foo(par1, par2, par3, ...)
{
    // Check that all parameters are correct, return undefined (null)
    // or throw exception if this is not the case.

    // Compute and (possibly) return result.
}

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

เพื่อให้เป็นไปในทางที่เป็นนามธรรมยิ่งกว่าแนวทางของฉันคือ

if all input is OK --> compute result
else               --> do not compute result, notify problem

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

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

สรุป

คุณจะแนะนำวิธีใดในสองวิธีในการจัดการอินพุตที่ไม่ถูกต้อง

Inconsistent input --> no action + notification

หรือ

Inconsistent input --> undefined behaviour or crash

แก้ไข

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

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

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


รหัสป้องกันเสมอ ในที่สุดด้วยเหตุผลด้านประสิทธิภาพคุณสามารถวางสวิตช์เพื่อปิดการทดสอบบางอย่างในโหมดการเปิดตัว
deadalnix

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

1
Rule of Repair: เมื่อคุณต้องล้มเหลวให้ล้มเสียงดังและเร็วที่สุด
deadalnix

"Rule of Repair: เมื่อคุณต้องล้มเหลวให้ทำเสียงดังไม่ดังและเร็วที่สุด": ฉันเดาว่า BSOD ทั้งหมดของ Windows เป็นแอปพลิเคชันของกฎนี้ :-)
Giorgio

คำตอบ:


8

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

มีจุดดีด้านบน: เกิดอะไรขึ้นถ้าอินพุตไม่ถูกต้อง แต่โปรแกรมไม่ทำงานผิดพลาด? จากนั้นคุณจะได้รับขยะในฐานข้อมูลและข้อผิดพลาดในบรรทัด

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

สี่ทศวรรษที่ผ่านมาได้รับวิทยาศาสตรบัณฑิตของฉันจากวิทยาการคอมพิวเตอร์ UCBerkeley เราบอกว่าโปรแกรมที่ดีคือการจัดการข้อผิดพลาด 50% เป็นคนหวาดระแวง


ใช่ IMHO นี่เป็นหนึ่งในไม่กี่สถานการณ์ที่หวาดระแวงเป็นคุณลักษณะและไม่ใช่ปัญหา
จอร์โจ

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

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

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

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

7

คุณมีความคิดที่ถูกต้องอยู่แล้ว

คุณจะแนะนำวิธีใดในสองวิธีในการจัดการอินพุตที่ไม่ถูกต้อง

อินพุตที่ไม่สอดคล้อง -> ไม่มีการดำเนินการ + การแจ้งเตือน

หรือดีกว่า

อินพุตที่ไม่สอดคล้องกัน -> การดำเนินการที่จัดการอย่างเหมาะสม

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

อารมณ์ความรู้สึกของคนที่คลั่งไคล้ลัทธิปฏิบัตินิยม

Steve McConnell กล่าวว่าดีที่สุด

Steve McConnell เขียนหนังสือเล่มนี้ ( Code Complete ) เกี่ยวกับการตั้งโปรแกรมป้องกันและนี่เป็นหนึ่งในวิธีการหนึ่งที่เขาแนะนำว่าคุณควรตรวจสอบอินพุตของคุณเสมอ

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


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

3

ไม่มีคำตอบที่ "ถูกต้อง" ที่นี่โดยเฉพาะอย่างยิ่งโดยไม่ต้องระบุภาษาประเภทของรหัสและประเภทของผลิตภัณฑ์ที่รหัสอาจเข้าสู่ พิจารณา:

  • เรื่องภาษา ใน Objective-C การส่งข้อความไปยังศูนย์มักจะไม่เป็นไร ไม่มีอะไรเกิดขึ้น แต่โปรแกรมก็ไม่ผิดพลาดเช่นกัน Java ไม่มีพอยน์เตอร์ที่ชัดเจนดังนั้นไม่มีพอยน์เตอร์กังวลมาก ใน C คุณต้องระวังให้มากขึ้นอีกเล็กน้อย

  • การเป็นคนหวาดระแวงคือการสงสัยอย่างไร้เหตุผลหรือไม่ไว้วางใจ อาจไม่ดีไปกว่าซอฟต์แวร์สำหรับคนทั่วไป

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

  • คุณไม่สามารถระบุอินพุตที่ไม่ดีได้เสมอ คุณสามารถเปรียบเทียบพอยน์เตอร์ของคุณกับทางศาสนาได้ แต่จะจับค่าที่เป็นไปได้เพียงหนึ่งใน 2 ^ 32 ซึ่งเกือบทั้งหมดเป็นค่าที่ไม่ดี

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

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


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

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

1

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

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

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

ดังนั้นคำตอบสั้น ๆ ของฉันก็คือตัวเลือกแรกของคุณดีที่สุด

Inconsistent input -> no action + notify caller

1

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

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

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


0

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

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

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


ใน Java คุณจะได้รับข้อยกเว้นตัวชี้ null ใน C ++ ตัวชี้ null จะขัดข้องแอปพลิเคชัน อาจมีตัวอย่างอื่น ๆ : การหารด้วยศูนย์, ดัชนีอยู่นอกช่วงและอื่น ๆ
Giorgio
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.