Câu hỏi Tiêm phụ thuộc là gì?


Đã có một số câu hỏi đã được đăng với các câu hỏi cụ thể về tiêm phụ thuộc, chẳng hạn như khi nào sử dụng nó và những khung công tác nào có cho nó. Tuy nhiên,

Tiêm phụ thuộc là gì và khi nào / tại sao nên hoặc không nên được sử dụng?


2635
2017-09-25 00:28


gốc


Xem thảo luận của tôi về tiêm phụ thuộc Đây. - Kevin S.
Tôi đồng ý với các ý kiến ​​về liên kết. Tôi có thể hiểu bạn có thể muốn tham khảo người khác. Nhưng ít nhất là thêm lý do tại sao bạn đang liên kết họ và những gì làm cho liên kết này tốt hơn so với các liên kết khác tôi có thể nhận được bằng cách sử dụng google - Christian Payne
@AR: Về mặt kỹ thuật, Dependency Injection là không phải một dạng đặc biệt của IoC. Thay vào đó, IoC là một kỹ thuật được sử dụng để cung cấp Dependency Injection. Các kỹ thuật khác có thể được sử dụng để cung cấp Dependency Injection (mặc dù IoC là chỉ sử dụng phổ biến), và IoC cũng được sử dụng cho nhiều vấn đề khác. - Sean Reilly
Nếu bạn không tóm tắt nó, chúng tôi không biết nó có giá trị nỗ lực để đi và đọc nó - Casebash
Về liên kết, hãy nhớ rằng chúng thường biến mất theo cách này hay cách khác. Có một số lượng lớn các liên kết chết trong các câu trả lời SO. Vì vậy, không có vấn đề làm thế nào tốt các bài viết liên kết là, nó không tốt ở tất cả nếu bạn không thể tìm thấy nó. - DOK


Các câu trả lời:


Về cơ bản, thay vì để các đối tượng của bạn tạo ra một phụ thuộc hoặc yêu cầu một đối tượng factory để tạo một đối tượng cho chúng, bạn truyền các phụ thuộc cần thiết vào đối tượng bên ngoài, và bạn làm cho nó trở thành vấn đề của người khác. Điều này "một ai đó" hoặc là một đối tượng tiếp tục lên đồ thị phụ thuộc, hoặc một injector dependency (framework) xây dựng đồ thị phụ thuộc. Một phụ thuộc như tôi đang sử dụng nó ở đây là bất kỳ đối tượng khác đối tượng hiện tại cần phải giữ một tham chiếu đến.

Một trong những ưu điểm chính của tiêm phụ thuộc là nó có thể làm cho việc thử nghiệm trở nên dễ dàng hơn nhiều. Giả sử bạn có một đối tượng mà trong constructor của nó thực hiện như sau:

public SomeClass() {
    myObject = Factory.getObject();
}

Điều này có thể rắc rối khi tất cả những gì bạn muốn làm là chạy một số kiểm tra đơn vị trên SomeClass, đặc biệt nếu myObject là một cái gì đó thực hiện truy cập mạng hoặc ổ đĩa phức tạp. Vì vậy, bây giờ bạn đang nhìn vào mocking myObject nhưng cũng bằng cách nào đó chặn các cuộc gọi nhà máy. Cứng. Thay vào đó, chuyển đối tượng vào như một đối số cho hàm tạo. Bây giờ bạn đã di chuyển vấn đề ở nơi khác, nhưng thử nghiệm có thể trở nên dễ dàng hơn nhiều. Chỉ cần tạo một myObject giả và truyền nó vào. Constructor bây giờ sẽ trông giống như sau:

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

Đây là một kiểu tiêm phụ thuộc - thông qua hàm tạo. Một số cơ chế là có thể.

  • Như đã lưu ý trong các chú thích, một lựa chọn phổ biến là định nghĩa một hàm tạo không có gì, và có các phụ thuộc được tiêm thông qua các bộ định vị thuộc tính (h / t @MikeVella).
  • Martin Fowler tài liệu thay thế thứ ba (h / t @MarcDix), nơi các lớp thực hiện rõ ràng một giao diện cho các phụ thuộc mà họ muốn tiêm.

