ควรทำการฉีดแบบพึ่งพาได้ใน ctor หรือต่อวิธี?


16

พิจารณา:

public class CtorInjectionExample
{
    public CtorInjectionExample(ISomeRepository SomeRepositoryIn, IOtherRepository OtherRepositoryIn)
    {
        this._someRepository = SomeRepositoryIn;
        this._otherRepository = OtherRepositoryIn;
    }

    public void SomeMethod()
    {
        //use this._someRepository
    }

    public void OtherMethod()
    {
        //use this._otherRepository
    }
}

ต่อต้าน:

public class MethodInjectionExample
{
    public MethodInjectionExample()
    {
    }

    public void SomeMethod(ISomeRepository SomeRepositoryIn)
    {
        //use SomeRepositoryIn
    }

    public void OtherMethod(IOtherRepository OtherRepositoryIn)
    {
        //use OtherRepositoryIn
    }
}

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

มีวิธีการฉีดที่ชัดเจนหรือไม่?

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


4
หากชั้นเรียนของคุณมีความเหนียวแน่นวิธีการต่าง ๆ มากมายจะใช้ฟิลด์เดียวกัน นี้ควรจะให้คำตอบที่คุณต้องการ ...
Oded

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

เกี่ยวข้องกับตัวอย่างที่ฉันได้รับ: en.wikipedia.org/wiki/False_dilemma
StuperUser

คำตอบ:


3

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


15

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

คอนเทนเนอร์ DI ของคุณควรจะสามารถฉีดการอ้างอิงที่เกี่ยวข้องทั้งหมดได้ดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับการเปลี่ยนลายเซ็นคอนสตรัคเตอร์ดังนั้นอาร์กิวเมนต์นั้นจึงไม่ถือ

สำหรับแนวคิดต่อวิธีเปรียบเทียบกับวิธีฉีดแบบต่อคลาสนั้นมีสองประเด็นหลักที่เกี่ยวข้องกับวิธีฉีดต่อวิธี

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

ปัญหาต่อไปคือเพื่อ "ฉีด" การพึ่งพาต่อวิธีคุณจะต้องผ่านพวกเขาในการโทรวิธี ซึ่งหมายความว่าคุณจะต้องแก้ไขการขึ้นต่อกันก่อนที่จะเรียกเมธอดดังนั้นคุณอาจจะต้องจบด้วยโค้ดจำนวนมากดังนี้:

var someDependency = ServiceLocator.Resolve<ISomeDependency>();
var something = classBeingInjected.DoStuff(someDependency);

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

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

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

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

(NB ฉันได้ทำวิธีที่คุณแนะนำมาก่อนและมันเป็นความคิดที่แย่มากที่ฉันไม่ได้ทำซ้ำนับตั้งแต่กลับกลายเป็นว่าฉันพยายามแยกชั้นเรียนที่ไม่จำเป็นต้องแยกจากกันและจบลงด้วยดี ด้วยชุดอินเทอร์เฟซที่แต่ละการเรียกใช้เมธอดต้องการการเลือกอินเทอร์เฟซอื่นใกล้เคียงคงที่มันเป็นฝันร้ายที่จะรักษาไว้)


1
"if you're doing dependency injection and you have any code calling new() on an object with dependencies, you're doing it wrong."หากคุณเป็นหน่วยทดสอบวิธีการในชั้นเรียนที่คุณต้องยกตัวอย่างหรือคุณใช้ DI เพื่อยกตัวอย่างรหัสของคุณภายใต้การทดสอบ?
StuperUser

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

1
นั่นคือรหัสที่ฉันพูดถึงเมื่อฉันพูด"any code calling the ctor will need updating"รหัสการผลิตของฉันไม่ได้เรียกพนักงาน ด้วยเหตุผลเหล่านี้.
StuperUser

2
@ SuperUser พอใช้ได้ แต่คุณยังคงต้องโทรหาวิธีที่มีการอ้างอิงที่จำเป็นซึ่งหมายความว่าคำตอบที่เหลือของฉันยังคงยืนอยู่! ตามความจริงฉันเห็นด้วยกับปัญหาของการฉีดคอนสตรัคเตอร์และบังคับให้พึ่งพาทั้งหมดจากมุมมองการทดสอบหน่วย ฉันมักจะใช้การฉีดคุณสมบัติเนื่องจากจะช่วยให้คุณฉีดเฉพาะการอ้างอิงที่คุณต้องการสำหรับการทดสอบซึ่งฉันพบว่าเป็นชุด Assert.WasNotCalled โดยนัยอีกชุดหนึ่งโดยไม่ต้องเขียนรหัสใด ๆ ! : D
Ed James

3

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

คำตอบคือ: มันขึ้นอยู่กับความต้องการ บางครั้งมันจะดีกว่าถ้าใช้แบบหนึ่งและแบบอื่น

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


3

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


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

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

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

2
@Chris จริงๆแล้วภายใต้สภาวะปกติคอนสตรัคเตอร์และการฉีดคุณสมบัติจะทำหน้าที่เหมือนกันมากโดยมีการฉีดวัตถุในระหว่างการแก้ปัญหา ครั้งเดียวที่อาจถือว่าเป็น "ไม่ถูกต้อง" คือในระหว่างการทดสอบหน่วยเมื่อคุณไม่ต้องการใช้คอนเทนเนอร์ DI เพื่อยกตัวอย่างการใช้งาน ดูความคิดเห็นของฉันเกี่ยวกับคำตอบสำหรับสิ่งที่ฉันทำเพราะเหตุใดสิ่งนี้จึงเป็นประโยชน์จริง ๆ ฉันขอขอบคุณที่ความไม่แน่นอนอาจเป็นปัญหา แต่ตามจริงแล้วคุณจะอ้างอิงคลาสที่ได้รับการแก้ไขผ่านอินเทอร์เฟซของพวกเขาเท่านั้นซึ่งจะไม่เปิดเผยการพึ่งพาในการแก้ไข
Ed James
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.