มันเป็นนิสัยที่ไม่ดีที่จะใช้การสะท้อน (มากกว่า) หรือไม่?


16

เป็นวิธีปฏิบัติที่ดีหรือไม่หากใช้การสะท้อนกลับหากลดจำนวนรหัสสำเร็จรูปลงอย่างมาก?

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

แก้ไข: นี่คือตัวอย่างของการใช้การสะท้อนที่แนะนำ

เพื่อให้ตัวอย่างเช่นสมมติว่ามีระดับนามธรรมBaseซึ่งมี 10 สาขาและมี 3 subclasses SubclassA, SubclassBและSubclassCแต่ละคนมี 10 สาขาที่แตกต่างกัน พวกเขาทั้งหมดถั่วง่าย ๆ ปัญหาคือคุณได้รับBaseการอ้างอิงสองประเภทและคุณต้องการดูว่าวัตถุที่เกี่ยวข้องนั้นเป็นประเภทเดียวกัน (ย่อย) และเท่ากันหรือไม่

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

boolean compare(Base base1, Base, base2) {
    if (base1 instanceof SubclassA && base2 instanceof SubclassA) { 
         SubclassA subclassA1 = (SubclassA) base1;
         SubclassA subclassA2 = (SubclassA) base2;
         compare(subclassA1, subclassA2);
    } else if (base1 instanceof SubclassB && base2 instanceof SubclassB) {
         //the same
    }
    //boilerplate
}

boolean compare(SubclassA subA1, SubclassA subA2) {
    if (!subA1.getField1().equals(subA2.getField1)) {
         return false;
    }
    if (!subA1.getField2().equals(subA2.getField2)) {
         return false;
    }
    //boilerplate
}

boolean compare(SubclassB subB1, SubclassB subB2) {
    //boilerplate
}

//boilerplate

//alternative with reflection 
boolean compare(Base base1, Base base2) {
        if (!base1.getClass().isAssignableFrom(base2.getClass())) {
            System.out.println("not same");
            System.exit(1);
        }
        Method[] methods = base1.getClass().getMethods();
        boolean isOk = true;
        for (Method method : methods) {
            final String methodName = method.getName();
            if (methodName.startsWith("get")) {
                Object object1 = method.invoke(base1);
                Object object2 = method.invoke(base2);
                if(object1 == null || object2 == null)  {
                    continue;
                }
                if (!object1.equals(object2)) {
                    System.out.println("not equals because " + object1 + " not equal with " + object2);
                    isOk = false;
                }
            }
        }

        if (isOk) {
            System.out.println("is OK");
        }
}

20
การใช้อะไรมากเกินไปเป็นนิสัยที่ไม่ดี
Tulains Córdova

1
@ user61852 ถูกต้องเสรีภาพมากเกินไปนำไปสู่การปกครองแบบเผด็จการ ชาวกรีกโบราณบางคนรู้เรื่องนี้แล้ว
ott--

6
“ น้ำมากเกินไปจะไม่ดีสำหรับคุณ เห็นได้ชัดว่าจำนวนที่มากเกินไปนั้นแม่นยำ - นั่นคือความหมาย!” - สตีเฟ่นฟราย
Jon Purdy

4
"เมื่อใดก็ตามที่คุณพบว่าคุณกำลังเขียนโค้ดของแบบฟอร์ม" ถ้าวัตถุเป็นประเภท T1 ให้ทำบางสิ่ง แต่ถ้าเป็นประเภท T2 จากนั้นทำอย่างอื่น "ตบตัวเองjavapractices.com/topic/TopicAction.do?Id = 31
rm5248

คำตอบ:


25

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

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

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

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


12

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

ดังนั้นคุณกำลังเปรียบเทียบกับการเรียนที่แตกต่างกันนี้เป็นปัญหาที่สมบูรณ์แบบสำหรับวิธีการเอาชนะ โปรดทราบว่าอินสแตนซ์ของคลาสที่แตกต่างกันสองคลาสไม่ควรถูกพิจารณาว่าเท่าเทียมกัน คุณสามารถเปรียบเทียบความเท่าเทียมกันได้ก็ต่อเมื่อคุณมีอินสแตนซ์ของคลาสเดียวกัน ดู/programming/27581/overriding-equals-and-hashcode-in-javaสำหรับตัวอย่างวิธีใช้การเปรียบเทียบความเท่าเทียมกันอย่างถูกต้อง


16
+1 - พวกเราบางคนเชื่อว่าการใช้การสะท้อนใด ๆ เป็นธงสีแดงซึ่งบ่งบอกถึงการออกแบบที่ไม่ดี
Ross Patterson

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

2
@RossPatterson ทำไม?
m3th0dman

2
@ m3th0dman เพราะมันนำไปสู่สิ่งต่าง ๆ เช่นcompare()วิธีการของคุณสมมติว่าวิธีการใด ๆ ที่ขึ้นต้นด้วย "get" เป็น getter และปลอดภัยและเหมาะสมที่จะเรียกเป็นส่วนหนึ่งของการดำเนินการเปรียบเทียบ เป็นการละเมิดคำจำกัดความอินเทอร์เฟซที่ตั้งใจของวัตถุและในขณะที่อาจเป็นประโยชน์ก็เกือบจะผิดเสมอ
Ross Patterson

