เหตุใดจึงเป็นความคิดที่ไม่ดีที่จะสร้าง setter ทั่วไปและทะเยอทะยานด้วยการสะท้อน?


49

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

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


12
หากคุณเพียงต้องการลดสำเร็จรูปทั้งลอมบอกและ Groovy จะสร้าง getters และ setters อย่างเงียบ ๆ แต่ปลอดภัย
chrylis -on strike-

2
คุณจะใช้ตัวรับและตัวตั้งค่าเหล่านี้เป็นภาษาแบบคงที่เช่น java ได้อย่างไร ดูเหมือนว่าไม่ใช่ผู้เริ่มต้นสำหรับฉัน
Esben Skov Pedersen

@EsbenSkovPedersen คุณจะกินพวกมันเหมือนเป็นMapของตัวเองgetและputมันก็เป็นวิธีการทำสิ่งที่ค่อนข้างน่าเบื่อ
HAEM

2
IIRC, ลอมบอกสังเคราะห์ตัวรับสัญญาณ / ตัวเซ็ต ณ เวลารวบรวมตามคำอธิบายประกอบที่เหมาะสมดังนั้นพวกเขา: ไม่ต้องเสียค่าปรับในการสะท้อนภาพสามารถวิเคราะห์ / อินไลน์แบบคงที่ได้อีกครั้ง (IntelliJ มีปลั๊กอินสำหรับลอมบอก)
Alexander

คำตอบ:


117

ข้อเสียของการสะท้อนโดยทั่วไป

การสะท้อนนั้นเข้าใจได้ยากกว่าโค้ดแบบเส้นตรง

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

โค้ดสะท้อนไม่สามารถเข้าถึงการวิเคราะห์แบบคงที่ได้

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

แต่ถ้าสถานที่ที่ใช้ผู้ทะเยอทะยานเป็นสิ่งที่ชอบcallGetter("Foo")และcallGetterทำgetClass().getMethod("get"+name).invoke(this)แล้ววิธีการด้านบนจะไม่พบและผู้เรียบเรียงจะไม่บ่น คุณจะได้รับรหัสเมื่อทำงานจริงNoSuchMethodExceptionหรือไม่ และจินตนาการถึงความเจ็บปวดที่คุณกำลังเผชิญหากข้อยกเว้นนั้น (ซึ่งถูกติดตาม) ถูกกลืนโดยcallGetterเพราะ "มันใช้กับสตริงที่มีการเข้ารหัสยากเท่านั้นมันจะไม่เกิดขึ้นจริง" (ไม่มีใครทำอย่างนั้นใครบางคนอาจเถียง? ยกเว้นว่า OP ทำอย่างนั้นในคำตอบ SO ของเขาหากมีการเปลี่ยนชื่อฟิลด์ผู้ใช้ setter ทั่วไปจะไม่สังเกตเห็น ผู้ใช้ getter อาจสังเกตเห็นผลลัพธ์ของคอนโซลของข้อยกเว้นที่ถูกละเว้น)

โค้ดตัวสะท้อนไม่ได้ตรวจสอบโดยคอมไพเลอร์

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

ไม่สามารถใช้รหัสการสะท้อนได้

อาจจะไม่ได้อยู่ในทฤษฎี แต่ในทางปฏิบัติคุณจะไม่พบว่า JVM inlines Method.invokeหรือสร้างแคชอินไลน์สำหรับ การเรียกใช้เมธอดปกติพร้อมใช้งานสำหรับการปรับแต่งเช่นนี้ ทำให้พวกเขาเร็วขึ้นมาก

รหัสตัวสะท้อนนั้นโดยทั่วไปแล้วจะช้า

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

ข้อเสียของ getter / setter ทั่วไปโดยเฉพาะ

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


8
คุณตีจุดสำคัญทั้งหมด คำตอบที่สมบูรณ์แบบ ฉันสามารถเล่นลิ้นได้ว่า getters และ setter นั้นดีกว่าทุ่งสาธารณะ แต่จุดที่ใหญ่กว่านั้นก็คือความถูกต้อง
JimmyJames

2
นอกจากนี้ยังมีมูลค่าการกล่าวขวัญว่าสามารถสร้าง getters / setters ได้ง่ายโดย IDE เช่นกัน
stiemannkj1

26
+1 ตรรกะการแก้จุดบกพร่องสะท้อนในโครงการขนาดการผลิตควรได้รับการยอมรับว่าเป็นการทรมานโดยสหประชาชาติและผิดกฎหมาย
เดินทางที่หลงผิดใน

4
"ยากที่จะเข้าใจสำหรับโปรแกรมเมอร์เหล่านี้" - สำหรับโปรแกรมเมอร์เหล่านี้มันยากที่จะเข้าใจอย่างมีนัยสำคัญ แต่มันยากที่จะเข้าใจสำหรับทุกคนไม่ว่าจะมีความชำนาญเพียงใด
BartoszKP

