รูปแบบการพึ่งพาการฉีดและการบริการค้นหาความแตกต่างคืออะไร


304

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

พึ่งพาการฉีด (DI) ดูเหมือนว่าจะใช้ตัวสร้างหรือ setter เพื่อ "ฉีด" มันขึ้นอยู่กับการพึ่งพา

ตัวอย่างการใช้ Constructor Injection:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

ผู้ให้บริการดูเหมือนว่าจะใช้ "ภาชนะ" ซึ่งสายขึ้นอยู่กับการพึ่งพาและให้แถบมันเป็น foo

ตัวอย่างการใช้ตัวระบุบริการ:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

เนื่องจากการพึ่งพาของเราเป็นเพียงวัตถุเองการพึ่งพาเหล่านี้มีการพึ่งพาซึ่งมีการพึ่งพามากขึ้นและอื่น ๆ เป็นต้น ดังนั้น Inversion of Control Container (หรือ DI Container) จึงเกิดขึ้น ตัวอย่าง: Castle Windsor, Ninject, แผนผังโครงสร้าง, Spring, ฯลฯ )

แต่คอนเทนเนอร์ IOC / DI ดูเหมือนว่าตัวระบุบริการ การเรียกชื่อคอนเทนเนอร์ DI เป็นชื่อที่ไม่ดีหรือไม่? IOC / DI Container เป็นเพียงแค่Locator ประเภทอื่นหรือไม่ ความแตกต่างกันนิดหน่อยในความจริงที่ว่าเราใช้ DI Containers เป็นส่วนใหญ่เมื่อเรามีการพึ่งพาจำนวนมาก?


13
การผกผันของการควบคุมหมายความว่า "วัตถุไม่ควรรู้วิธีการสร้างการอ้างอิงของมัน"?!? อันนั้นใหม่สำหรับฉัน ไม่จริงๆนั่นไม่ใช่ความหมายของ "การกลับกันของการควบคุม" ดูmartinfowler.com/bliki/InversionOfControl.htmlบทความนั้นยังให้การอ้างอิงสำหรับนิรุกติศาสตร์ของคำศัพท์ย้อนหลังไปถึงยุค 80
Rogério

1
ตอบได้ที่นี่: infoq.com/articles/Succeeding-Dependency-Inject
Mark Seemann

1
Mark Seemann โต้แย้งผู้ให้บริการว่าเป็นรูปแบบต่อต้าน ( blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern ) นอกจากนี้ฉันพบแผนภาพ (พบได้ที่นี่ stackoverflow.com/a/9503612/1977871 ) มีประโยชน์ในการเข้าใจสถานการณ์ DI และ SL หวังว่านี่จะช่วยได้
VivekDev

คำตอบ:


181

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


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

6
ไม่ ServiceLocator เป็นผู้รับผิดชอบในการสร้างอินสแตนซ์ที่ถูกต้องสำหรับการพึ่งพาที่กำหนด (ปลั๊กอิน) ในกรณีของ DI นั้น DI "คอนเทนเนอร์" เป็นผู้รับผิดชอบ
Rogério

5
@Rogerio ใช่ แต่คลาสยังต้องรู้เกี่ยวกับ Service Locator ... นั่นเป็นสองการพึ่งพา บ่อยครั้งกว่าที่ฉันไม่ได้เห็นตัวแทนผู้ให้บริการไปยังคอนเทนเนอร์ DI สำหรับการค้นหาโดยเฉพาะอย่างยิ่งสำหรับวัตถุชั่วคราวที่ต้องการการสนับสนุนการบริการ
Adam Gent