4
@ m3th0dman มันละเมิด encapsulation - คลาสฐานมีการเข้าถึงคุณลักษณะในคลาสย่อย เกิดอะไรขึ้นถ้าพวกเขาเป็นส่วนตัว (หรือทะเยอทะยานส่วนตัว)? สิ่งที่เกี่ยวกับความแตกต่าง? หากฉันตัดสินใจที่จะเพิ่มคลาสย่อยอื่นและฉันต้องการทำการเปรียบเทียบที่แตกต่างกัน ชั้นฐานได้ทำให้มันแล้วสำหรับฉันและฉันไม่สามารถเปลี่ยนได้ เกิดอะไรขึ้นถ้า getters ขี้เกียจโหลดอะไร? ฉันต้องการวิธีเปรียบเทียบเพื่อทำเช่นนั้นหรือไม่? ฉันจะรู้ได้อย่างไรว่าวิธีที่ขึ้นต้นด้วยgetgetter ไม่ใช่วิธีที่กำหนดเองที่คืนค่าบางอย่าง
Sulthan

1

ฉันคิดว่าคุณมีสองประเด็นที่นี่

  1. ฉันควรมีโค้ดไดนามิก vs สแตติกเท่าใด
  2. ฉันจะแสดงความเท่าเทียมรุ่นที่กำหนดเองได้อย่างไร

Dynamic vs Static Code

นี่เป็นคำถามที่ยืนต้นและคำตอบนั้นมีความเห็นมาก

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

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

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

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

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

ความเท่าเทียมที่กำหนดเอง

ในขณะที่ฉันเข้าใจว่า Java พึ่งพาอย่างมาก equalsวิธีการการเปรียบเทียบวัตถุสองชิ้นซึ่งไม่เหมาะสมเสมอไปในบริบทที่กำหนด

มีวิธีอื่นและเป็นมาตรฐาน Java ด้วย มันเรียกว่าComparatorเปรียบเทียบ

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

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

1

ฉันชอบที่จะหลีกเลี่ยงการเขียนโปรแกรมไตร่ตรองมากที่สุดเพราะมัน

  • ทำให้โค้ดยากขึ้นในการตรวจสอบโดยคอมไพเลอร์
  • ทำให้รหัสยากขึ้นสำหรับเหตุผลเกี่ยวกับ
  • ทำให้รหัสยากต่อการ refactor

นอกจากนี้ยังมีประสิทธิภาพน้อยกว่าการเรียกเมธอดธรรมดา มันเคยช้าตามลำดับความสำคัญหรือมากกว่า

การตรวจสอบรหัสแบบคงที่

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

ยากกว่าเกี่ยวกับเหตุผล

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

ยากที่จะ Refactor

เพราะรหัสเป็นแบบสตริง / แบบไดนามิก ทันทีที่การสะท้อนเกิดขึ้นในฉากคุณไม่สามารถสร้างรหัสใหม่ได้อย่างมั่นใจ 100% โดยใช้เครื่องมือการปรับโครงสร้างของ IDE เนื่องจาก IDE ไม่สามารถรับการสะท้อนกลับได้

โดยทั่วไปให้หลีกเลี่ยงการสะท้อนในรหัสทั่วไปถ้าเป็นไปได้; มองหาการออกแบบที่ดีขึ้น


0

การใช้คำจำกัดความที่มากเกินไปในสิ่งที่ไม่ดีใช่มั้ย งั้นลองกำจัด (ไป) ตอนนี้

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

สปริงสะท้อนแสงโดยใช้คำอธิบายประกอบ - เช่นเดียวกับ Hibernate (และอาจเป็นเครื่องมืออื่น ๆ อีกหลายสิบหรือหลายร้อย)

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

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


0

ฉันคิดว่าคำตอบส่วนใหญ่พลาดจุดนี้

  1. ใช่คุณควรเขียนequals()และhashcode()@KarlBielefeldt

  2. แต่สำหรับชั้นเรียนที่มีหลายสาขาอาจเป็นสิ่งที่น่าเบื่อ

  3. ดังนั้นมันขึ้นอยู่กับ

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

    • ใส่เขตข้อมูลลงในแผนที่
    • คุณยังสามารถเขียน getters และ setters แบบกำหนดเองได้
    • ใช้Map.equals()สำหรับการเปรียบเทียบของคุณ (หรือMap.hashcode())

เช่น ( หมายเหตุ : ละเว้นการตรวจสอบค่า Null, ควรใช้ Enums แทนการใช้คีย์ String, ไม่ได้แสดงมาก ... )

class TooManyFields {
  private HashMap<String, Object> map = new HashMap<String, Object>();

  public setFoo(int i) { map.put("Foo", Integer.valueOf(i)); }
  public int getFoo()  { return map.get("Foo").intValue(); }

  public setBar(Sttring s) { map.put("Bar", s); }
  public String getBar()  { return map.get("Bar").toString(); }

  ... more getters and setters ...

  public boolean equals(Object o) {
    return (o instanceof TooManyFields) &&
           this.map.equals( ((TooManyFields)o).map);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.