Khi không sử dụng tiêm phụ thuộc (chẳng hạn như trong các lớp làm quá nhiều công việc trong các nhà xây dựng của họ, vv), nó có xu hướng trở nên khó khăn hơn để cô lập các thành phần trong thử nghiệm đơn vị.

Trở lại năm 2013 khi tôi viết câu trả lời này, đây là một chủ đề chính về Blog thử nghiệm của Google. Điều này vẫn là lợi thế lớn nhất đối với tôi, vì bạn có thể không cần sự linh hoạt thêm trong thiết kế thời gian chạy của bạn (ví dụ, đối với định vị dịch vụ hoặc các mẫu tương tự), nhưng bạn thường cần phải tách riêng các lớp của bạn trong quá trình thử nghiệm.


1638
2017-09-25 00:49



Thừa nhận rằng bài viết của Ben Hoffstein về bài viết của Martin Fowler là cần thiết khi chỉ một 'phải đọc' về chủ đề này, tôi chấp nhận câu trả lời của wds vì nó thực sự trả lời câu hỏi ở đây trên SO. - AR.
1 cho lời giải thích và động lực: tạo ra các đối tượng mà trên đó một lớp phụ thuộc vào vấn đề của người khác. Một cách khác để nói rằng DI làm cho lớp học gắn kết hơn (chúng có ít trách nhiệm hơn). - Fuhrmanator
Bạn nói rằng sự phụ thuộc được truyền vào "trong constructor" nhưng khi tôi hiểu nó thì điều này không đúng. Nó vẫn còn phụ thuộc tiêm nếu phụ thuộc được thiết lập như là một tài sản sau khi đối tượng đã được instantiated, chính xác? - Mike Vella
@MikeVella Vâng, đó là chính xác. Nó không tạo ra sự khác biệt thực sự trong hầu hết các trường hợp, mặc dù các thuộc tính thường linh hoạt hơn một chút. Tôi sẽ chỉnh sửa văn bản một chút để chỉ ra điều đó. - wds
Một trong những câu trả lời hay nhất tôi đã tìm thấy từ trước đến nay, do đó tôi thực sự quan tâm đến việc cải thiện nó. Thiếu mô tả về dạng tiêm phụ thuộc thứ ba: Giao diện tiêm. - Marc Dix


Định nghĩa tốt nhất tôi đã tìm thấy cho đến nay là một bởi James Shore:

"Dependency Injection" là 25 đô la   thuật ngữ cho một khái niệm 5 xu. [...]   Tiêm phụ thuộc có nghĩa là cho một   đối tượng các biến cá thể của nó. [...].

một bài báo của Martin Fowler điều đó cũng có thể hữu ích.

Dependency injection về cơ bản cung cấp các đối tượng mà một đối tượng cần (các phụ thuộc của nó) thay vì có nó tự xây dựng chúng. Đó là một kỹ thuật rất hữu ích để thử nghiệm, vì nó cho phép các phụ thuộc được chế giễu hoặc bị bung ra.

Phụ thuộc có thể được tiêm vào các đối tượng bằng nhiều phương tiện (chẳng hạn như tiêm xây dựng hoặc tiêm setter). Người ta thậm chí có thể sử dụng các khuôn khổ tiêm phụ thuộc chuyên biệt (ví dụ: Spring) để làm điều đó, nhưng chắc chắn chúng không bắt buộc. Bạn không cần những khung công tác đó để có tiêm phụ thuộc. Instantiating và đi qua các đối tượng (phụ thuộc) rõ ràng là chỉ là một tiêm tốt như tiêm theo khuôn khổ.


2063
2017-09-26 16:50



