Khi nào thì không thích hợp để sử dụng mẫu tiêm phụ thuộc?


124

Từ khi học (và yêu) kiểm tra tự động, tôi đã thấy mình sử dụng mô hình tiêm phụ thuộc trong hầu hết các dự án. Có phải luôn luôn thích hợp để sử dụng mẫu này khi làm việc với thử nghiệm tự động? Có bất kỳ tình huống nào bạn nên tránh sử dụng tiêm phụ thuộc?


1
Liên quan (có thể không trùng lặp) - stackoverflow.com/questions/2407540/
Kẻ

3
Âm thanh hơi giống như mô hình chống búa vàng. vi.wikipedia.org/wiki/Anti-potype
Cuga

Câu trả lời:


123

Về cơ bản, tiêm phụ thuộc làm cho một số giả định (thường nhưng không phải lúc nào cũng hợp lệ) về bản chất của các đối tượng của bạn. Nếu đó là sai, DI có thể không phải là giải pháp tốt nhất:

  • Đầu tiên, về cơ bản nhất, DI giả định rằng sự kết hợp chặt chẽ giữa việc triển khai đối tượng là LUÔN LUÔN . Đây là bản chất của Nguyên tắc đảo ngược phụ thuộc: "không bao giờ nên phụ thuộc vào một sự lắng đọng; chỉ khi trừu tượng hóa".

    Điều này đóng đối tượng phụ thuộc để thay đổi dựa trên thay đổi đối với việc thực hiện cụ thể; một lớp phụ thuộc vào ConsoleWriter cụ thể sẽ cần thay đổi nếu đầu ra cần phải đi đến một tệp thay vào đó, nhưng nếu lớp đó chỉ phụ thuộc vào một IWriter phơi bày phương thức Write (), chúng ta có thể thay thế ConsoleWriter hiện đang được sử dụng với FileWriter và lớp phụ thuộc sẽ không biết sự khác biệt (Nguyên tắc thay thế Liskhov).

    Tuy nhiên, một thiết kế KHÔNG BAO GIỜ được đóng cho tất cả các loại thay đổi; nếu thiết kế của giao diện IWriter tự thay đổi, để thêm một tham số vào Write (), thì một đối tượng mã bổ sung (giao diện IWriter) bây giờ phải được thay đổi, bên trên đối tượng / phương thức triển khai và (các) cách sử dụng. Nếu những thay đổi trong giao diện thực tế có nhiều khả năng hơn những thay đổi đối với việc thực hiện giao diện đã nói, thì việc ghép lỏng lẻo (và các phụ thuộc được ghép lỏng lẻo) có thể gây ra nhiều vấn đề hơn giải quyết.

  • Thứ hai, và hệ quả, DI giả định rằng lớp phụ thuộc KHÔNG BAO GIỜ là nơi tốt để tạo ra sự phụ thuộc . Điều này đi đến Nguyên tắc Trách nhiệm duy nhất; nếu bạn có mã tạo ra một phụ thuộc và cũng sử dụng nó, thì có hai lý do mà lớp phụ thuộc có thể phải thay đổi (thay đổi cách sử dụng HOẶC việc triển khai), vi phạm SRP.

    Tuy nhiên, một lần nữa, việc thêm các lớp không xác định cho DI có thể là một giải pháp cho một vấn đề không tồn tại; Nếu nó hợp lý để đóng gói logic trong một phụ thuộc, nhưng logic đó là cách thực hiện phụ thuộc duy nhất như vậy, thì việc viết mã độ phân giải lỏng lẻo của phụ thuộc (tiêm, vị trí dịch vụ, nhà máy) sẽ khó khăn hơn chỉ sử dụng newvà quên nó đi

  • Cuối cùng, DI về bản chất tập trung kiến ​​thức của tất cả các phụ thuộc VÀ việc thực hiện chúng . Điều này làm tăng số lượng tài liệu tham khảo mà hội đồng thực hiện tiêm phải có, và trong hầu hết các trường hợp KHÔNG làm giảm số lượng tài liệu tham khảo theo yêu cầu của các lớp phụ thuộc thực tế.

    SOMETHING, SOMEWHERE, phải có kiến ​​thức về phụ thuộc, giao diện phụ thuộc và triển khai phụ thuộc để "kết nối các dấu chấm" và đáp ứng sự phụ thuộc đó. DI có xu hướng đặt tất cả kiến ​​thức đó ở mức rất cao, trong bộ chứa IoC hoặc trong mã tạo ra các đối tượng "chính" như dạng chính hoặc Bộ điều khiển phải hydrat hóa (hoặc cung cấp các phương thức xuất xưởng cho) các phụ thuộc. Điều này có thể đặt rất nhiều mã được liên kết chặt chẽ và rất nhiều tài liệu tham khảo lắp ráp ở cấp độ cao của ứng dụng của bạn, chỉ cần kiến ​​thức này để "ẩn" nó khỏi các lớp phụ thuộc thực tế (mà theo quan điểm rất cơ bản là nơi tốt nhất để có kiến ​​thức này, nơi nó được sử dụng).

    Nó cũng thường không loại bỏ các tham chiếu đã nói từ phía dưới xuống trong mã; một phụ thuộc vẫn phải tham chiếu thư viện chứa giao diện cho sự phụ thuộc của nó, nằm ở một trong ba nơi:

    • tất cả trong một hội đồng "Giao diện" duy nhất trở thành trung tâm ứng dụng,
    • mỗi cái cùng với (các) triển khai chính, loại bỏ lợi thế của việc không phải biên dịch lại các phụ thuộc khi phụ thuộc thay đổi, hoặc
    • một hoặc hai apiece trong các cụm lắp ráp có độ kết dính cao, làm tăng số lượng lắp ráp, làm tăng đáng kể thời gian "xây dựng đầy đủ" và giảm hiệu suất ứng dụng.

    Tất cả điều này, một lần nữa để giải quyết một vấn đề ở những nơi có thể không có.