2
@ อดัมฉันไม่ได้บอกว่าผู้ให้บริการจะมอบหมายให้ภาชนะ DI เหล่านี้มีสองรูปแบบพิเศษร่วมกันตามที่อธิบายไว้ในบทความ "อย่างเป็นทางการ" สำหรับฉันแล้วตัวระบุตำแหน่งบริการมีข้อได้เปรียบมากกว่า DI ในทางปฏิบัติ: การใช้คอนเทนเนอร์ DI จะเป็นการละเมิด (ซึ่งฉันได้เห็นบ่อยๆ) ในขณะที่การใช้ตัวระบุตำแหน่งบริการไม่ได้
Rogério

3
"ผลลัพธ์ที่สำคัญอย่างหนึ่งของสิ่งนี้คือตัวอย่าง DI นั้นง่ายกว่ามากในการทดสอบหน่วย - เพราะคุณสามารถผ่านการจำลองการใช้งานวัตถุที่อ้างถึงได้" ไม่จริง. ในการทดสอบหน่วยของคุณการเรียกใช้ฟังก์ชันการลงทะเบียนในคอนเทนเนอร์ตัวระบุตำแหน่งบริการสามารถใช้เพื่อเพิ่ม mocks ในรีจิสทรีได้อย่างง่ายดาย
Drumbeg

93

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

การเปรียบเทียบที่ดี: http://martinfowler.com/articles/inject.html

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


17
แต่คุณจะจัดการกับกรณีที่คุณต้องสร้างวัตถุระหว่างรันไทม์ได้อย่างไร หากคุณสร้างด้วยตนเองด้วย "ใหม่" คุณจะไม่สามารถใช้ DI ได้ ถ้าคุณเรียกใช้ DI Framework สำหรับความช่วยเหลือแสดงว่าคุณกำลังทำลายรูปแบบ ตัวเลือกอะไรที่เหลืออยู่?
บอริส

9
@ บอริสฉันมีปัญหาเดียวกันและตัดสินใจที่จะฉีดโรงงานเฉพาะระดับ ไม่สวย แต่ทำงานเสร็จแล้ว ชอบที่จะเห็นทางออกที่ดีกว่า
Charlie Rudenstål

ลิงก์โดยตรงไปยังการเปรียบเทียบ: martinfowler.com/articles/…
Teoman shipahi

2
@ บอริสถ้าฉันต้องการสร้างวัตถุใหม่ในทันทีฉันจะฉีด Abstract Factory สำหรับวัตถุดังกล่าว ซึ่งจะคล้ายกับการฉีด service locator ในอินสแตนซ์นี้ แต่ให้คอนกรีต, สม่ำเสมอ, รวบรวมเวลา, อินเตอร์เฟสสำหรับการสร้างวัตถุที่เกี่ยวข้องและทำให้การอ้างอิงชัดเจน
LivePastTheEnd

51

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

ยิ่งไปกว่านั้น locators ที่ให้บริการจะทำลาย encapsulation เนื่องจากเป็นจุดเข้าถึงทั่วโลกสำหรับการพึ่งพาของวัตถุอื่น ๆ ด้วยตัวระบุตำแหน่งบริการเช่นเดียวกับซิงเกิลใด ๆ :

มันจะยากที่จะระบุเงื่อนไขก่อนและหลังการโพสต์สำหรับอินเทอร์เฟซของวัตถุไคลเอนต์เนื่องจากการทำงานของการใช้งานสามารถ meddled ด้วยจากภายนอก

ด้วยการฉีดพึ่งพาเมื่อมีการระบุการอ้างอิงของวัตถุพวกเขาจะอยู่ภายใต้การควบคุมของวัตถุเอง


3
ฉันชอบ "Singletons Considered Stupid", steve.yegge.googlepages.com/singleton-considered-stupid
Charles Graham

2
ฉันรัก Ol 'Steve Yegge และชื่อของบทความนั้นดีมาก แต่ฉันคิดว่าบทความที่ฉันอ้างและMiško Hevery ของ "Singletons เป็นผู้โกหกทางพยาธิวิทยา" ( misko.hevery.com/2008/08/17/singletons-are-pathological- โกหก ) ทำคดีให้ดีขึ้นเมื่อเทียบกับปีศาจร้ายของผู้ให้บริการ
34490 Jeff Sternal

