การปฏิบัติที่พึ่งพาการฉีด / IoC คอนเทนเนอร์เมื่อเขียนเฟรมเวิร์ก


18

ฉันใช้คอนเทนเนอร์ IoC (Castle.Windsor, Autofac, MEF และอื่น ๆ ) สำหรับ. Net ในหลายโครงการ ฉันพบว่าพวกเขามักจะถูกทารุณกรรมบ่อยครั้งและส่งเสริมการปฏิบัติที่ไม่ดี

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

ตัวอย่างเช่นโค้ดสองสามกลิ่นที่ฉันสังเกตเห็นและไม่มีคำแนะนำที่ดีในการ:

  1. พารามิเตอร์จำนวนมาก (> 5) สำหรับตัวสร้าง การสร้างบริการมีความซับซ้อน การพึ่งพาทั้งหมดจะถูกฉีดผ่านทางคอนสตรัคเตอร์ - แม้ว่าจะมีความจริงที่ว่าส่วนประกอบนั้นเป็นทางเลือกที่หาได้ยาก (ยกเว้นในการทดสอบ)

  2. การขาดคลาสส่วนตัวและภายใน อันนี้อาจเป็นข้อ จำกัด เฉพาะของการใช้ C # และ Silverlight แต่ฉันสนใจที่จะแก้ไขมันอย่างไร เป็นการยากที่จะบอกว่าอินเตอร์เฟสเฟรมเวิร์กคืออะไรถ้าคลาสทั้งหมดเป็นแบบสาธารณะ มันช่วยให้ฉันเข้าถึงชิ้นส่วนส่วนตัวที่ฉันไม่ควรแตะ

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

ตัวอย่างเช่น. Net framework มีวิธีการคงที่มากมาย เช่น DateTime.UtcNow หลายครั้งที่ฉันเห็นสิ่งนี้พันกันและฉีดเป็นพารามิเตอร์การก่อสร้าง

ขึ้นอยู่กับการใช้งานที่เป็นรูปธรรมทำให้รหัสของฉันยากต่อการทดสอบ การฉีดการพึ่งพาทำให้โค้ดของฉันใช้งานยากโดยเฉพาะถ้าคลาสมีพารามิเตอร์มากมาย

ฉันจะให้ทั้งอินเตอร์เฟซที่สามารถทดสอบได้และอินเทอร์เฟซที่ใช้งานง่ายได้อย่างไร อะไรคือการปฏิบัติที่ดีที่สุด?


1
พวกเขาสนับสนุนการปฏิบัติที่ไม่ดีอะไร? สำหรับชั้นเรียนส่วนตัวคุณไม่จำเป็นต้องมีหลายคนถ้าคุณมีการห่อหุ้มที่ดี สำหรับสิ่งหนึ่งพวกเขาทดสอบได้ยากกว่ามาก
Aaronaught

ข้อผิดพลาด 1 และ 2 เป็นวิธีปฏิบัติที่ไม่ดี ตัวสร้างขนาดใหญ่และไม่ใช้การห่อหุ้มเพื่อสร้างส่วนต่อประสานที่น้อยที่สุด? นอกจากนี้ฉันยังบอกว่าเป็นส่วนตัวและภายใน (ใช้ภายในเพื่อทดสอบ) ฉันไม่เคยใช้เฟรมเวิร์กที่บังคับให้ฉันใช้คอนเทนเนอร์ IoC เฉพาะ
Dave Hillier

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

3
การใช้คอนเทนเนอร์ IoC ที่เฉพาะเจาะจงในกรอบโดยทั่วไปไม่ใช่วิธีปฏิบัติที่ดี ใช้ฉีดพึ่งพา คือการปฏิบัติที่ดี คุณตระหนักถึงความแตกต่างหรือไม่?
Aaronaught