1
SRP = nguyên tắc trách nhiệm duy nhất, cho bất cứ ai khác thắc mắc.
Theodore Murdock

1
Có, và LSP là nguyên tắc thay thế Liskov; với sự phụ thuộc vào A, sự phụ thuộc sẽ có thể được đáp ứng bởi B xuất phát từ A mà không có bất kỳ thay đổi nào khác.
KeithS

tiêm phụ thuộc đặc biệt giúp đỡ khi bạn cần lấy lớp A từ lớp B từ lớp D, v.v. trong trường hợp đó (và chỉ trong trường hợp đó), khung DI có thể tạo ra sự phình to lắp ráp hơn so với tiêm người nghèo. Ngoài ra, tôi chưa bao giờ bị tắc nghẽn do DI .. nghĩ đến chi phí bảo trì, không bao giờ chi phí cpu vì mã luôn có thể được tối ưu hóa, nhưng làm điều đó mà không có lý do có chi phí
GameDeveloper

Một giả định khác sẽ là "nếu một người phụ thuộc thay đổi thì nó sẽ thay đổi ở mọi nơi"? Hoặc nếu không, bạn phải xem tất cả các lớp tiêu thụ
Richard Tingle

3
Câu trả lời này không giải quyết được tác động kiểm tra của việc tiêm phụ thuộc so với mã hóa chúng. Xem thêm Nguyên tắc phụ thuộc rõ ràng ( deviq.com/explicit-dependencies-principl )
ssmith

30

Ngoài các khuôn khổ tiêm phụ thuộc, tiêm phụ thuộc (thông qua tiêm xây dựng hoặc tiêm setter) gần như là một trò chơi tổng bằng không: bạn giảm khớp nối giữa đối tượng A và đó là phụ thuộc B, nhưng bây giờ bất kỳ đối tượng nào cần một thể hiện của A cũng xây dựng đối tượng B.

