Sự khác biệt thực tế giữa các phong cách tiêm phụ thuộc là gì?


12

Tôi mới sử dụng phương pháp tiêm phụ thuộc và tôi có một vài câu hỏi về việc nên sử dụng kiểu nào trong các ứng dụng của mình. Tôi vừa đọc Inversion of Control Container và mẫu Dependency Injection của Martin Fowler, nhưng tôi không thể có được sự khác biệt thực tế giữa constructor, setter và giao diện tiêm.

Dường như với tôi rằng các lý do để sử dụng cái này hơn cái kia chỉ là vấn đề làm sạch mã và / hoặc rõ ràng. Sự khác biệt là gì? Có bất kỳ lợi thế hay bất lợi nào khi sử dụng cái này hơn cái kia không, hay nó chỉ là những gì tôi đã nêu trước đây?

Theo tôi, tiêm constructor là trực quan nhất trong tất cả, cũng như tiêm giao diện là ít nhất. Mặt khác, tiêm setter là một thuật ngữ trung gian, nhưng, bạn có được cho là có thể thay đổi thể hiện của đối tượng phụ thuộc mà bạn đã tiêm ban đầu không? Liệu phong cách tiêm này có đảm bảo rằng đối tượng cần sự phụ thuộc sẽ luôn luôn tiêm không? Tôi tin là không, nhưng làm ơn hãy sửa tôi nếu tôi sai.


Không chắc chắn những gì khác bạn đã tìm thấy hoặc đọc trên đường đi. Tôi tìm thấy một số chủ đề tương tự ở đâyở đây . Tôi là người mới, và sẽ tò mò với câu trả lời bạn có thể nhận được :)

@ user1766760 bạn nên giúp tôi sau đó, câu hỏi của tôi đang được bỏ phiếu để đóng, thêm hai phiếu và thế là xong !!! Bỏ phiếu cho câu hỏi hoặc một cái gì đó, tôi không biết những gì có thể được thực hiện để tránh đóng cửa.
ecampver

erm ... Tôi là người mới đối với bản thân mình vì vậy tôi không chắc chắn làm thế nào tôi có thể giúp đỡ / tại sao nó lại được bỏ phiếu để đóng (Tôi không thấy bất kỳ dấu hiệu nào ??). Nếu tôi mạo hiểm đoán, có lẽ đó không phải là một câu hỏi lập trình cụ thể mà là một cuộc thảo luận?

Bạn có thể muốn mở rộng câu hỏi một chút. Tiêm phụ thuộc là một dạng của "Đảo ngược kiểm soát". Có những lựa chọn thay thế. Một thay thế hữu ích nhưng chín muồi cho lạm dụng thay thế là Vị trí dịch vụ chẳng hạn.
Ian

Câu trả lời:


12

Con Contortor tiêm có lợi thế là nó làm cho sự phụ thuộc rõ ràng và buộc khách hàng phải cung cấp một thể hiện. Nó cũng có thể đảm bảo rằng máy khách không thể thay đổi thể hiện sau này. Một nhược điểm (có thể) là bạn phải thêm một tham số cho hàm tạo của mình.

Setter Injection có lợi thế là nó không yêu cầu thêm tham số vào hàm tạo. Nó cũng không yêu cầu khách hàng thiết lập thể hiện. Điều này rất hữu ích cho các phụ thuộc tùy chọn. Điều này cũng có thể hữu ích nếu bạn muốn lớp tạo, ví dụ, một kho lưu trữ dữ liệu thực theo mặc định, và sau đó trong một thử nghiệm, bạn có thể sử dụng setter để thay thế nó bằng một thể hiện kiểm tra.

Giao diện tiêm , theo như tôi có thể nói, không khác nhiều so với tiêm setter. Trong cả hai trường hợp, bạn (tùy chọn) thiết lập một phụ thuộc có thể được thay đổi sau này.

Cuối cùng nó là một vấn đề ưu tiên và có hoặc không phụ thuộc là cần thiết . Cá nhân, tôi sử dụng tiêm constructor gần như độc quyền. Tôi thích điều đó làm cho các phụ thuộc của một lớp rõ ràng bằng cách buộc máy khách cung cấp một thể hiện trong hàm tạo. Tôi cũng thích rằng khách hàng không thể thay đổi trường hợp sau khi thực tế.

