Field Injection คืออะไรและจะหลีกเลี่ยงได้อย่างไร?


130

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

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

ในระหว่างการวิจัยของฉันฉันยังอ่านเกี่ยวกับการฉีดคอนสตรัคเตอร์ :

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

ข้อดีและข้อเสียของการฉีดทั้งสองประเภทนี้คืออะไร?


แก้ไข 1:เนื่องจากคำถามนี้ถูกทำเครื่องหมายว่าซ้ำกับคำถามนี้ฉันจึงตรวจสอบแล้ว สาเหตุที่ไม่มีตัวอย่างโค้ดใด ๆ ทั้งในคำถามหรือในคำตอบมันไม่ชัดเจนสำหรับฉันว่าฉันเดาถูกต้องหรือไม่ว่าฉันใช้การฉีดแบบไหน


3
หากการฉีดสนามไม่ดีอย่างที่คุณอธิบายเหตุใด Spring จึงอนุญาต Field Injection มีประโยชน์ในตัวเองในการทำให้โค้ดอ่านง่ายขึ้นและมีรายละเอียดน้อยลง หากคุณมีวินัยเพียงพอในการเขียนโค้ดคุณสามารถมั่นใจได้ว่าสิ่งต่างๆจะไม่พังแม้ว่าคุณจะใช้การฉีดฟิลด์ก็ตาม
ashes

1
@ashes เนื่องจากเป็นคุณสมบัติที่เรียบร้อยในเวลานั้นและผลกระทบไม่ได้ถูกคิดทั้งหมด เหตุผลเดียวกับที่Date(int,int,int)มีอยู่
chrylis -cautiouslyoptimistic-

คำตอบ:


225

ประเภทการฉีด

มีสามตัวเลือกสำหรับวิธีการฉีดการพึ่งพาลงในถั่ว:

  1. ผ่านตัวสร้าง
  2. ผ่าน setters หรือวิธีอื่น ๆ
  3. ผ่านการสะท้อนโดยตรงลงในสนาม

คุณกำลังใช้ตัวเลือก 3 นั่นคือสิ่งที่เกิดขึ้นเมื่อคุณใช้งาน@Autowiredโดยตรงบนสนามของคุณ


แนวทางการฉีด

แนวทางทั่วไปซึ่งแนะนำโดย Spring (ดูหัวข้อในDI ที่ใช้ตัวสร้างหรือDI ที่ยึดตามตัวตั้ง ) มีดังต่อไปนี้:

  • สำหรับการพึ่งพาที่จำเป็นหรือเมื่อมีเป้าหมายเพื่อการไม่เปลี่ยนรูปให้ใช้การฉีดตัวสร้าง
  • สำหรับการพึ่งพาที่เป็นทางเลือกหรือเปลี่ยนแปลงได้ให้ใช้ setter injection
  • หลีกเลี่ยงการฉีดสนามในกรณีส่วนใหญ่

ข้อเสียของการฉีดสนาม

สาเหตุที่ทำให้การฉีดยาในช่องขมวดมีดังนี้:

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

ข้อสรุป

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


อ่านเพิ่มเติม

ผมเขียนบทความบล็อกเกี่ยวกับสาเหตุการฉีดฟิลด์มักจะไม่แนะนำ: สนามพึ่งพาการฉีดพิจารณาอันตราย


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

6
นั่นอาจเป็นเช่นนั้นที่นี่ แต่ยังมีอีกกรณีหนึ่งที่ชุมชนได้มีมติร่วมกันในการกีดกันบางสิ่งบางอย่าง ยกตัวอย่างเช่นสัญกรณ์ฮังการี
Jannik

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

ฉันคิดว่าคุณหมายถึง "สำหรับการพึ่งพาที่จำเป็นหรือเมื่อมีเป้าหมายเพื่อการไม่เปลี่ยนรูป "
Alex Terreaux

1
ฉันอ้างถึงลิงก์ที่จุดเริ่มต้นของคำตอบซึ่งเชื่อมโยงไปยังเอกสารฤดูใบไม้ผลิ
Vojtech Ruzicka

47

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

การฉีดตัวสร้าง