Bạn đã giảm nhẹ khớp nối giữa A và B, nhưng giảm đóng gói của A và tăng khớp nối giữa A và bất kỳ lớp nào phải xây dựng một thể hiện của A, bằng cách ghép chúng với các phụ thuộc của A.

Vì vậy, tiêm phụ thuộc (không có khung) cũng có hại như nhau vì nó hữu ích.

Tuy nhiên, chi phí bổ sung thường dễ dàng được chứng minh: nếu mã khách hàng biết nhiều hơn về cách xây dựng sự phụ thuộc so với chính đối tượng, thì việc tiêm phụ thuộc thực sự sẽ làm giảm sự ghép đôi; ví dụ, Máy quét không biết nhiều về cách lấy hoặc xây dựng luồng đầu vào để phân tích cú pháp đầu vào hoặc nguồn mã khách muốn phân tích đầu vào từ đâu, do đó, hàm xây dựng của luồng đầu vào là giải pháp rõ ràng.

Kiểm tra là một biện minh khác, để có thể sử dụng phụ thuộc giả. Điều đó có nghĩa là thêm một hàm tạo bổ sung được sử dụng để chỉ kiểm tra cho phép các phần phụ thuộc được thêm vào: nếu thay vào đó, bạn thay đổi các hàm tạo của mình để luôn luôn yêu cầu các phần phụ thuộc được chèn vào, đột nhiên, bạn phải biết về các phần phụ thuộc của phần phụ thuộc của bạn để xây dựng phần phụ thuộc của bạn phụ thuộc trực tiếp và bạn không thể hoàn thành công việc.

Nó có thể hữu ích, nhưng bạn chắc chắn nên tự hỏi mình cho từng phụ thuộc, liệu lợi ích thử nghiệm có xứng đáng với chi phí không, và tôi có thực sự muốn chế giễu sự phụ thuộc này trong khi thử nghiệm không?

Khi một khung tiêm phụ thuộc được thêm vào và việc xây dựng các phụ thuộc được ủy thác không phải cho mã khách hàng mà thay vào đó là khung, phân tích chi phí / lợi ích thay đổi rất nhiều.

Trong khuôn khổ tiêm phụ thuộc, sự đánh đổi có một chút khác biệt; những gì bạn đang mất bằng cách tiêm phụ thuộc là khả năng dễ dàng biết bạn đang dựa vào việc triển khai nào và chuyển trách nhiệm quyết định sự phụ thuộc nào bạn đang dựa vào một số quy trình giải quyết tự động (ví dụ: nếu chúng tôi yêu cầu @ Tiêm Foo , phải có một cái gì đó mà @Provides Foo và có sẵn các phụ thuộc được tiêm) hoặc một số tệp cấu hình cấp cao quy định nhà cung cấp nào sẽ được sử dụng cho mỗi tài nguyên hoặc cho một số kết hợp của hai tài nguyên (ví dụ: có thể là một quá trình giải quyết tự động cho các phụ thuộc có thể bị ghi đè, nếu cần, sử dụng tệp cấu hình).

Như trong tiêm chích xây dựng, tôi nghĩ rằng lợi thế của việc này lại kết thúc, rất giống với chi phí thực hiện: bạn không cần phải biết ai đang cung cấp dữ liệu mà bạn dựa vào và nếu có nhiều tiềm năng nhà cung cấp, bạn không cần phải biết thứ tự ưu tiên để kiểm tra nhà cung cấp, đảm bảo rằng mọi vị trí cần kiểm tra dữ liệu cho tất cả các nhà cung cấp tiềm năng, v.v., bởi vì tất cả các điều đó được xử lý ở mức cao bằng cách tiêm phụ thuộc nền tảng.