Tôi thích lời giải thích của bài viết của James, đặc biệt là kết thúc: "Tuy nhiên, bạn phải ngạc nhiên trước bất kỳ phương pháp nào có ba khái niệm ('TripPlanner,' 'CabAgency,' và 'AirlineAgency'), biến chúng thành 9 lớp, và sau đó thêm hàng chục dòng mã keo và cấu hình XML trước khi một dòng logic ứng dụng được viết. " Đây là những gì tôi đã thấy rất thường xuyên (thật đáng buồn) - sự tiêm chích phụ thuộc (đó là điều tốt cho anh ta được giải thích bởi anh ta) bị lạm dụng để vượt quá những thứ có thể được thực hiện dễ dàng hơn - kết thúc bằng văn bản "hỗ trợ" mã ... - Matt
Re: "Instantiating và đi qua các đối tượng (phụ thuộc) rõ ràng chỉ là một tiêm tốt như tiêm theo khuôn khổ.". Vậy tại sao mọi người làm khung làm việc đó? - dzieciou
Vì lý do tương tự mà mọi khung công tác đều nhận được (hoặc ít nhất cũng nên nhận) được viết: bởi vì có rất nhiều mã lặp lại / soạn sẵn cần phải được viết khi bạn đạt đến độ phức tạp nhất định. Vấn đề là nhiều lần mọi người sẽ đạt tới một khuôn khổ ngay cả khi nó không thực sự cần thiết. - Thiago Arrais
Đây phải là câu trả lời đúng .. hoặc "Chèn phụ thuộc có nghĩa là 'chuyển một tham chiếu đối tượng'" - Hal50000
$ 25 hạn cho một khái niệm 5 xu là chết trên. Đây là một bài viết hay đã giúp tôi: codeproject.com/Articles/615139/… - Hill


Tôi đã tìm thấy ví dụ hài hước này về khớp nối lỏng lẻo:

Bất kỳ ứng dụng nào cũng bao gồm nhiều đối tượng cộng tác với nhau để thực hiện một số nội dung hữu ích. Theo truyền thống, mỗi đối tượng chịu trách nhiệm lấy các tham chiếu riêng của nó tới các đối tượng phụ thuộc (các phụ thuộc) mà nó cộng tác với. Điều này dẫn đến các lớp học kết hợp cao và mã khó kiểm tra.

Ví dụ, hãy xem xét một Car vật.

A Car phụ thuộc vào bánh xe, động cơ, nhiên liệu, pin, vv để chạy. Theo truyền thống, chúng tôi xác định thương hiệu của các đối tượng phụ thuộc như vậy cùng với định nghĩa của Car vật.

Không có Dependency Injection (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Ở đây, Car vật chịu trách nhiệm tạo các đối tượng phụ thuộc.

Nếu chúng ta muốn thay đổi loại đối tượng phụ thuộc của nó - hãy nói Wheel - sau lần đầu tiên NepaliRubberWheel() thủng? Chúng ta cần tạo lại đối tượng Car với sự phụ thuộc mới của nó ChineseRubberWheel(), nhưng chỉ Car nhà sản xuất có thể làm điều đó.

Vậy thì cái gì Dependency Injection làm chúng tôi vì ...?

Khi sử dụng tiêm phụ thuộc, các đối tượng được cung cấp sự phụ thuộc của chúng tại thời gian chạy hơn là thời gian biên dịch (thời gian sản xuất ô tô). Để bây giờ chúng ta có thể thay đổi Wheel bất cứ khi nào chúng ta muốn. Ở đây, dependency (wheel) có thể được tiêm vào Car trong thời gian chạy.

Sau khi sử dụng tiêm phụ thuộc:

Ở đây, chúng tôi tiêm chích các phụ thuộc (Bánh xe và pin) khi chạy. Vì thế từ : Dependency Injection. 

class Car{
  private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime]
  private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime]
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

Nguồn: Tìm hiểu về tiêm phụ thuộc


507
2018-05-22 04:01



