เมื่อใดจึงไม่เหมาะสมที่จะใช้รูปแบบการฉีดพึ่งพา


124

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


1
ที่เกี่ยวข้อง (อาจไม่ซ้ำกัน) - stackoverflow.com/questions/2407540/…
Steve314

3
ฟังดูคล้ายกับรูปแบบการต่อต้านของ Golden Hammer en.wikipedia.org/wiki/Anti-pattern
Cuga

คำตอบ:


123

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

  • ครั้งแรกส่วนใหญ่โดยทั่วไปDI อนุมานว่าการมีเพศสัมพันธ์แน่นของการใช้วัตถุที่ไม่ดีเสมอ นี่คือสาระสำคัญของหลักการการผกผันของการพึ่งพา: "การพึ่งพาไม่ควรทำเมื่อมีการสรุป; เพียง แต่ในสิ่งที่เป็นนามธรรม"

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

    อย่างไรก็ตามการออกแบบไม่สามารถปิดการเปลี่ยนแปลงได้ทุกประเภท หากการออกแบบของอินเทอร์เฟซ IWriter เปลี่ยนแปลงตัวเองเพื่อเพิ่มพารามิเตอร์ในการเขียน () วัตถุรหัสพิเศษ (อินเทอร์เฟซ IWriter) จะต้องเปลี่ยนตอนนี้บนวัตถุ / วิธีการใช้งานและการใช้งาน (s) หากการเปลี่ยนแปลงในอินเทอร์เฟซที่เกิดขึ้นจริงมีแนวโน้มมากกว่าการเปลี่ยนแปลงในการใช้งานอินเทอร์เฟซดังกล่าวการมีเพศสัมพันธ์แบบหลวม ๆ

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

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

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

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

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

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

    ทั้งหมดนี้อีกครั้งเพื่อแก้ปัญหาในสถานที่ที่อาจไม่มี


1
SRP = หลักการความรับผิดชอบเดี่ยวสำหรับทุกคนที่สงสัย
Theodore Murdock

1
ใช่แล้ว LSP เป็นหลักการทดแทน Liskov เมื่อได้รับการอ้างอิงจาก A การพึ่งพาควรจะสามารถพบได้โดย B ซึ่งมาจาก A โดยไม่มีการเปลี่ยนแปลงอื่นใด
KeithS

การฉีดพึ่งพาช่วยโดยเฉพาะอย่างยิ่งที่คุณจำเป็นต้องได้รับคลาส A จากคลาส B จากคลาส D ฯลฯ ในกรณีนั้น (และเฉพาะกรณีนั้น) กรอบ DI อาจสร้างการชุมนุมน้อยลงกว่าการฉีดของคนยากจน ยังฉันไม่เคยมีคอขวดที่เกิดจากการ DI .. คิดค่าใช้จ่ายในการบำรุงรักษาไม่เคยที่จะมีค่าใช้จ่ายซีพียูเพราะรหัสจะสามารถเพิ่มประสิทธิภาพ แต่การทำเช่นนั้นโดยไม่มีเหตุผลมีค่าใช้จ่าย
GameDeveloper

ข้อสันนิษฐานอื่นจะเป็น "ถ้าการพึ่งพาอาศัยกันมันเปลี่ยนไปทุกหนทุกแห่ง" ไม่อย่างนั้นคุณต้องดูคลาสที่ใช้ไปหมดทั้งหมด
Richard Tingle

3
คำตอบนี้ล้มเหลวในการจัดการกับผลกระทบที่สามารถทดสอบได้ของการฉีดความสัมพันธ์กับการเข้ารหัสแบบฮาร์ดโค้ด ดูหลักการพึ่งพาอย่างชัดเจน ( deviq.com/explicit-dependencies-principle )
ssmith

30

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

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

ดังนั้นการฉีดพึ่งพา (โดยไม่มีกรอบ) เป็นอันตรายอย่างเท่าเทียมกันเกี่ยวกับมันเป็นประโยชน์

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

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

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

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

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

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

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

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


6
เพียงแค่แสดงความคิดเห็นอย่างรวดเร็วเกี่ยวกับการอ้างอิงล้อเลียนในการทดสอบและ "คุณต้องรู้เกี่ยวกับการพึ่งพา 'การพึ่งพา' การพึ่งพาของคุณ" ... นั่นไม่จริงเลย หนึ่งไม่จำเป็นต้องรู้หรือสนใจเกี่ยวกับการพึ่งพาการใช้งานที่เป็นรูปธรรมบางอย่างหากมีการฉีดจำลอง เราต้องการเพียงแค่รู้เกี่ยวกับการพึ่งพาโดยตรงของชั้นเรียนภายใต้การทดสอบซึ่งเป็นที่พอใจโดยการเยาะเย้ย
Eric King เมื่อ

