การฉีดพึ่งพามากเกินไปมากแค่ไหน?


38

ฉันทำงานในโครงการที่ใช้ (Spring) Dependency Injection สำหรับทุกสิ่งที่เป็นที่พึ่งของชั้นเรียน เราอยู่ในจุดที่ไฟล์การกำหนดค่าของ Spring โตขึ้นประมาณ 4,000 บรรทัด เมื่อไม่นานมานี้ฉันได้ดูหนึ่งในการพูดคุยของลุงบ็อบบน YouTube (น่าเสียดายที่ฉันไม่สามารถหาลิงก์ได้) ซึ่งเขาแนะนำให้ฉีดการพึ่งพาจากศูนย์กลางเพียงไม่กี่คู่ (เช่นโรงงานฐานข้อมูล ... ) ลงในองค์ประกอบหลักจากนั้น แจกจ่าย

ข้อดีของวิธีนี้คือการแยกส่วนของ DI Framework จากแอปพลิเคชั่นส่วนใหญ่และทำให้ Spring config Cleaner เป็นโรงงานจะมีสิ่งที่อยู่ใน config ก่อนหน้านี้ ในทางตรงกันข้ามสิ่งนี้จะส่งผลให้เกิดการแพร่กระจายตรรกะการสร้างในคลาสโรงงานจำนวนมากและการทดสอบอาจยากขึ้น

ดังนั้นคำถามของฉันคืออะไรคือข้อดีหรือข้อเสียอื่น ๆ ที่คุณเห็นในแนวทางหนึ่งหรืออีกวิธีหนึ่ง มีวิธีปฏิบัติที่ดีที่สุดหรือไม่? ขอบคุณมากสำหรับคำตอบของคุณ!


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

1
@Maybe_Factor TL; DR แยกโครงการออกเป็นส่วนประกอบที่เล็กลง
Walfrat

คำตอบ:


44

และเช่นเคยมันขึ้นอยู่กับ™ คำตอบขึ้นอยู่กับปัญหาที่คนพยายามแก้ไข ในคำตอบนี้ฉันจะพยายามพูดถึงแรงจูงใจทั่วไปบางประการ:

ชอบฐานรหัสขนาดเล็ก

หากคุณมีรหัสการกำหนดค่าสปริง 4,000 บรรทัดฉันคิดว่าฐานรหัสนั้นมีคลาสนับพัน

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

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

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

โปรดปราน Pure DI

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

ฉันไม่มีประสบการณ์จริงกับ Spring แต่ฉันสมมติว่าไฟล์การกำหนดค่าไฟล์ XML นั้นมีนัย

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

เมื่อเทียบกับปัญหาที่มันอ้างไปยังที่อยู่แฟ้มการกำหนดค่าฉีดพึ่งพาครอบครองสถานที่ที่ไม่ถูกต้องเกี่ยวกับนาฬิกาซับซ้อนการกำหนดค่า

กรณีสำหรับการฉีดขึ้นอยู่กับเม็ดหยาบ

ฉันสามารถสร้างกรณีสำหรับการฉีดขึ้นอยู่กับเนื้อหยาบ ฉันยังสามารถสร้างกรณีสำหรับการฉีดพึ่งพาอย่างละเอียด (ดูหัวข้อถัดไป)

หากคุณเพียงแค่แทรกการพึ่งพา 'ศูนย์กลาง' เพียงไม่กี่คลาสส่วนใหญ่อาจมีลักษณะเช่นนี้:

public class Foo
{
    private readonly Bar bar;

    public Foo()
    {
        this.bar = new Bar();
    }

    // Members go here...
}

นี้ยังคงเหมาะกับการออกแบบรูปแบบขององค์ประกอบความโปรดปรานวัตถุมรดกชั้นเพราะประกอบด้วยFoo จากมุมมองของการบำรุงรักษานี้อาจยังคงได้รับการพิจารณาการบำรุงรักษาเพราะถ้าคุณจำเป็นต้องเปลี่ยนองค์ประกอบคุณก็แก้ไขซอร์สโค้ดBarFoo

นี่คือการบำรุงรักษาน้อยกว่าการฉีดพึ่งพา ในความเป็นจริงฉันจะบอกว่ามันง่ายกว่าที่จะแก้ไขคลาสที่ใช้Barโดยตรงแทนที่จะต้องทำตามวิธีการทางอ้อมด้วยการฉีดพึ่งพา

ในหนังสือเล่มแรกของฉันเรื่องการพึ่งพาการฉีดฉันสร้างความแตกต่างระหว่างการพึ่งพาที่ผันผวนและมั่นคง

การพึ่งพาแบบระเหยนั้นเป็นการพึ่งพาที่คุณควรพิจารณาฉีด พวกเขารวมถึง

  • การอ้างอิงที่ต้องสามารถกำหนดค่าใหม่ได้หลังจากการคอมไพล์
  • การพัฒนาที่พึ่งพาในแบบคู่ขนานโดยทีมอื่น
  • การพึ่งพากับพฤติกรรมที่ไม่ได้กำหนดไว้หรือพฤติกรรมที่มีผลข้างเคียง

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