4
"รหัสสะท้อนไม่สามารถเข้าถึงการวิเคราะห์แบบคงที่" - นี่ สำคัญมากสำหรับการเปิดใช้งานการเปลี่ยนโครงสร้าง และเมื่อเครื่องมือการวิเคราะห์แบบสแตติกได้รับประสิทธิภาพมากขึ้น (เช่นการค้นหาโค้ดใน Team Foundation Server ล่าสุด) การเขียนโค้ดที่ทำให้พวกเขามีค่ายิ่งขึ้น
Dan J

11

เนื่องจาก pass-through getter / setters เป็นสิ่งที่น่ารังเกียจที่ไม่มีคุณค่า พวกเขาไม่มีการห่อหุ้มใด ๆ เนื่องจากผู้ใช้สามารถรับค่าที่แท้จริงผ่านคุณสมบัติ พวกเขาเป็น YAGNI เพราะคุณไม่ค่อยจะเปลี่ยนการใช้พื้นที่เก็บข้อมูลของคุณ

และเพราะนี่เป็นวิธีที่มีประสิทธิภาพน้อยกว่า ใน C # อย่างน้อย getter / setter จะอินไลน์ตรงไปยังคำสั่ง CIL เดียว ในคำตอบของคุณคุณกำลังแทนที่ด้วยการเรียกใช้ฟังก์ชัน 3 ครั้งซึ่งจำเป็นต้องโหลดข้อมูลเมตาเพื่อสะท้อนกลับ โดยทั่วไปฉันใช้ 10x เป็นกฎง่ายๆในการสะท้อนต้นทุน แต่เมื่อฉันค้นหาข้อมูลหนึ่งในคำตอบแรกรวมคำตอบนี้ซึ่ง ballparked มันใกล้ถึง 1,000 เท่า

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


2
โปรดทราบว่าคำถามนั้นมีแท็ก Java ซึ่งมีอึที่น่ายินดีเช่นแนวคิด Beans ถั่วไม่มีฟิลด์สาธารณะ แต่อาจเปิดเผยฟิลด์ผ่านgetX()และsetX()วิธีการที่สามารถพบได้ผ่านการสะท้อนกลับ ถั่วไม่จำเป็นต้องห่อหุ้มคุณค่าของพวกเขา แต่พวกเขาอาจจะเป็น DTO ดังนั้นใน Java ผู้ได้รับความเจ็บปวดจึงเป็นสิ่งจำเป็น คุณสมบัติของ C # นั้นมากมายกว่าทุกประการ
amon

11
ถึงแม้ว่าคุณสมบัติของ C # นั้นจะเป็น getters / setters แต่ใช่แนวคิดของถั่วเป็นหายนะ
Sebastian Redl

6
@ ขวาใช่, C # เอาความคิดที่แย่และทำให้ง่ายต่อการนำไปใช้
JimmyJames

5
@JimmyJames มันไม่ได้เป็นความคิดที่น่ากลัวจริงๆ; คุณสามารถรักษาความเข้ากันได้ของ ABI แม้ว่าจะมีการเปลี่ยนแปลงรหัสก็ตาม ฉันไม่คิดว่าจะมีปัญหาอะไร
Casey

3
@Casey ขอบเขตของสิ่งนี้เกินความคิดเห็น แต่การสร้างอินเทอร์เฟซจากรายละเอียดภายในของการนำไปใช้นั้นจะล้าหลัง มันนำไปสู่การออกแบบขั้นตอน มีบางครั้งที่การใช้เมธอด getX เป็นคำตอบที่ถูกต้อง แต่มันก็ไม่บ่อยนักในรหัสที่ออกแบบมาอย่างดี ปัญหาเกี่ยวกับ getters และ setters ใน Java ไม่ใช่รหัสต้นแบบที่อยู่รอบตัวพวกเขามันเป็นส่วนหนึ่งของวิธีการออกแบบที่แย่มาก การทำให้ง่ายขึ้นนั้นก็เหมือนกับการกดหมัดตัวเองในหน้า
JimmyJames

8

นอกจากข้อโต้แย้งที่นำเสนอแล้ว

ไม่ได้ให้อินเทอร์เฟซที่คาดไว้

ผู้ใช้คาดหวังว่าจะเรียก foo.getBar () และ foo.setBar (ค่า) ไม่ใช่ foo.get ("bar") และ foo.set ("bar", value)

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


ดูเหมือนกับฉันว่าเหตุผลนี้สำคัญกว่าคำตอบอื่น ๆ ทั้งหมด
Esben Skov Pedersen

-4

แน่นอนว่าไม่ใช่ความคิดที่ไม่ดีมันเป็นเพียงแค่ในโลก java ปกติเป็นความคิดที่ไม่ดี (เมื่อคุณต้องการเพียงผู้ทะเยอทะยานหรือผู้ตั้งค่า) ตัวอย่างเช่นเฟรมเวิร์กบางตัว (spring mvc) หรือ librares ใช้มันแล้ว (jstl) ด้วยวิธีนี้ parsers json o xml บางตัวกำลังใช้งานอยู่หากคุณต้องการใช้ DSL คุณจะใช้มัน จากนั้นขึ้นอยู่กับสถานการณ์กรณีการใช้ของคุณ

ไม่มีอะไรถูกหรือผิดตามความเป็นจริง

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