คำตอบนี้ถูกต้องมากที่สุดเพราะให้คำจำกัดความตัวระบุบริการ: "คลาสที่ซ่อนการอ้างอิงของมัน" โปรดทราบว่าการสร้างการพึ่งพาภายในแม้ว่าบ่อยครั้งจะไม่ใช่เรื่องดี แต่ก็ไม่ได้ทำให้ class เป็น service locator นอกจากนี้การใช้การอ้างอิงบนคอนเทนเนอร์เป็นปัญหา แต่ไม่ใช่ปัญหา "" ที่ชัดเจนที่สุดคือการระบุที่ตั้งบริการ
Sam

1
With dependency injection (at least constructor injection) the dependencies are explicit.. กรุณาอธิบาย.
FindOutIslamNow

ข้างต้นฉันไม่สามารถเห็นว่า SL ทำให้การอ้างอิงน้อยกว่า DI ...
MichałPowłoka

38

มาร์ตินฟาวเลอร์ฯ :

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

กล่าวโดยย่อ: Service Locator และ Dependency Injection เป็นเพียงการดำเนินการตามหลักการผกผันของการพึ่งพา

หลักการสำคัญคือ“ ขึ้นอยู่กับ Abstractions ไม่ใช่ตาม Concretions” สิ่งนี้จะทำให้การออกแบบซอฟต์แวร์ของคุณ“ เชื่อมโยงอย่างอิสระ”,“ ขยายได้”,“ ยืดหยุ่นได้”

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

คุณสามารถตรวจสอบโพสต์นี้: การพึ่งพาการผกผัน: บริการค้นหาหรือการฉีดพึ่งพา

อีกทั้งคลาสสิก: Inversion of Control Containers และรูปแบบการฉีดพึ่งพาโดย Martin Fowler

การออกแบบคลาสที่สามารถใช้ซ้ำได้ โดย Ralph E. Johnson และ Brian Foote

อย่างไรก็ตามสิ่งที่เปิดตาของฉันคือ: ASP.NET MVC: แก้ไขหรือฉีด? นั่นคือปัญหา ... โดย Dino Esposito


บทสรุปที่ยอดเยี่ยม: "เครื่องมือค้นหาผู้ให้บริการและการฉีดการพึ่งพาเป็นเพียงการดำเนินการตามหลักการการผกผันของการพึ่งพา"
Hans

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

1
ServiceLocator และ DI ไม่มีส่วนเกี่ยวข้องกับ "หลักการพึ่งพาการผกผัน" (DIP) กรมทรัพย์สินทางปัญญาเป็นวิธีที่จะทำให้องค์ประกอบระดับสูงสามารถนำมาใช้ซ้ำได้มากขึ้นโดยการแทนที่การพึ่งพาเวลาคอมไพล์ในองค์ประกอบระดับต่ำด้วยการพึ่งพาประเภทนามธรรมที่กำหนดไว้พร้อมกับส่วนประกอบระดับสูงซึ่งได้รับการดำเนินการโดยต่ำ องค์ประกอบระดับ ด้วยวิธีนี้การรวบรวมเวลารวบรวมจะกลับด้านเนื่องจากตอนนี้เป็นระดับต่ำซึ่งขึ้นอยู่กับระดับสูง นอกจากนี้โปรดทราบว่าบทความของ Martin Fowler อธิบายว่า DI และ IoC ไม่ใช่สิ่งเดียวกัน
Rogério

23

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

DI และ SL สามารถทำงานร่วมกันได้ มันมีประโยชน์ที่จะมีที่ตั้งส่วนกลางสำหรับการอ้างอิงทั่วไป (เช่นการตั้งค่าตัวบันทึก ฯลฯ ) กำหนดคลาสโดยใช้ deps คุณสามารถสร้าง constructor "ของจริง" ที่ได้รับ deps และคอนสตรัคเตอร์เริ่มต้น (ไม่มีพารามิเตอร์) ที่ดึงจาก SL และส่งต่อไปยัง constructor "ของจริง"

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