Mặc dù cá nhân tôi không có nhiều kinh nghiệm với các khung DI, nhưng ấn tượng của tôi là chúng mang lại nhiều lợi ích hơn chi phí khi đau đầu tìm nhà cung cấp chính xác dữ liệu hoặc dịch vụ mà bạn cần có chi phí cao hơn đau đầu, khi một cái gì đó không thành công, không biết ngay tại địa phương mã nào đã cung cấp dữ liệu xấu gây ra lỗi sau đó trong mã của bạn.

Trong một số trường hợp, các mẫu khác che khuất sự phụ thuộc (ví dụ: bộ định vị dịch vụ) đã được áp dụng (và có lẽ cũng đã chứng minh giá trị của chúng) khi các khung DI xuất hiện trên hiện trường và các khung DI được áp dụng vì chúng mang lại một số lợi thế cạnh tranh, như yêu cầu ít mã soạn sẵn, hoặc có khả năng làm ít hơn để che khuất nhà cung cấp phụ thuộc khi cần thiết để xác định nhà cung cấp nào thực sự được sử dụng.


6
Chỉ cần một nhận xét nhanh về sự phụ thuộc chế giễu trong các bài kiểm tra và "bạn phải biết về sự phụ thuộc của bạn" sự phụ thuộc của bạn "... Điều đó không đúng chút nào. Người ta không cần phải biết hoặc quan tâm đến một số phụ thuộc của việc triển khai cụ thể nếu một giả được tiêm. Người ta chỉ cần biết về các phụ thuộc trực tiếp của lớp được kiểm tra, được thỏa mãn bởi các giả (s).
Eric King

6
Bạn hiểu lầm, tôi không nói về khi bạn tiêm giả, tôi đang nói về mã thực. Xem xét lớp A với phụ thuộc B, lần lượt có phụ thuộc C, lần lượt có phụ thuộc D. Không có DI, A xây dựng B, B xây dựng C, C xây dựng D. Với tiêm xây dựng, để xây dựng A, trước tiên bạn phải xây dựng B, để xây dựng B trước tiên bạn phải xây dựng C và để xây dựng C trước tiên bạn phải xây dựng D. Vì vậy, lớp A bây giờ phải biết về D, sự phụ thuộc của một phụ thuộc của một phụ thuộc, để xây dựng B. Điều này dẫn đến khớp nối quá mức.
Theodore Murdock

1
Sẽ không có quá nhiều chi phí để có một nhà xây dựng bổ sung được sử dụng để thử nghiệm chỉ cho phép các phụ thuộc được đưa vào. Tôi sẽ cố gắng sửa đổi những gì tôi nói.
Theodore Murdock

3
Liều lượng tiêm phụ thuộc chỉ di chuyển phụ thuộc xung quanh trong một trò chơi tổng bằng không. Bạn di chuyển các phụ thuộc của bạn đến những nơi họ đã tồn tại: dự án chính. Thậm chí, bạn có thể sử dụng các cách tiếp cận dựa trên quy ước để đảm bảo các phụ thuộc chỉ được giải quyết trong thời gian chạy, ví dụ bằng cách chỉ định lớp nào cụ thể bằng không gian tên hoặc bất cứ thứ gì. Tôi phải nói rằng đây là một câu trả lời ngây thơ
Sprague

2
@Sprague Tiêm phụ thuộc di chuyển xung quanh các phụ thuộc trong trò chơi có tổng âm hơi, nếu bạn áp dụng nó trong trường hợp không nên áp dụng. Mục đích của câu trả lời này là để nhắc nhở mọi người rằng tiêm phụ thuộc không phải là tốt, nó là một mẫu thiết kế cực kỳ hữu ích trong các trường hợp phù hợp, nhưng chi phí không đáng có trong các trường hợp thông thường (ít nhất là trong các lĩnh vực lập trình tôi đã làm việc , Tôi hiểu trong một số lĩnh vực, nó thường hữu ích hơn những lĩnh vực khác). DI đôi khi trở thành một từ thông dụng và một kết thúc trong chính nó, mà bỏ lỡ điểm.
Theodore Murdock