1
@Aaronaught (กลับมาจากความตาย) การใช้ "การฉีดการพึ่งพาเป็นวิธีปฏิบัติที่ดี" เป็นเท็จ การฉีดพึ่งพาเป็นเครื่องมือที่ควรใช้เมื่อเหมาะสมเท่านั้น การใช้การพึ่งพาการฉีดตลอดเวลาคือการปฏิบัติที่ไม่ดี
Dave Hillier

คำตอบ:


27

รูปแบบการต่อต้านการพึ่งพาการฉีดที่ถูกต้องตามกฎหมายเท่านั้นที่ฉันรับรู้คือรูปแบบของตัวค้นหาบริการซึ่งเป็นรูปแบบการต่อต้านเมื่อใช้เฟรมเวิร์ก DI

รูปแบบการต่อต้าน DI อื่น ๆ ทั้งหมดที่ฉันเคยได้ยินที่นี่หรือที่อื่น ๆ เป็นเพียงกรณีที่เฉพาะเจาะจงมากขึ้นเล็กน้อยของรูปแบบการต่อต้านการออกแบบ OO / ซอฟต์แวร์ทั่วไป ตัวอย่างเช่น

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

  • การฉีดข้อมูลซึ่งตรงข้ามกับพฤติกรรมเป็นประเภทย่อยของโพลเทอร์จิสต์ anti-pattern โดยที่ 'geist ในกรณีนี้คือคอนเทนเนอร์ หากคลาสจำเป็นต้องทราบวันที่และเวลาปัจจุบันคุณไม่ต้องฉีด a DateTimeซึ่งเป็นข้อมูล แต่คุณฉีดสิ่งที่เป็นนามธรรมเหนือนาฬิการะบบ (ฉันมักจะเรียกฉันISystemClockว่าแม้ว่าฉันคิดว่ามีทั่วไปมากกว่าหนึ่งในโครงการSystemWrappers ) สิ่งนี้ไม่เพียงถูกต้องสำหรับ DI; เป็นสิ่งสำคัญอย่างยิ่งสำหรับความสามารถในการทดสอบเพื่อให้คุณสามารถทดสอบฟังก์ชั่นที่เปลี่ยนแปลงตามเวลาโดยไม่จำเป็นต้องรอฟัง

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

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

  • ข้อผิดพลาดที่ผ่านมาผมมักจะเห็นคือ "การพึ่งพาตัวเลือก" ซึ่งพวกเขาได้ใน NerdDinner ในคำอื่น ๆ ที่มีคอนสตรัคที่รับฉีดพึ่งพา แต่ยังอีกคอนสตรัคที่ใช้การดำเนินการ "เริ่มต้น" สิ่งนี้ยังละเมิดกรมและมีแนวโน้มจะนำไปสู่การละเมิดLSPเช่นเดียวกับนักพัฒนาเมื่อเวลาผ่านไปเริ่มต้นการตั้งสมมติฐานเกี่ยวกับการใช้งานเริ่มต้นและ / หรือเริ่มต้นอินสแตนซ์ใหม่โดยใช้ตัวสร้างเริ่มต้น

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

... และต่อไป

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

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

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

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


ฉันมีคำถามสองสามข้อ สำหรับไลบรารีที่เผยแพร่บน NuGet ฉันไม่สามารถพึ่งพาระบบ IoC ได้เนื่องจากแอปพลิเคชันที่ใช้งานไลบรารีนั้นไม่ได้ใช้ตัวไลบรารีเอง ในหลายกรณีผู้ใช้ห้องสมุดไม่สนใจการพึ่งพาภายในเลย มีปัญหากับการมีตัวสร้างค่าเริ่มต้นเริ่มต้นการอ้างอิงภายในและตัวสร้างที่ยาวขึ้นสำหรับการทดสอบหน่วยและ / หรือการปรับใช้แบบกำหนดเองหรือไม่? นอกจากนี้คุณพูดเพื่อหลีกเลี่ยง "ใหม่" สิ่งนี้ใช้ได้กับสิ่งต่างๆเช่น StringBuilder, DateTime และ TimeSpan หรือไม่
Etienne Charland
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.