จากมุมมองการทดสอบทำให้การทดสอบหน่วยยากขึ้น คุณสามารถทดสอบหน่วยไม่ได้เป็นอิสระจากFoo Barดังที่JB Rainsberger อธิบายว่าการทดสอบการรวมตัวต้องทนทุกข์ทรมานจากการระเบิดที่ซับซ้อน คุณจะต้องเขียนกรณีทดสอบนับหมื่นถ้าคุณต้องการครอบคลุมทุกเส้นทางผ่านการรวมคลาส 4-5 คลาส

ข้อโต้แย้งโต้แย้งกันคือบ่อยครั้งที่งานของคุณไม่ได้ตั้งโปรแกรมคลาส งานของคุณคือการพัฒนาระบบที่แก้ปัญหาเฉพาะบางอย่าง นี่คือแรงจูงใจที่อยู่เบื้องหลังการพัฒนาพฤติกรรมขับเคลื่อน (BDD)

มุมมองเกี่ยวกับเรื่องนี้ก็คือการที่นำเสนอโดย DHH ที่อ้างว่านำไปสู่ความเสียหาย TDD ออกแบบการทดสอบที่เกิดขึ้น นอกจากนี้เขายังสนับสนุนการทดสอบการรวมที่หยาบ

หากคุณใช้มุมมองนี้ในการพัฒนาซอฟต์แวร์การฉีดพึ่งพาอย่างหยาบทำให้รู้สึก

กรณีสำหรับการฉีดพึ่งพาอย่างละเอียด

ในทางกลับกันการฉีดขึ้นรูปแบบละเอียดสามารถอธิบายได้ว่าเป็นการฉีดทุกสิ่ง!

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

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

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

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

ชอบเขียนโปรแกรมฟังก์ชั่น

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

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

ชอบเขียนโปรแกรมฟังก์ชั่นแบบคงที่

ณ จุดนี้ฉันได้ให้ความสำคัญกับการเขียนโปรแกรมเชิงวัตถุ (OOP) แม้ว่าปัญหาหลายอย่างของ OOP นั้นมีการเชื่อมโยงกับภาษากระแสหลักเช่น Java และ C # มากกว่าแนวคิด

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

นอกจากนี้ฉีดพึ่งพาไม่ได้ทำงาน โปรแกรมการทำงานที่แท้จริงจะต้องปฏิเสธความคิดทั้งหมดของการอ้างอิง ผลลัพธ์ที่ได้คือรหัสที่ง่ายขึ้น

สรุป

ถ้าถูกบังคับให้ทำงานกับ C # ฉันชอบการฉีดแบบพึ่งพาอย่างละเอียดเพราะมันช่วยให้ฉันครอบคลุมฐานรหัสทั้งหมดด้วยจำนวนกรณีทดสอบที่จัดการได้

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


1
ฉันชอบคำตอบนี้มากและโดยเฉพาะอย่างยิ่งข้อความนี้ที่ใช้ในบริบทของ Spring: "การกำหนดค่าการพึ่งพาโดยใช้ XML นั้นเลวร้ายที่สุดของทั้งสองโลกขั้นแรกคุณสูญเสียความปลอดภัยประเภทเวลาคอมไพล์ แต่คุณไม่ได้อะไรเลย ไฟล์อาจใหญ่พอ ๆ กับรหัสที่พยายามแทนที่ "
Thomas Carlisle

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

1

คลาสการตั้งค่า DI ขนาดใหญ่เป็นปัญหา แต่คิดว่ามันแทนรหัส

คุณต้องยกตัวอย่างบริการทั้งหมดและสิ่งที่ไม่ รหัสที่จะทำอาจเป็นได้ทั้งในapp.main()จุดเริ่มต้นและฉีดด้วยตนเองหรือคู่อย่างแน่นหนาเช่นthis.myService = new MyService();ในชั้นเรียน

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

main()
{
   var c = new diContainer();
   var service1 = diSetupClass.SetupService1(c);
   var service2 = diSetupClass.SetupService2(c, service1); //if service1 is required by service2
   //etc

   //main logic
}

คุณไม่จำเป็นต้องอ้างอิง service1 หรือ 2 ยกเว้นส่งผ่านไปยังวิธีการตั้งค่า ci อื่น ๆ

นี่จะดีกว่าการพยายามดึงมันออกมาจากตู้คอนเทนเนอร์เพราะมันจะบังคับให้ลำดับของการเรียกการตั้งค่า


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

@Ewan ciตัวแปรนั้นคืออะไร
superjos

คลาสการตั้งค่า ci ของคุณด้วยวิธีการคงที่
Ewan

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