11
  1. nếu bạn đang tạo các thực thể cơ sở dữ liệu, thay vào đó bạn nên có một số lớp xuất xưởng mà bạn sẽ tiêm vào bộ điều khiển của mình,

  2. nếu bạn cần tạo các đối tượng nguyên thủy như ints hoặc longs. Ngoài ra, bạn nên tạo "bằng tay" hầu hết các đối tượng thư viện tiêu chuẩn như ngày tháng, hướng dẫn, v.v.

  3. Nếu bạn muốn tiêm chuỗi cấu hình, có lẽ tốt hơn là nên tiêm một số đối tượng cấu hình (nói chung, nên bọc các loại đơn giản vào các đối tượng có ý nghĩa: int nhiệt độ InCelsiusDegrees -> Nhiệt độ CelciusDeegree)

Và không sử dụng Công cụ định vị dịch vụ thay thế tiêm phụ thuộc, đó là mô hình chống, thông tin thêm: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPotype.aspx


Tất cả các điểm là về việc sử dụng hình thức tiêm khác nhau thay vì không sử dụng nó hoàn toàn. +1 cho liên kết mặc dù.
Jan Hudec

3
Trình định vị dịch vụ KHÔNG phải là một mô hình chống và anh chàng mà blog bạn liên kết không phải là một chuyên gia về các mẫu. Rằng anh ấy có StackExchange - nổi tiếng là một loại dịch vụ đối với thiết kế phần mềm. Martin Fowler (tác giả của các mẫu chủ yếu của Kiến trúc ứng dụng doanh nghiệp) có cái nhìn hợp lý hơn về vấn đề này: martinfowler.com/articles/injection.html
Colin

1
@Colin, ngược lại, Bộ định vị dịch vụ chắc chắn là một mô hình chống, và ở đây tóm lại là lý do tại sao .
Kyralessa

@Kyralessa - bạn nhận ra rằng các khung DI bên trong sử dụng mẫu Trình định vị dịch vụ để tra cứu các dịch vụ, phải không? Có những trường hợp cả hai đều phù hợp và không phải là kiểu chống mặc dù StackExchange ghét một số người muốn kết xuất.
Colin

Chắc chắn, và chiếc xe của tôi sử dụng pít-tông và bugi và một loạt những thứ khác không phải là giao diện tốt cho một người bình thường chỉ muốn lái xe.
Kyralessa

8

Khi bạn không thể đạt được bất cứ điều gì bằng cách làm cho dự án của bạn có thể duy trì và kiểm tra được.

Nghiêm túc mà nói, tôi yêu thích IoC và DI nói chung, và tôi nói rằng 98% thời gian tôi sẽ sử dụng mô hình đó mà không thất bại. Điều này đặc biệt quan trọng trong môi trường nhiều người dùng, nơi mã của bạn có thể được sử dụng lại nhiều lần bởi các thành viên nhóm khác nhau và các dự án khác nhau, vì nó tách biệt logic khỏi việc thực hiện. Ghi nhật ký là một ví dụ điển hình cho việc này, giao diện ILog được đưa vào một lớp có thể duy trì gấp ngàn lần so với việc cắm vào khung ghi nhật ký của bạn, vì bạn không đảm bảo rằng một dự án khác sẽ sử dụng cùng một khung ghi nhật ký (nếu nó sử dụng một trong tất cả!).

Tuy nhiên, có những lúc nó không phải là một mẫu áp dụng. Ví dụ: các điểm nhập chức năng được triển khai trong ngữ cảnh tĩnh bởi trình khởi tạo không thể ghi đè (WebMethods, tôi đang xem xét bạn, nhưng phương thức Main () trong lớp Chương trình của bạn là một ví dụ khác) đơn giản là không thể có các phụ thuộc được đưa vào lúc khởi tạo thời gian. Tôi cũng đã đi xa để nói rằng một nguyên mẫu, hoặc bất kỳ đoạn mã điều tra vứt bỏ nào, cũng là một ứng cử viên tồi; lợi ích của DI là khá nhiều lợi ích từ trung hạn đến dài hạn (khả năng kiểm tra và khả năng duy trì), nếu bạn chắc chắn rằng bạn sẽ vứt bỏ phần lớn một đoạn mã trong vòng một tuần hoặc do đó tôi sẽ nói rằng bạn chẳng đạt được gì khi cô lập phụ thuộc, chỉ cần dành thời gian bạn thường dành để kiểm tra và cô lập các phụ thuộc để mã hoạt động.