6
คุณเข้าใจผิดฉันไม่ได้พูดถึงเมื่อคุณเยาะเย้ยฉันกำลังพูดถึงรหัสจริง พิจารณาคลาส A ที่มีการพึ่งพา B ซึ่งจะมีการพึ่งพา C ซึ่งจะมีการพึ่งพา D โดยไม่มี DI, โครงสร้าง B, B สร้าง C, C สร้าง C กับการฉีดก่อสร้างเพื่อสร้าง A คุณต้องสร้าง B เพื่อสร้าง B คุณต้องสร้าง C ก่อนและสร้าง C ก่อนอื่นคุณต้องสร้าง D ดังนั้นคลาส A ต้องทราบเกี่ยวกับ D การพึ่งพาการพึ่งพาของการพึ่งพาเพื่อสร้าง B สิ่งนี้นำไปสู่การมีเพศสัมพันธ์มากเกินไป
Theodore Murdock

1
จะไม่มีค่าใช้จ่ายมากนักในการใช้ตัวสร้างพิเศษสำหรับการทดสอบเท่านั้นที่อนุญาตให้มีการพึ่งพาการฉีด ฉันจะพยายามทบทวนสิ่งที่ฉันพูด
Theodore Murdock

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

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

11
  1. ถ้าคุณกำลังสร้างเอนทิตีฐานข้อมูลคุณควรมีคลาสโรงงานบางส่วนที่คุณจะแทรกลงในคอนโทรลเลอร์ของคุณแทน

  2. หากคุณต้องการสร้างวัตถุดั้งเดิมเช่น ints หรือ longs นอกจากนี้คุณควรสร้าง "ด้วยมือ" ส่วนใหญ่ของวัตถุไลบรารีมาตรฐานเช่นวันที่ guids ฯลฯ

  3. หากคุณต้องการฉีดสตริงการกำหนดค่าอาจเป็นความคิดที่ดีกว่าในการแทรกออบเจ็กต์การกำหนดค่าบางอย่าง (โดยทั่วไปขอแนะนำให้ห่อประเภทที่เรียบง่ายเป็นวัตถุที่มีความหมาย: int temperatureInCelsiusDegrees -> อุณหภูมิ CelciusDeegree)

และอย่าใช้ตัวระบุตำแหน่งบริการเป็นทางเลือกในการเพิ่มการพึ่งพามันเป็นแบบป้องกันข้อมูลเพิ่มเติม: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx


ประเด็นทั้งหมดเกี่ยวกับการใช้รูปแบบการฉีดที่แตกต่างกันมากกว่าที่จะไม่ใช้มันโดยสิ้นเชิง +1 สำหรับลิงก์
Jan Hudec

3
ตัวระบุบริการไม่ใช่รูปแบบการต่อต้านและคนที่บล็อกคุณเชื่อมโยงไปนั้นไม่ใช่ผู้เชี่ยวชาญในรูปแบบ การที่เขาได้รับชื่อเสียงจาก StackExchange นั้นเป็นความเสียหายต่อการออกแบบซอฟต์แวร์ Martin Fowler (ผู้เขียนรูปแบบหลักของสถาปัตยกรรมแอปพลิเคชันองค์กร) มีมุมมองที่สมเหตุสมผลมากขึ้นในเรื่องนี้: martinfowler.com/articles/inject.html
Colin

1
@Colin ในทางตรงกันข้าม, บริการสเป็นส่วนใหญ่แน่นอนต่อต้านรูปแบบและนี่สั้นคือเหตุผลว่าทำไม
Kyralessa

@Kyralessa - คุณรู้ว่า DI Frameworks ภายในใช้รูปแบบตัวระบุบริการเพื่อค้นหาบริการใช่ไหม มีสถานการณ์ที่เหมาะสมและทั้งสองไม่มีรูปแบบการต่อต้านแม้ StackExchange จะเกลียดบางคนต้องการถ่ายโอนข้อมูล
โคลิน

แน่นอนว่ารถยนต์ของฉันใช้ลูกสูบและหัวเทียนและสิ่งอื่น ๆ ที่ไม่ใช่อินเทอร์เฟซที่ดีสำหรับมนุษย์ธรรมดาที่ต้องการขับรถ
Kyralessa

8

เมื่อคุณไม่ได้รับสิ่งใดโดยทำให้โครงการของคุณบำรุงรักษาและทดสอบได้

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

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

สรุปแล้วก็มีเหตุผลที่จะใช้วิธีการหรือรูปแบบใด ๆ ในทางปฏิบัติเนื่องจากไม่มีสิ่งใดที่สามารถนำไปใช้ได้ 100% ของเวลา

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


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

@RichardTingle ฉันจะบอกว่าคุณควรกำหนด wrapper การบันทึกของคุณเป็นอินเทอร์เฟซและจากนั้นคุณเขียนการใช้งานเบาของอินเทอร์เฟซนั้นสำหรับแต่ละบริการการบันทึกภายนอกแทนการใช้คลาสการบันทึกเดียวที่ล้อมบทคัดย่อ มันเป็นแนวคิดเดียวกับที่คุณเสนอ แต่แยกออกเป็นระดับต่าง ๆ
Ed James

