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


10

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

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

ดังนั้นการรับประกันการเปลี่ยนแปลงไม่ได้จึงเป็นเหตุผลที่ถูกกฎหมายในการเลือกreadonlyเขตข้อมูลเหนือทรัพย์สินในบางกรณี?

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



1
@ เพียงแค่ชี้แจงโดย "คุณสมบัติแบบอ่านอย่างเดียว" พวกเขากำลังใช้คำศัพท์ VB.NET ซึ่งหมายถึงคุณสมบัติที่ไม่มีตัวตั้งค่าไม่ใช่เขตข้อมูลแบบอ่านอย่างเดียว สำหรับวัตถุประสงค์ของคำถามของฉันทั้งสองตัวเลือกที่คำถามนั้นเปรียบเทียบกัน - พวกเขาทั้งสองใช้คุณสมบัติ
Ben Aaronson

คำตอบ:


6

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

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

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

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

  1. สิ่งใดก็ตามที่เขียนไปยังสมาชิกของวัตถุนั้นเช่น: object.Location.X = 4จะทำได้Locationก็ต่อเมื่อเป็นเขตข้อมูล สิ่งนี้ไม่เกี่ยวข้องกับเขตข้อมูลแบบอ่านอย่างเดียวเนื่องจากคุณไม่สามารถแก้ไขโครงสร้างแบบอ่านอย่างเดียว (นี่ถือว่าLocationเป็นประเภทค่า - หากไม่ใช่แล้วปัญหานี้จะไม่นำมาใช้เพราะreadonlyจะไม่ป้องกันสิ่งนั้น)
  2. โทรไปที่วิธีการที่ผ่านค่าเป็นหรือout refอีกครั้งสิ่งนี้ไม่เกี่ยวข้องกับเขตข้อมูลแบบอ่านอย่างเดียวเนื่องจากoutและrefพารามิเตอร์มีแนวโน้มที่จะได้รับการแก้ไขและเป็นข้อผิดพลาดของคอมไพเลอร์ต่อไปเพื่อส่งผ่านค่าแบบอ่านอย่างเดียวหรืออ้างอิง

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

ฉันไม่เห็นข้อได้เปรียบใด ๆ กับreadonlyฟิลด์สมาชิกและการไม่สามารถมีสิ่งนั้นบนอินเทอร์เฟซก็เป็นข้อเสียสำหรับฉันมากพอที่ฉันจะไม่ใช้มัน


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

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

2

จากประสบการณ์ของฉันreadonlyคำหลักไม่ใช่เวทมนตร์ที่สัญญาไว้

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

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

ข้อ จำกัด ทางเทคนิคนี้ได้รับการยอมรับจากนักออกแบบของ C # และอาจได้รับการแก้ไขในรุ่นอนาคต แต่จนกว่าจะได้รับการแก้ไขการใช้readonlyจะเข้มงวดมากขึ้นและอาจสนับสนุนรูปแบบการเข้ารหัสที่ไม่ดี

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


1
ฉันชอบที่จะทำให้คอนสตรัคเตอร์เรียบง่าย - ดูbrendan.enrick.com/post/… , blog.ploeh.dk/2011/03/03/InjectConstructorsshouldbesimpleดังนั้นในความเป็นจริงอ่านง่ายส่งเสริมสไตล์การเข้ารหัสที่ดี
AlexFoxGill

1
คอนสตรัคสามารถส่งผ่านreadonlyฟิลด์เป็นพารามิเตอร์outหรือrefวิธีการอื่น ๆ ซึ่งจะมีอิสระในการปรับเปลี่ยนตามที่เห็นสมควร
supercat

1

ฟิลด์เป็นรายละเอียดการใช้งานเสมอ คุณไม่ต้องการเปิดเผยรายละเอียดการใช้งานของคุณ

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

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

เพียงเพราะเราสามารถทำอะไรกับภาษาไม่ได้หมายความว่าเราควรทำอะไรกับภาษา

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


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

แต่คุณสามารถมั่นใจได้หรือไม่ว่าการใช้งานโดยใช้floatประเภทสำหรับNumeratorและDenominatorไม่จำเป็นต้องเปลี่ยนเป็นdoubles?
Stephen

1
เอ่อไม่ใช่พวกเขาควรจะไม่floatและdoubleแน่นอน พวกเขาควรจะint, หรือlong BigIntegerอาจจะต้องเปลี่ยน ... แต่ถ้าเป็นเช่นนั้นพวกเขาจะต้องเปลี่ยนในส่วนต่อประสานสาธารณะด้วยเช่นกันดังนั้นการใช้คุณสมบัติจึงไม่มีการห่อหุ้มรายละเอียดนั้น
Ben Aaronson

-3

ไม่มันไม่ใช่เหตุผล

การใช้แบบอ่านอย่างเดียวเป็นการรับประกันความสามารถในการเข้าไม่ถึงการให้เหตุผลก็เหมือนแฮ็ค

อ่านอย่างเดียวหมายความว่าค่าถูกกำหนดโดยนวกรรมิก o เมื่อกำหนดตัวแปร

ฉันคิดว่ามันจะเป็นการปฏิบัติที่ไม่ดี

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

ตัวอย่างอินเตอร์เฟสที่เรียบง่าย: ป้อนคำอธิบายรูปภาพที่นี่


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