Nói chung, thật hợp lý khi áp dụng một cách tiếp cận thực tế cho bất kỳ phương pháp hoặc mô hình nào, vì không có gì có thể áp dụng 100% thời gian.

Một điều cần lưu ý là nhận xét của bạn về kiểm tra tự động: định nghĩa của tôi về điều này là kiểm tra chức năng tự động , ví dụ kiểm tra selen theo kịch bản nếu bạn đang ở trong bối cảnh web. Đây thường là các thử nghiệm hộp đen hoàn toàn, không cần biết về hoạt động bên trong của mã. Nếu bạn đang đề cập đến các bài kiểm tra Đơn vị hoặc Tích hợp, tôi sẽ nói rằng mẫu DI hầu như luôn có thể áp dụng cho bất kỳ dự án nào phụ thuộc nhiều vào loại thử nghiệm hộp trắng đó, ví dụ, vì nó cho phép bạn thử nghiệm những thứ như các phương thức chạm vào DB mà không cần phải có DB.


Tôi không hiểu ý của bạn về việc đăng nhập. Chắc chắn tất cả các logger sẽ không tuân thủ cùng một giao diện, vì vậy bạn phải viết trình bao bọc của riêng mình để dịch giữa cách ghi nhật ký dự án này và một trình ghi nhật ký cụ thể (giả sử bạn muốn có thể dễ dàng thay đổi logger). Vậy sau đó DI cung cấp cho bạn những gì?
Richard Tingle

@RichardTingle Tôi muốn nói rằng bạn nên xác định trình bao bọc ghi nhật ký của mình là một giao diện và sau đó bạn viết một triển khai nhẹ của giao diện đó cho mỗi dịch vụ ghi nhật ký bên ngoài, thay vì sử dụng một lớp ghi nhật ký duy nhất bao bọc nhiều trừu tượng. Đó là cùng một khái niệm bạn đang đề xuất nhưng được trừu tượng hóa ở một cấp độ khác nhau.
Ed James

1

Trong khi các câu trả lời khác tập trung vào các khía cạnh kỹ thuật, tôi muốn thêm một khía cạnh thực tế.

Trong những năm qua, tôi đã đi đến một kết luận rằng có một số yêu cầu thực tế cần phải được đáp ứng nếu giới thiệu Dependency Injection là một thành công.

  1. Cần có một lý do để giới thiệu nó.

    Điều này nghe có vẻ rõ ràng, nhưng nếu mã của bạn chỉ lấy những thứ từ cơ sở dữ liệu và trả về nó mà không có logic thì việc thêm một thùng chứa DI làm cho mọi thứ phức tạp hơn với lợi ích không thực sự. Kiểm thử tích hợp sẽ quan trọng hơn ở đây.

  2. Đội cần được đào tạo và trên tàu.

    Trừ khi phần lớn của nhóm ở trên tàu và hiểu DI thêm đảo ngược container điều khiển trở thành một cách khác để làm mọi thứ và làm cho cơ sở mã thậm chí còn phức tạp hơn.

    Nếu DI được giới thiệu bởi một thành viên mới của nhóm, vì họ hiểu và thích nó và chỉ muốn chứng tỏ rằng họ tốt, VÀ nhóm không tích cực tham gia, có nguy cơ thực sự là nó sẽ làm giảm chất lượng mật mã.

  3. Bạn cần kiểm tra

    Mặc dù việc tách rời nói chung là một điều tốt, DI có thể chuyển độ phân giải của một phụ thuộc từ thời gian biên dịch sang thời gian chạy. Điều này thực sự khá nguy hiểm nếu bạn không kiểm tra tốt. Chạy thất bại giải quyết thời gian có thể tốn kém để theo dõi và giải quyết.
    (Rõ ràng từ bài kiểm tra của bạn mà bạn làm, nhưng nhiều đội không kiểm tra đến mức mà DI yêu cầu.)