Cách tôi hiểu điều này là, thay vì instantiating một đối tượng mới như là một phần của đối tượng khác, chúng ta có thể tiêm đối tượng đã nói khi nào và nếu nó là cần thiết, do đó loại bỏ sự phụ thuộc của đối tượng đầu tiên vào nó. Có đúng không? - JeliBeanMachine
Tôi đã mô tả điều này với một ví dụ về quán cà phê ở đây:digigene.com/design-patterns/dependency-injection-coffeeshop - Ali Nem
Thực sự thích sự tương tự này bởi vì nó là tiếng Anh đơn giản bằng cách sử dụng một sự tương tự đơn giản. Nói rằng tôi là Toyota, đã dành quá nhiều tài chính và sức mạnh của con người để chế tạo một chiếc xe từ thiết kế để tung ra dây chuyền lắp ráp, nếu có nhà sản xuất lốp xe có uy tín, tại sao tôi nên bắt đầu từ đầu để làm bộ phận sản xuất lốp xe, tức là new một lốp xe? Tôi không. Tất cả tôi phải làm là để mua (tiêm thông qua param) từ họ, cài đặt và wah-lah! Vì vậy, quay trở lại lập trình, nói rằng một dự án C # cần sử dụng một thư viện / lớp hiện có, có hai cách để chạy / gỡ lỗi, thêm một tham chiếu vào toàn bộ dự án này - Jeb50
(con't), .. bên ngoài thư viện / lớp, hoặc 2-thêm nó từ DLL. Trừ khi chúng ta phải xem những gì bên trong lớp bên ngoài này, thêm nó như là DLL là một cách dễ dàng hơn. Vì vậy, tùy chọn 1 là new nó, tùy chọn 2 là vượt qua nó trong như param. Có thể không chính xác, nhưng đơn giản ngu ngốc dễ hiểu. - Jeb50
Lời giải thích tuyệt vời, - Avan


Dependency Injection là một thực hành trong đó các đối tượng được thiết kế theo cách mà chúng nhận các thể hiện của các đối tượng từ các phần mã khác, thay vì xây dựng chúng trong nội bộ. Điều này có nghĩa rằng bất kỳ đối tượng nào thực hiện giao diện được yêu cầu bởi đối tượng có thể được thay thế mà không thay đổi mã, điều này đơn giản hóa việc kiểm thử và cải thiện tách.

Ví dụ, hãy xem xét các clases:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

Trong ví dụ này, việc thực hiện PersonService::addManager và PersonService::removeManager sẽ cần một thể hiện của GroupMembershipService để thực hiện công việc của mình. Nếu không có Dependency Injection, cách truyền thống để làm điều này sẽ là khởi tạo một GroupMembershipService trong hàm tạo của PersonService và sử dụng thuộc tính instance đó trong cả hai hàm. Tuy nhiên, nếu các nhà xây dựng của GroupMembershipService có nhiều thứ nó đòi hỏi, hoặc tệ hơn, có một số "setters" khởi tạo cần phải được gọi trên GroupMembershipService, mã phát triển khá nhanh và PersonServicebây giờ không chỉ phụ thuộc vào GroupMembershipService mà còn mọi thứ khác GroupMembershipService phụ thuộc. Hơn nữa, liên kết đến GroupMembershipService được hardcoded vào PersonService có nghĩa là bạn không thể "giả mạo" một GroupMembershipService cho mục đích thử nghiệm hoặc sử dụng mẫu chiến lược trong các phần khác nhau của ứng dụng của bạn.

Với Dependency Injection, thay vì instantiating GroupMembershipService bên trong của bạn PersonService, bạn có thể chuyển nó vào PersonService constructor, hoặc người nào khác thêm một thuộc tính (getter và setter) để thiết lập một cá thể cục bộ của nó. Điều này có nghĩa là PersonService không còn phải lo lắng về cách tạo GroupMembershipService, nó chỉ chấp nhận những cái được đưa ra, và làm việc với chúng. Điều này cũng có nghĩa là bất kỳ thứ gì là lớp con của GroupMembershipServicehoặc triển khai GroupMembershipService giao diện có thể được "tiêm" vào PersonService, và PersonService không cần biết về sự thay đổi.


233
2017-09-25 06:49



Sẽ tuyệt vời nếu bạn có thể đưa ra cùng một ví dụ về mã SAU KHI sử dụng DI - CodyBugstein
Tôi muốn python có một cái gì đó như thế này. Ngay bây giờ chúng ta phải thử mọi thứ để thử nghiệm. - user2601010
Bạn chắc chắn có thể làm tiêm phụ thuộc với Python, nhưng nhiều thư viện lớn (Django, vv) không phải vì các thư viện nhại trong python rất chắc chắn. Bạn vẫn phải viết mocks khi bạn sử dụng DI, nó chỉ dễ dàng hơn. - Adam Ness


Câu trả lời được chấp nhận là một câu trả lời hay - nhưng tôi muốn thêm vào điều này rằng DI rất giống với việc tránh các hằng số được mã hóa trong mã.

Khi bạn sử dụng một số hằng số như tên cơ sở dữ liệu, bạn sẽ nhanh chóng di chuyển nó từ bên trong mã sang một số tệp cấu hình và chuyển một biến chứa giá trị đó đến vị trí cần thiết. Lý do để làm điều đó là các hằng số này thường thay đổi thường xuyên hơn phần còn lại của mã. Ví dụ: nếu bạn muốn kiểm tra mã trong cơ sở dữ liệu thử nghiệm.

DI tương tự như thế này trong thế giới lập trình hướng đối tượng. Các giá trị ở đó thay vì các hằng số là toàn bộ các đối tượng - nhưng lý do để di chuyển mã tạo ra chúng từ mã lớp là tương tự - các đối tượng thay đổi thường xuyên hơn sau đó mã sử dụng chúng. Một trường hợp quan trọng cần thay đổi như vậy là kiểm tra.


142
2018-01-06 18:33



+1 "các đối tượng thay đổi thường xuyên hơn sau đó mã sử dụng chúng". Để khái quát hóa, hãy thêm một hướng vào các điểm thông lượng. Tùy thuộc vào điểm của thông lượng, các indirections được gọi bằng tên khác nhau !! - Chethan
"rất giống như việc tránh các hằng số được mã hóa trong mã" - dsdsdsdsd


Hãy tưởng tượng rằng bạn muốn đi câu cá:

  • Nếu không có tiêm phụ thuộc, bạn cần tự chăm sóc mọi thứ. Bạn cần phải tìm một chiếc thuyền, để mua một cần câu, để tìm mồi, vv Có thể, tất nhiên, nhưng nó đặt rất nhiều trách nhiệm về bạn. Về phần mềm, điều đó có nghĩa là bạn phải thực hiện tra cứu tất cả những thứ này.

  • Với tiêm phụ thuộc, người khác sẽ chăm sóc cho tất cả các chuẩn bị và làm cho các thiết bị cần thiết có sẵn cho bạn. Bạn sẽ nhận được ("được tiêm") thuyền, cần câu và mồi - tất cả đã sẵn sàng để sử dụng.


96
2017-10-22 04:47



Flipside là, hãy tưởng tượng bạn thuê một thợ sửa ống nước để làm lại phòng tắm của bạn, người sau đó nói, "Tuyệt vời, đây là danh sách các công cụ và vật liệu tôi cần bạn để có được cho tôi". Chẳng phải đó là công việc của thợ sửa ống nước sao? - Josh Caswell
Vì vậy, ai đó cần phải chăm sóc một số người nó không có kinh doanh biết đến .. nhưng vẫn quyết định thu thập danh sách thuyền, gậy và mồi - mặc dù đã sẵn sàng để sử dụng. - Chookoos
@JoshCaswell Không, đó sẽ là công việc của người thợ sửa ống nước. Là khách hàng, bạn cần thực hiện hệ thống ống nước. Cho rằng bạn cần một thợ sửa ống nước. Thợ sửa ống nước cần công cụ của nó. Để có được chúng, nó được trang bị bởi các công ty đường ống dẫn nước. Là một khách hàng, bạn không muốn biết chính xác những gì thợ ống nước làm hoặc cần. Là thợ sửa ống nước bạn biết bạn cần gì, nhưng bạn chỉ muốn làm công việc của mình, không nhận được mọi thứ. Khi sử dụng thợ ống nước bạn có trách nhiệm trang bị cho thợ ống nước của bạn những gì họ cần trước khi gửi chúng đến nhà của mọi người. - kai
@kai Tôi hiểu điểm của bạn. Trong phần mềm chúng ta đang nói về một nhà máy, đúng không? Nhưng DI cũng thường có nghĩa là lớp học không sử dụng một nhà máy vì nó vẫn chưa được tiêm. Bạn, khách hàng, sẽ cần phải liên hệ với chủ nhân (nhà máy) để cung cấp cho bạn các công cụ, vì vậy bạn có thể chuyển sang thợ sửa ống nước. Đó không phải là nó sẽ thực sự hoạt động như thế nào trong một chương trình sao? Vì vậy, trong khi khách hàng (gọi lớp / chức năng / bất cứ điều gì) không phải mua các công cụ, họ vẫn phải là người đàn ông trung gian đảm bảo họ làm cho nó đến thợ sửa ống nước (tiêm lớp) từ nhà tuyển dụng (nhà máy). - KingOfAllTrades
@KingOfAllTrades: Tất nhiên tại một số điểm bạn phải có một ai đó sử dụng và outfitting thợ ống nước, hoặc bạn không có thợ ống nước. Nhưng bạn không có khách hàng làm việc đó. Khách hàng chỉ yêu cầu một thợ ống nước, và được một người đã được trang bị với những gì anh ta cần để làm công việc của mình. Với DI, bạn vẫn có một số mã để hoàn thành các phụ thuộc. Nhưng bạn đang tách nó khỏi mã thực sự hoạt động. Nếu bạn đưa nó đến mức tối đa của nó, các đối tượng của bạn chỉ làm cho các phụ thuộc của chúng được biết, và việc xây dựng đồ thị đối tượng xảy ra bên ngoài, thường là trong mã init. - cHao


Điều này là giải thích đơn giản nhất về Dependency Injection và Dependency Injection Container Tôi đã từng gặp:

Không phụ thuộc tiêm

  • Ứng dụng cần Foo (ví dụ: bộ điều khiển), vì vậy:
  • Ứng dụng tạo Foo
  • Cuộc gọi ứng dụng Foo
    • Foo cần Bar (ví dụ: dịch vụ), vì vậy:
    • Foo tạo Bar
    • Foo cuộc gọi Bar
      • Bar cần Bim (một dịch vụ, một kho lưu trữ, …), vì thế:
      • Bar tạo ra Bim
      • Bar làm điều gì đó

Với Dependency Injection

  • Ứng dụng cần Foo, cần Bar, cần Bim, vì vậy:
  • Ứng dụng tạo Bim
  • Ứng dụng tạo Bar và cho nó Bim
  • Ứng dụng tạo Foo và cung cấp cho nó Bar
  • Cuộc gọi ứng dụng Foo
    • Foo cuộc gọi Bar
      • Bar làm điều gì đó

Sử dụng Dependency Injection Container

  • Ứng dụng cần Foo như vậy:
  • Ứng dụng nhận Foo từ Vùng chứa, vì vậy:
    • Vùng chứa tạo Bim
    • Container tạo Bar và cho nó Bim
    • Container tạo Foo và cho nó Bar
  • Cuộc gọi ứng dụng Foo
    • Foo cuộc gọi Bar
      • Bar làm điều gì đó

Dependency Injection và phụ thuộc vào Container Container là những thứ khác nhau:

  • Dependency Injection là một phương thức để viết mã tốt hơn
  • một DI Container là một công cụ để giúp tiêm phụ thuộc

Bạn không cần một container để làm tiêm phụ thuộc. Tuy nhiên một container có thể giúp bạn.


79
2018-05-05 11:53



@Trix Có tốt không khi sử dụng tiêm phụ thuộc hay không ?? - roottraveller
@rootTraveller google là bạn của bạn: Khi nào thì không thích hợp để sử dụng mô hình tiêm phụ thuộc? - Trix