การโยนข้อยกเว้นจากแบบฟอร์มคุณสมบัติที่ไม่ดีหรือไม่?


15

ฉันมักจะคิดว่าคุณสมบัติ (เช่นการดำเนินงาน set / get ของพวกเขา) ควรรวดเร็ว / ทันทีและปราศจากความล้มเหลว คุณไม่จำเป็นต้องลอง / รับรอบหรือการตั้งค่าคุณสมบัติ

แต่ฉันกำลังมองหาวิธีที่จะใช้การรักษาความปลอดภัยตามบทบาทกับคุณสมบัติของวัตถุบางอย่าง ตัวอย่างเช่นคุณสมบัติ Employee.Salary วิธีแก้ปัญหาบางอย่างที่ฉันได้ลองใช้โดยที่คนอื่นพยายาม (โดยเฉพาะอย่างยิ่งหนึ่งคือตัวอย่าง AOP ที่นี่ ) เกี่ยวข้องกับการโยนข้อยกเว้นถ้า accessor ไม่มีสิทธิ์ที่ถูกต้อง - แต่สิ่งนี้ขัดกับกฎส่วนบุคคลที่ฉันมี เป็นเวลานานแล้ว

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


1
คำถามเดียวกันนี้ใน StackOverflowได้รับความสนใจมากขึ้นและคำตอบที่ดีกว่า
Roman Starkov

คำตอบ:


14

เมื่อคุณตั้งค่าของคุณสมบัติการโยนข้อยกเว้นในค่าที่ไม่ถูกต้องเป็นเรื่องปกติ

การรับค่าของคุณสมบัติควร (เกือบ) ไม่เคยเกิดข้อยกเว้น

สำหรับการเข้าถึงตามบทบาทให้ใช้อินเทอร์เฟซ / dumber ที่แตกต่างกันหรือส่วนหน้า อย่าปล่อยให้คนอื่นเห็นสิ่งที่พวกเขาไม่มี!


5

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


4

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

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

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

ตัวอย่างหนึ่งที่กล่าวถึงแล้วในคำตอบที่แตกต่างกันคือStream.Positionคุณสมบัติ สิ่งนี้จะสร้างผลข้างเคียงและอาจโยนข้อยกเว้น แต่ setter คุณสมบัตินี้เป็นเพียงเสื้อคลุมรอบ ๆStream.Seekที่คุณสามารถโทรหาแทน

โดยส่วนตัวฉันเชื่อว่าตำแหน่งไม่ควรเป็นทรัพย์สินที่เขียนได้

อีกตัวอย่างหนึ่งที่คุณอาจถูกล่อลวงให้โยนข้อยกเว้นจาก setter คุณสมบัติคือการตรวจสอบข้อมูล:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

แต่มีวิธีแก้ไขปัญหานี้ได้ดีกว่า แนะนำประเภทที่อยู่อีเมลที่ถูกต้อง:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

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

สิ่งนี้ยังนำไปสู่การทำงานร่วมกันที่สูงขึ้น (ตัวบ่งชี้ของการออกแบบซอฟต์แวร์ที่ดี) - ความรู้เกี่ยวกับที่อยู่อีเมลและวิธีการตรวจสอบความถูกต้องมีอยู่เฉพาะในEmailชั้นเรียนเท่านั้น

* ObjectDisposedException เป็นข้อยกเว้นที่ถูกต้องเท่านั้น (ไม่มีการเล่นสำนวนเจตนา) ฉันสามารถคิดได้ในขณะนี้


2

ฉันรู้ว่าคำถามของคุณเฉพาะกับ. NETแต่เนื่องจาก C # แบ่งปันประวัติบางอย่างกับ Java ฉันคิดว่าคุณอาจสนใจ ฉันไม่ได้หมายความว่าเพราะสิ่งที่ทำใน Java มันควรจะทำใน C # ฉันรู้ว่าทั้งสองแตกต่างกันมากโดยเฉพาะอย่างยิ่งในวิธีที่ C # ได้รับการสนับสนุนในระดับภาษาที่ดีขึ้นสำหรับคุณสมบัติ ฉันแค่ให้บริบทและมุมมอง

จากข้อมูลจำเพาะ JavaBeans :

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

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

กรุณาเอาทั้งหมดนี้ด้วยเม็ดเกลือ ข้อกำหนด JavaBeans นั้นเก่าและ C # Properties นั้น (IMO) เป็นการปรับปรุงครั้งใหญ่ในคุณสมบัติตาม "หลักการตั้งชื่อ" ที่ Java มี ฉันแค่พยายามให้บริบทเล็ก ๆ น้อย ๆ ก็คือทั้งหมด!


มันเป็นจุดที่ดี แต่ความแตกต่างระหว่างเมธอด setter และ ac # property setter นั้นมีความสำคัญพอที่จะเป็นกรณีอื่นสำหรับฉัน ในทำนองเดียวกันข้อยกเว้นที่ตรวจสอบของ Java บังคับให้รหัสการโทรต้องทราบว่าสามารถโยนข้อยกเว้นได้ดังนั้นจึงมีโอกาสน้อยที่สิ่งนี้จะจับทุกคนด้วยความประหลาดใจ
Steven Evers

2

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

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


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

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

1

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

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


0

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

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

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