1

Đây không phải là một câu trả lời hoàn chỉnh, mà chỉ là một điểm khác.

Khi bạn có một ứng dụng khởi động một lần, chạy trong một thời gian dài (như ứng dụng web) DI có thể tốt.

Khi bạn có một ứng dụng khởi động nhiều lần và chạy trong thời gian ngắn hơn (như ứng dụng di động), bạn có thể không muốn có container.


Tôi không thấy thời gian trực tiếp của ứng dụng liên quan đến DI
Michael Freidgeim

@MichaelFreidgeim Phải mất thời gian để khởi tạo bối cảnh, thông thường các container DI khá nặng, chẳng hạn như Spring. Tạo một ứng dụng hello world chỉ với một lớp và tạo một ứng dụng với Spring và bắt đầu cả 10 lần và bạn sẽ thấy ý tôi là gì.
Koray Tugay

Nghe có vẻ như là một vấn đề với một container DI cá nhân. Trong thế giới .Net, tôi chưa từng nghe về thời gian khởi tạo là một vấn đề thiết yếu đối với các container DI
Michael Freidgeim

1

Cố gắng sử dụng các nguyên tắc OOP cơ bản: sử dụng tính kế thừa để trích xuất chức năng chung, đóng gói (ẩn) những thứ cần được bảo vệ khỏi thế giới bên ngoài bằng cách sử dụng các loại / thành viên riêng tư / nội bộ / được bảo vệ. Sử dụng bất kỳ khung kiểm tra mạnh mẽ nào để tiêm mã chỉ cho các thử nghiệm, ví dụ: https://www.typemock.com/ hoặc https://www.telerik.com/products/mocking.aspx .

Sau đó thử viết lại bằng DI và so sánh mã, những gì bạn sẽ thấy thường thấy với DI:

  1. Bạn có nhiều giao diện hơn (nhiều loại hơn)
  2. Bạn đã tạo các bản sao chữ ký của các phương thức công khai và bạn sẽ phải nhân đôi nó (bạn không thể thay đổi một số tham số một lần, bạn sẽ phải thực hiện hai lần, về cơ bản tất cả các khả năng tái cấu trúc và điều hướng trở nên phức tạp hơn)
  3. Bạn đã di chuyển một số lỗi biên dịch để chạy lỗi thời gian (với DI bạn có thể bỏ qua một số phụ thuộc trong quá trình mã hóa và không chắc chắn nó sẽ bị lộ trong quá trình thử nghiệm)
  4. Bạn đã mở gói của bạn. Bây giờ các thành viên được bảo vệ, các loại nội bộ vv đã trở thành công khai
  5. Tổng số lượng mã đã được tăng lên

Tôi sẽ nói từ những gì tôi thấy, hầu như luôn luôn, chất lượng mã đang bị giảm với DI.

Tuy nhiên, nếu bạn chỉ sử dụng công cụ sửa đổi truy cập "công khai" trong khai báo lớp và / hoặc công cụ sửa đổi công khai / riêng tư cho các thành viên và / hoặc bạn không có lựa chọn mua các công cụ kiểm tra đắt tiền và đồng thời bạn cần kiểm tra đơn vị có thể ' Được thay thế bằng thử nghiệm tích hợp và / hoặc bạn đã có giao diện cho các lớp bạn đang nghĩ để tiêm, DI là một lựa chọn tốt!