! ที่น่าสนใจ ฉันใช้ทั้ง DI และ SL แต่ไม่ใช่กับสอง constructors สามหรือสี่ที่น่าเบื่อที่สุดมักจะต้องได้รับการพึ่งพา (การตั้งค่า ฯลฯ ... ) ได้รับจาก SL ในทันที ทุกอย่างอื่นได้รับการฉีดคอนสตรัค มันค่อนข้างน่าเกลียด แต่ในทางปฏิบัติ
maaartinus

10

ทั้งสองเป็นเทคนิคการใช้งานของ IoC นอกจากนี้ยังมีรูปแบบอื่น ๆ ที่ดำเนินการควบคุมการผกผัน:

  • รูปแบบโรงงาน
  • บริการระบุตำแหน่ง
  • คอนเทนเนอร์ DI (IoC)
  • การพึ่งพาการฉีด (การสร้างคอนสตรัคเตอร์การฉีดพารามิเตอร์ (หากไม่จำเป็น) การฉีดเซทเทอร์ของการฉีดอินเทอร์เฟซ) ...

Service locator และ DI Container ดูคล้ายกันมากขึ้นซึ่งทั้งคู่ใช้คอนเทนเนอร์เพื่อกำหนดการขึ้นต่อกันซึ่งแม็พสิ่งที่เป็นนามธรรมกับการใช้งานที่เป็นรูปธรรม

ข้อแตกต่างที่สำคัญคือการอ้างอิงที่ตั้งอยู่ใน Service Locator, รหัสลูกค้าขอการพึ่งพาใน DI Container เราใช้คอนเทนเนอร์เพื่อสร้างวัตถุทั้งหมดและมันจะฉีดขึ้นอยู่กับพารามิเตอร์คอนสตรัค (หรือคุณสมบัติ)


7

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

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

ในกรณีของฉันฉันใช้ServiceLocatorซึ่งเป็นส่วนประกอบเฟรมเวิร์ก และใช้Unityสำหรับ IoC container ถ้าในอนาคตฉันต้องเปลี่ยนคอนเทนเนอร์ IoC ของฉันเป็นNinjectทั้งหมดที่ฉันต้องทำคือฉันต้องกำหนดค่า Service locator ของฉันให้ใช้ Ninject แทน Unity โยกย้ายง่าย

นี่คือบทความที่ดีอธิบายสถานการณ์นี้ http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/


ลิงก์บทความ johandekoning เสีย
JakeJ

6

ฉันคิดว่าทั้งสองทำงานร่วมกัน

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

บทบาทของตัวระบุตำแหน่งบริการคือการรวมการใช้งานของคุณเข้าด้วยกัน คุณตั้งค่าตัวระบุตำแหน่งบริการผ่านการบูตแบบรัดในตอนเริ่มต้นโปรแกรมของคุณ Bootstrapping เป็นกระบวนการเชื่อมโยงประเภทของการนำไปใช้กับบทคัดย่อ / อินเตอร์เฟสเฉพาะ สิ่งใดที่สร้างขึ้นสำหรับคุณในขณะดำเนินการ (ขึ้นอยู่กับคุณกำหนดค่าหรือ bootstrap) หากคุณไม่ได้ใช้การฉีดขึ้นต่อกันมันจะยากมากในการใช้ service locator หรือคอนเทนเนอร์ IOC


6

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

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

  • มีการอ้างอิงที่ต้องการ
  • ไม่เกี่ยวข้องกับรอบการพึ่งพาใด ๆ ที่ไม่ถูกต้องและ
  • ในกรณีของ MEF ให้มาพร้อมกับอินสแตนซ์เดียวเท่านั้น

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

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

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