Thông thường, lý do duy nhất của tôi để thông qua hai triển khai riêng biệt là để thử nghiệm. Trong sản xuất, tôi có thể vượt qua trong một DataRepository, nhưng trong thử nghiệm, tôi sẽ vượt qua trong một FakeDataRepository. Trong trường hợp này, tôi thường sẽ cung cấp hai hàm tạo: một không có tham số và một hàm chấp nhận a IDataRepository. Sau đó, trong hàm tạo không có tham số, tôi sẽ xâu chuỗi một cuộc gọi đến hàm tạo thứ hai và chuyển vào a new DataRepository().

Đây là một ví dụ trong C #:


public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}

Điều này được gọi là tiêm phụ thuộc của người nghèo. Tôi thích nó bởi vì trong mã khách hàng sản xuất, tôi không cần phải lặp lại chính mình bằng cách có một vài câu lệnh lặp đi lặp lại trông giống như

var foo = new Foo(new DataRepository());
Tuy nhiên, tôi vẫn có thể vượt qua trong một triển khai thay thế để thử nghiệm. Tôi nhận ra rằng với DI của Poor Man, tôi đang giải mã sự phụ thuộc của mình, nhưng tôi chấp nhận được vì tôi chủ yếu sử dụng DI để thử nghiệm.


cảm ơn, điều đó khá gần với những gì tôi hiểu, nhưng vẫn có kịch bản nào mà bạn không thể sử dụng phương thức tiêm xây dựng ?
ecampver 17/03/13

1
Bạn có thể sẽ không muốn sử dụng tiêm constructor cho các phụ thuộc tùy chọn. Tôi không thể nghĩ ra bất kỳ lý do nào khác để không sử dụng nó.
jhewlett 17/03/13

Tôi sử dụng phương thức tiêm setter đặc biệt để điền vào một số lớp cấu hình bao gồm một số lượng lớn các giá trị. Tôi không sử dụng nó ở bất cứ nơi nào khác. Tôi luôn có một chút mơ hồ về việc tạo ra một trường hợp cho các tham số tùy chọn bởi vì rất có thể đó là một sự vi phạm quy tắc trách nhiệm duy nhất. Tất nhiên các quy tắc có nghĩa là bị phá vỡ, vì vậy ...
Ian

@jhewlett - thật tuyệt vời, tôi đã tìm kiếm một kỹ thuật khai thác đơn giản tốt để thử nghiệm đơn vị mà không làm phức tạp giải pháp của tôi - và tôi nghĩ rằng đây là nó :)
Rocklan 18/03/13

2

Sự khác biệt giữa hàm tạo và hàm setter đã được mô tả đầy đủ ở trên, vì vậy tôi sẽ không giải thích thêm về chúng.

Giao diện tiêm là một hình thức tiêm nâng cao hơn rất hữu ích vì nó cho phép quyết định sự phụ thuộc vào thời điểm nó được sử dụng thay vì trong quá trình khởi tạo đối tượng sẽ sử dụng nó. Điều này cho phép một số tùy chọn hữu ích:

  • Sự phụ thuộc có thể được đặt trong phạm vi khác nhau cho đối tượng mà nó được tiêm vào; chẳng hạn, bạn có thể sử dụng giao diện tiêm để cung cấp một đối tượng tồn tại trên mỗi phiên người dùng hoặc mỗi luồng cho một đơn vị toàn cầu. Mỗi khi đối tượng cần sự phụ thuộc, nó sẽ gọi phương thức getter được cung cấp bởi khung công tác và điều này có thể trả về các kết quả khác nhau tùy thuộc vào tình huống mà nó được gọi.

  • Nó cho phép khởi tạo lười biếng - không cần phải phụ thuộc vào khởi tạo cho đến khi nó được sử dụng

  • Nó cho phép các phụ thuộc được tải từ một bản sao được lưu trong bộ nhớ cache khi chúng tồn tại hoặc được khởi tạo lại khi chúng không (ví dụ: sử dụng SoftReferencetrong Java).

Rõ ràng các kỹ thuật tiên tiến như thế này có nhược điểm; trong trường hợp này, vấn đề chính là mã trở nên kém rõ ràng hơn (các lớp được sử dụng trong mã của bạn trở nên trừu tượng và không có triển khai cụ thể rõ ràng nào về chúng, điều này có thể gây nhầm lẫn nếu bạn không sử dụng nó) và bạn trở nên phụ thuộc hơn trên khung tiêm phụ thuộc của bạn ( dĩ nhiên vẫn có thể khởi tạo các đối tượng của bạn một cách thủ công, nhưng khó hơn so với các kiểu tiêm khác).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.