Có lẽ tôi sẽ nhận được rất nhiều điểm trừ cho bài đăng này, tôi tin bởi vì hầu hết các nhà phát triển hiện đại chỉ không hiểu cách thức và lý do sử dụng từ khóa nội bộ và cách giảm khớp nối các thành phần của bạn và cuối cùng là tại sao giảm nó) thử viết mã và so sánh


0

Một thay thế cho Dependency Injection là sử dụng Bộ định vị dịch vụ . Trình định vị dịch vụ dễ hiểu hơn, gỡ lỗi và giúp việc xây dựng một đối tượng đơn giản hơn đặc biệt nếu bạn không sử dụng khung DI. Bộ định vị dịch vụ là một mẫu tốt để quản lý các phụ thuộc tĩnh bên ngoài , ví dụ: cơ sở dữ liệu mà nếu không bạn sẽ phải truyền vào mọi đối tượng trong lớp truy cập dữ liệu của mình.

Khi tái cấu trúc mã kế thừa , việc tái cấu trúc thành Bộ định vị dịch vụ thường dễ dàng hơn so với Dependency Injection. Tất cả những gì bạn làm là thay thế tức thời bằng tra cứu dịch vụ và sau đó giả mạo dịch vụ trong bài kiểm tra đơn vị của bạn.

Tuy nhiên, có một số nhược điểm đối với Trình định vị dịch vụ. Biết được sự suy giảm của một lớp là khó khăn hơn vì các phụ thuộc được ẩn trong việc thực hiện của lớp, không phải trong các hàm tạo hoặc setters. Và việc tạo ra hai đối tượng dựa trên các triển khai khác nhau của cùng một dịch vụ là khó khăn hoặc không thể.

TLDR : Nếu lớp của bạn có các phụ thuộc tĩnh hoặc bạn đang tái cấu trúc mã kế thừa, Bộ định vị dịch vụ tốt hơn nhiều so với DI.


12
Service Locator ẩn phụ thuộc trong mã của bạn . Nó không phải là một mô hình tốt để sử dụng.
Kyralessa

Khi nói đến sự phụ thuộc tĩnh, tôi muốn thấy các mặt tiền được đặt trên chúng thực hiện các giao diện. Những thứ này sau đó có thể được tiêm phụ thuộc và chúng rơi vào lựu đạn tĩnh cho bạn.
Erik Dietrich

@Kyralessa Tôi đồng ý, Bộ định vị dịch vụ có nhiều nhược điểm và DI hầu như luôn luôn thích hợp hơn. Tuy nhiên, tôi tin rằng có một vài ngoại lệ cho quy tắc này, như với tất cả các nguyên tắc lập trình.
Hội trường Garrett

Việc sử dụng chính được chấp nhận của vị trí dịch vụ nằm trong mẫu Chiến lược, trong đó lớp "bộ chọn chiến lược" được cung cấp đủ logic để tìm chiến lược sử dụng và trao lại hoặc chuyển cuộc gọi cho nó. Ngay cả trong trường hợp này, bộ chọn chiến lược có thể là một mặt tiền cho một phương thức nhà máy được cung cấp bởi một bộ chứa IoC, được đưa ra cùng một logic; lý do bạn phá vỡ nó là để đặt logic ở nơi nó thuộc về chứ không phải nơi nó bị ẩn giấu nhiều nhất.
KeithS

Để trích dẫn Martin Fowler, "Sự lựa chọn giữa (DI và Bộ định vị dịch vụ) ít quan trọng hơn nguyên tắc tách cấu hình khỏi sử dụng". Ý tưởng rằng DI LUÔN tốt hơn là hogwash. Nếu một dịch vụ được sử dụng trên toàn cầu, việc sử dụng DI sẽ trở nên cồng kềnh và dễ vỡ hơn. Ngoài ra, DI ít thuận lợi hơn cho các kiến ​​trúc plugin nơi việc triển khai không được biết đến cho đến thời gian chạy. Hãy nghĩ rằng sẽ khó xử thế nào khi luôn chuyển các đối số bổ sung cho Debug.WriteLine () và tương tự!
Colin
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.