ข้อดี:

  • การตรวจสอบที่ดีขึ้น คุณไม่จำเป็นต้องมีไลบรารีจำลองหรือบริบทฤดูใบไม้ผลิในการทดสอบหน่วย คุณสามารถสร้างออบเจ็กต์ที่ต้องการทดสอบด้วยคีย์เวิร์ดใหม่ การทดสอบดังกล่าวจะเร็วกว่าเสมอเพราะไม่ต้องอาศัยกลไกการสะท้อนกลับ ( คำถามนี้ถูกถามในอีก 30 นาทีต่อมาหากผู้เขียนใช้ constructor injection ก็จะไม่ปรากฏ)
  • ความไม่เปลี่ยนรูป เมื่อตั้งค่าการอ้างอิงแล้วจะไม่สามารถเปลี่ยนแปลงได้
  • รหัสที่ปลอดภัยยิ่งขึ้น หลังจากเรียกใช้คอนสตรัคเตอร์แล้ววัตถุของคุณก็พร้อมใช้งานเนื่องจากคุณสามารถตรวจสอบความถูกต้องของสิ่งที่ส่งผ่านเป็นพารามิเตอร์ วัตถุสามารถพร้อมหรือไม่ก็ได้ไม่มีสถานะอยู่ระหว่าง ด้วยการฉีดสนามคุณจะแนะนำขั้นตอนกลางเมื่อวัตถุเปราะบาง
  • เครื่องดูดแสดงออกของการอ้างอิงบังคับ การฉีดสนามมีความคลุมเครือในเรื่องนี้
  • ทำให้นักพัฒนาคิดเกี่ยวกับการออกแบบ DIT เขียนเกี่ยวกับคอนสตรัคกับ 8 พารามิเตอร์ซึ่งอันที่จริงเป็นสัญญาณของการออกแบบที่ไม่ดีและพระเจ้าวัตถุต่อต้านรูปแบบ ไม่สำคัญว่าคลาสจะมีการอ้างอิง 8 ตัวในตัวสร้างหรือในฟิลด์มันก็ผิดเสมอ ผู้คนไม่เต็มใจที่จะเพิ่มการอ้างอิงให้กับตัวสร้างมากกว่าผ่านฟิลด์ มันทำงานเป็นสัญญาณไปยังสมองของคุณว่าคุณควรหยุดสักพักและคิดถึงโครงสร้างโค้ดของคุณ

จุดด้อย:

  • รหัสเพิ่มเติม (แต่ IDE สมัยใหม่ช่วยบรรเทาความเจ็บปวดได้)

โดยทั่วไปการฉีดสนามจะตรงกันข้าม


1
ความสามารถในการทดสอบใช่มันเป็นฝันร้ายสำหรับฉันที่จะเยาะเย้ยเมล็ดถั่ว ครั้งหนึ่งฉันใช้การฉีด contsructor ฉันไม่จำเป็นต้องทำการเยาะเย้ยโดยไม่จำเป็น
kenobiwan

25

เรื่องของรสชาติ เป็นการตัดสินใจของคุณ

แต่ฉันสามารถอธิบายได้ว่าทำไมฉันไม่เคยใช้การฉีดคอนสตรัคเตอร์

  1. ฉันไม่ต้องการที่จะใช้คอนสตรัคทั้งหมดของฉัน@Service, @Repositoryและ@Controllerถั่ว ฉันหมายถึงมีถั่วประมาณ 40-50 เมล็ดขึ้นไป ทุกครั้งถ้าฉันเพิ่มฟิลด์ใหม่ฉันจะต้องขยายตัวสร้าง ไม่ฉันไม่ต้องการและฉันไม่จำเป็นต้องทำ

  2. จะเกิดอะไรขึ้นถ้า Bean (Service หรือ Controller) ของคุณต้องฉีดถั่วอื่น ๆ จำนวนมาก? ตัวสร้างที่มีพารามิเตอร์ 4+ น่าเกลียดมาก

  3. ถ้าฉันใช้ CDI คอนสตรัคเตอร์ไม่เกี่ยวกับฉัน


แก้ไข # 1 : Vojtech Ruzicka กล่าวว่า:

ชั้นเรียนมีการพึ่งพามากเกินไปและอาจละเมิดหลักการความรับผิดชอบเดียวและควรปรับโครงสร้างใหม่

ใช่. ทฤษฎีและความเป็นจริง นี่เป็นตัวอย่าง en: แมปไปยังเส้นทางเดียวDashboardController*:8080/dashboard

ของฉันDashboardControllerรวบรวมข้อมูลจำนวนมากจากบริการอื่น ๆ เพื่อแสดงในหน้าแดชบอร์ด / ภาพรวมระบบ ฉันต้องการคอนโทรลเลอร์ตัวเดียวนี้ ดังนั้นฉันต้องรักษาความปลอดภัยเพียงเส้นทางเดียวนี้ (การตรวจสอบสิทธิ์ขั้นพื้นฐานหรือตัวกรองบทบาทผู้ใช้)