1

ในขณะที่คำตอบอื่น ๆ มุ่งเน้นด้านเทคนิคฉันต้องการเพิ่มมิติการปฏิบัติ

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

  1. ควรมีเหตุผลที่จะแนะนำ

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

  2. ทีมจะต้องได้รับการฝึกฝนและขึ้นบนเรือ

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

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

  3. คุณต้องทดสอบ

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


1

นี่ไม่ใช่คำตอบที่สมบูรณ์ แต่เป็นอีกประเด็นหนึ่ง

เมื่อคุณมีแอปพลิเคชั่นที่เริ่มต้นครั้งเดียวการรันเป็นเวลานาน (เช่นเว็บแอป) DI อาจดี

เมื่อคุณมีแอปพลิเคชั่นที่เริ่มทำงานหลายครั้งและทำงานในเวลาที่สั้นลง (เช่นแอพมือถือ) คุณอาจไม่ต้องการคอนเทนเนอร์


ฉันไม่เห็นว่าแอปพลิเคชันเกี่ยวกับชีวิตของ DI
Michael Freidgeim

@MichaelFreidgeim ใช้เวลาในการเริ่มต้นบริบทโดยปกติคอนเทนเนอร์ DI จะค่อนข้างหนักเช่น Spring ทำแอป Hello World ด้วยคลาสเดียวและสร้างแอพด้วย Spring แล้วเริ่มทั้ง 10 ครั้งแล้วคุณจะเห็นว่าฉันหมายถึงอะไร
Koray Tugay

ดูเหมือนว่ามีปัญหากับแต่ละภาชนะ DI ใน. Net โลกฉันไม่เคยได้ยินเกี่ยวกับเวลาเริ่มต้นเป็นปัญหาสำคัญสำหรับภาชนะ DI
Michael Freidgeim

1

ลองใช้หลักการ OOP ขั้นพื้นฐาน: ใช้การสืบทอดเพื่อแยกการทำงานทั่วไป, แค็ปซูล (ซ่อน) สิ่งต่าง ๆ ที่ควรได้รับการปกป้องจากโลกภายนอกโดยใช้สมาชิก / ประเภทส่วนตัว / ภายใน / ที่มีการป้องกัน ใช้ทดสอบกรอบที่มีประสิทธิภาพใด ๆ ที่จะฉีดรหัสสำหรับการทดสอบเท่านั้นตัวอย่างเช่นhttps://www.typemock.com/หรือhttps://www.telerik.com/products/mocking.aspx

จากนั้นลองเขียนใหม่ด้วย DI และเปรียบเทียบโค้ดสิ่งที่คุณมักจะเห็นด้วย DI:

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

ฉันจะพูดจากสิ่งที่ฉันเห็นเกือบตลอดเวลาคุณภาพของรหัสจะลดลงเมื่อ DI

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

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


0

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

เมื่อrefactoring code legacyมักจะทำให้ refactor ไปยังตัวระบุบริการได้ง่ายกว่าการฉีด Dependency สิ่งที่คุณทำคือการแทนที่อินสแตนซ์ด้วยการค้นหาบริการแล้วปลอมบริการในการทดสอบหน่วยของคุณ

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

TLDR : ถ้าคลาสของคุณมีการพึ่งพาแบบคงที่หรือคุณกำลังสร้างรหัสดั้งเดิมผู้ให้บริการจะดีกว่า DI


12
บริการสซ่อนอ้างอิงในรหัสของคุณ มันไม่ใช่รูปแบบที่ดีที่จะใช้
Kyralessa

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

@Kyralessa ฉันเห็นด้วยตัวระบุตำแหน่งบริการมีข้อเสียมากมายและ DI นั้นเป็นที่นิยมกว่าเสมอ อย่างไรก็ตามฉันเชื่อว่ามีข้อยกเว้นสองสามข้อเช่นเดียวกับหลักการเขียนโปรแกรมทั้งหมด
Garrett Hall

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

ในการอ้างอิง Martin Fowler "ตัวเลือกระหว่าง (DI และ Service Locator) มีความสำคัญน้อยกว่าหลักการแยกการกำหนดค่าจากการใช้" แนวคิดที่ว่า DI นั้นดีกว่าคือ hogwash หากมีการใช้บริการทั่วโลกจะยุ่งยากและเปราะมากกว่าการใช้ DI นอกจากนี้ DI ไม่ดีสำหรับสถาปัตยกรรมปลั๊กอินที่ไม่รู้จักจนถึงการใช้งาน คิดว่าวิธีที่น่าอึดอัดใจก็คือการส่งผ่านข้อโต้แย้งเพิ่มเติมเพื่อ Debug.WriteLine () และชอบ!
โคลิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.