คอนเทนเนอร์ IoC เมื่อใช้อย่างระมัดระวังสามารถวิเคราะห์การกำหนดค่ารันไทม์ของแอปของคุณแบบคงที่โดยไม่ต้องสร้างอินสแตนซ์ของส่วนประกอบที่เกี่ยวข้อง คอนเทนเนอร์ยอดนิยมจำนวนมากให้การเปลี่ยนแปลงนี้ Microsoft.Compositionซึ่งเป็นเวอร์ชันของ MEF ที่กำหนดเป้าหมาย. NET 4.5 เว็บและแอพสไตล์ Metro ให้CompositionAssertตัวอย่างในเอกสารวิกิ ใช้มันคุณสามารถเขียนโค้ดเช่น:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

(ดูตัวอย่างนี้ )

โดยการตรวจสอบComposition Rootsของแอปพลิเคชันของคุณในเวลาทดสอบคุณสามารถตรวจพบข้อผิดพลาดบางอย่างที่อาจส่งผ่านการทดสอบในภายหลังในกระบวนการ

หวังว่านี่เป็นส่วนเสริมที่น่าสนใจของชุดคำตอบที่ครอบคลุมในหัวข้อนี้!


5

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

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

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

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

ไม่ว่าจะเรียกว่า service locator (หรือเพียงแค่ locator) หรือเป็น IoC container (หรือเพียง container) สร้างความแตกต่างไม่ได้อย่างที่คุณคาดเดาเพราะพวกเขาอาจอ้างถึง abstraction เดียวกัน (แก้ไขให้ถูกต้องถ้าฉันผิด ) เป็นเพียงการเรียกมันว่า locator ของบริการแนะนำว่ามีการใช้รูปแบบการต่อต้านบริการของ Locator พร้อมกับรูปแบบการพึ่งพา

IMHO การตั้งชื่อมันว่า 'locator' แทนที่จะเป็น 'location' หรือ 'locating' บางครั้งอาจทำให้คนคิดว่า locator ของบริการในบทความนั้นอ้างถึง container Locator ของบริการและไม่ใช่รูปแบบของ Locator Locator (anti-) โดยเฉพาะเมื่อมีรูปแบบที่เกี่ยวข้องที่เรียกว่า Dependency Injection และไม่ใช่ Dependency Injector


4

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


4
อืมฉันไม่เห็นด้วย: ตัวระบุตำแหน่งบริการ แสดงให้เห็นอย่างชัดเจนว่ายังมีการพึ่งพาที่นั่น ... บริการค้นหา ถ้าbarคลาสนั้นมีการพึ่งพาจากนั้นก็barจะมีตัวระบุตำแหน่งบริการนั่นคือจุดรวมของการใช้ DI / IoC
GFoley83

2

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

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


1

ความคิดที่เรียบง่ายดังต่อไปนี้ทำให้ฉันเข้าใจถึงความแตกต่างที่ชัดเจนระหว่าง Service Locator และ DI Container:

  • ตัวระบุบริการถูกใช้ในผู้ใช้บริการและดึงบริการตาม ID จากที่เก็บข้อมูลบางส่วนตามคำขอของผู้บริโภคโดยตรง

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

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


0

DI container เป็นsupersetของ locator บริการ มันสามารถนำมาใช้ในการค้นหาบริการที่มีความสามารถที่เพิ่มขึ้นของการประกอบ (สายไฟ) ฉีดพึ่งพา


-2

สำหรับบันทึก

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

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

ทำไมมันเป็นความคิดที่ดีที่จะใช้ส่วนต่อประสาน? เนื่องจากเป็นการยากที่จะตรวจแก้จุดบกพร่อง

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

หากรหัสใช้การพึ่งพาอย่างหนักดังนั้นการดีบักข้อผิดพลาดได้ง่าย

//Foo Needs an IBar
public class Foo
{
  private BarService bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

หาก "บาร์" ล้มเหลวฉันควรตรวจสอบและยืนยันคลาส BarService


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