แก้ไข # 2 : เนื่องจากทุกคนให้ความสำคัญกับพารามิเตอร์ 8 ตัวในตัวสร้าง ... นี่คือตัวอย่างในโลกแห่งความจริง - รหัสเดิมของลูกค้า ฉันเปลี่ยนไปแล้ว อาร์กิวเมนต์เดียวกันนี้ใช้กับฉันสำหรับพารามิเตอร์ 4+ ตัว

ทุกอย่างเกี่ยวกับการแทรกโค้ดไม่ใช่การสร้างอินสแตนซ์


34
คอนสตรัคเตอร์ที่น่าเกลียดมากที่มีการอ้างอิง 8 แบบนั้นยอดเยี่ยมจริงๆเนื่องจากเป็นธงสีแดงที่มีบางอย่างผิดปกติคลาสมีการอ้างอิงมากเกินไปและอาจละเมิดหลักการความรับผิดชอบเดียวและควรได้รับการปรับโครงสร้างใหม่ มันเป็นสิ่งที่ดี
Vojtech Ruzicka

6
@VojtechRuzicka มันไม่ดีอย่างแน่นอน แต่บางครั้งคุณก็ไม่สามารถหลีกเลี่ยงได้
dieter

4
ฉันจะบอกว่ากฎง่ายๆที่ 3 นับประสาอะไรกับ 40-50 การอ้างอิงสำหรับคลาสใด ๆ ควรเป็นสัญญาณว่าคุณต้อง refactor ไม่มีทางที่คลาสที่มีการอ้างอิง 40 รายการจะยึดติดกับหลักความรับผิดชอบเดียวหรือหลักการเปิด / ปิด
Amin J

4
@AminJ กฎดีมาก แต่ความเป็นจริงแตกต่างกัน บริษัท ที่ฉันทำงานมากว่า 20 ปีและเรามีรหัสเดิมจำนวนมาก การปรับโครงสร้างเป็นความคิดที่ดี แต่ต้องเสียเงิน ฉันไม่รู้เหมือนกันว่าทำไมถึงพูด แต่ฉันไม่ได้หมายถึงการพึ่งพา 40-50 ฉันหมายถึงถั่ว 40-50 ส่วนประกอบโมดูล ...
dieter

7
@dit สถานการณ์ของคุณชัดเจนอย่างหนึ่งที่หนี้ทางเทคนิคทำให้คุณต้องเลือกที่เหมาะสมที่สุด ด้วยคำพูดของคุณเองคุณอยู่ในสถานการณ์ที่การตัดสินใจของคุณได้รับอิทธิพลอย่างมากจากรหัสเดิมที่มีอายุมากกว่า 20 ปี เมื่อเริ่มต้นในโครงการใหม่คุณจะยังแนะนำให้ฉีดสนามมากกว่าการฉีดตัวสร้างหรือไม่? บางทีคุณควรระบุข้อแม้ในคำตอบของคุณเพื่อระบุว่าคุณจะเลือกฉีดสนามในกรณีใด
Umar Farooq Khawaja

0

อีกหนึ่งความคิดเห็น - Vojtech Ruzicka กล่าวว่า Spring ฉีดถั่วในสามวิธีดังกล่าว (คำตอบที่มีคะแนนมากที่สุด):

  1. ผ่านตัวสร้าง
  2. ผ่าน setters หรือวิธีอื่น ๆ
  3. ผ่านการสะท้อนโดยตรงลงในสนาม

คำตอบนี้ไม่ถูกต้อง - เพราะทุกชนิดของการฉีดพ่นใช้การสะท้อนกลับ! ใช้ IDE ตั้งค่าเบรกพอยต์บน setter / constructor และตรวจสอบ

นี่อาจเป็นเรื่องของรสนิยม แต่ก็อาจเป็นเรื่องของกรณีได้เช่นกัน @dieter เป็นกรณีที่ยอดเยี่ยมเมื่อการฉีดสนามดีกว่า หากคุณใช้การแทรกฟิลด์ในการทดสอบการรวมที่กำลังตั้งค่าบริบท Spring - อาร์กิวเมนต์ที่มีความสามารถในการทดสอบของคลาสก็ไม่ถูกต้องเช่นกันเว้นแต่คุณต้องการเขียนการทดสอบในการทดสอบการรวมของคุณในภายหลัง;)

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