32

Tôi thấy rất nhiều bài báo nói rằng IoC và DI tuyệt vời như thế nào và không có gì về lý do tại sao nó không tuyệt vời như vậy vì nó có thể làm cho mã phức tạp hơn. Tôi cũng thấy rằng IoC không nên nằm trong phần cốt lõi của mã của bạn mà là phần lớn cho các thư viện và các trình cắm thêm. Các bài viết thường là một tham chiếu nhỏ về cách hai mẫu có thể làm cho mã phức tạp hơn nhưng không nhiều về chi tiết về điều đó. Đó là câu hỏi của tôi - nơi cụ thể bạn không nên sử dụng những mẫu này?Khi nào không sử dụng IoC và DI?

Đây là chủ đề hay: What is Inversion of Control?. Nếu bạn nhìn xa hơn, có một bài đăng về trình kiểm tra chính tả và một bài đăng khác về cách IoC có thể không phải là một sử dụng tốt ở đó nếu nó chỉ là một trình kiểm tra chính tả. Như một hướng dẫn chung, nên IoC không được sử dụng của tôi đã bao giờ chỉ có một lớp cụ thể cho giao diện? Có nghĩa là tôi có IMyClass. Và sau đó chỉ có MyClassA cụ thể triển khai IMyClass. Tại sao tôi muốn IoC ở đó?

Nếu tôi có MyClassA, MyClassB và MyClassC, mỗi người thực hiện IMyClass, đó có lẽ là ứng cử viên tốt cho IoC đúng không?

Từ cùng một sợi, không ai biết những gì bài này có nghĩa là:

  • Inversion of Control = Hôn nhân
  • IOC container = Vợ
+0

Nếu bạn chưa đọc nó, tôi cũng khuyên bạn nên đọc bài viết của IoC/DI của Martin Fowler: http://martinfowler.com/articles/injection.html –

+3

Tôi đã đọc nó và nó vô ích cho câu hỏi của tôi. – 4thSpace

+0

có lẽ tương tự Mariage/Vợ là "vợ là một thực hiện cụ thể của hôn nhân"? :) Có nhiều container IOC khác nhau, nhưng Inversion of Control là một khái niệm – Prokurors

Trả lời

32

Giới thiệu câu hỏi của bạn về việc chỉ thực hiện một giao diện. Khi bạn sử dụng IoC, nó vẫn còn hữu ích để sử dụng một giao diện. Nó sẽ được dễ dàng hơn nhiều để tạo ra các bài kiểm tra đơn vị thực tế (mà không phụ thuộc vào việc thực hiện giao diện để làm việc một cách chính xác) bằng cách sử dụng mocks cho các giao diện này. Cốt lõi của việc sử dụng IoC là làm cho mã dễ kiểm tra hơn. Vì vậy, không sử dụng IoC nếu bạn không muốn kiểm tra, hoặc đã có một kế hoạch tốt hơn về thử nghiệm mà không có nó.

IMHO, DI và IoC tăng độ phức tạp đến được trả bằng cách dễ dàng kiểm tra và mã ít ghép nối hơn. Dễ dàng tách biệt các vấn đề và thực hiện các thay đổi trong tương lai. Bạn cũng có thể kiểm soát hành vi của nó tốt hơn.

Tôi có thể thấy khi nào không sử dụng vùng chứa IoC (vì nó dẫn đến phí cấu hình). Điều này sẽ xảy ra trên các dự án nhỏ, nơi bạn có thể thực hiện nó theo cách thủ công, thay vì sử dụng một container. Nhưng tôi không thể thấy nhiều tổn thất khi sử dụng DI, trừ khi bạn không định kiểm tra mã của mình ...

+2

Sẽ không phải là một cải tiến cấu trúc lại thiết kế loại bỏ sự cần thiết cho IoC và DI? Tôi hỏi vì IoC và DI không hề có ý nghĩa (nghĩa là có thể đọc được). Nếu mã có thể được tái cấu trúc để tạo ra khả năng đọc và tính độc lập mà IoC cố gắng đạt được, tôi tin rằng bạn kết thúc với một giải pháp tốt hơn. Có thể bạn vẫn có thể rơi vào phần thử nghiệm giả bằng cách không sử dụng IoC và DI. – 4thSpace

+3

IMHO, IoC và DI tăng khả năng đọc, một khi tôi biết những gì các đối tượng cần. Nếu hàm tạo của bạn có quá nhiều tham số, bạn có thể làm quá nhiều bên trong lớp đó, và bạn cũng nên refactor –

+0

Ngoài ra, hầu hết các ngôn ngữ lập trình khác Java đều có tên là initargs, giảm thêm bất kỳ vấn đề nào có thể đọc được. – jrockway

10

Từ the Castle Project:

Tại sao tôi sẽ không sử dụng nó?

Bạn không nên sử dụng một Inversion of chứa kiểm soát nếu bạn không quen thuộc với các khái niệm và nếu bạn không nhận ra những vấn đề họ cố gắng để giải quyết.

Ngoài ra, tùy thuộc vào kích thước và độ phức tạp của dự án, hộp chứa IoC có thể quá mức cần thiết. Thích sử dụng sử dụng nó trên các dự án trung bình đến lớn.

+3

-1 "Bạn không nên sử dụng ??? nếu bạn không quen thuộc với các khái niệm và nếu bạn không nhận ra các vấn đề mà họ cố gắng giải quyết." Đó là một tautology, vì nó áp dụng cho bất kỳ ??? +1 lý do thứ hai là tốt. – flybywire

+0

# 1 thực sự là ý thức phổ biến hơn bất cứ điều gì khác và một cách đơn giản, nghiêm túc "bạn có biết mình đang làm gì không?" truy vấn là xem xét mã đơn giản nhất bạn có thể làm. –

+14

Các ứng dụng nhỏ có xu hướng trở thành ứng dụng lớn. Khi bạn viết ứng dụng nhỏ của bạn giống như một ứng dụng nhỏ và nó trở thành một ứng dụng lớn, bạn sẽ gặp rắc rối. Khi bạn viết ứng dụng nhỏ của mình như một ứng dụng lớn và nó trở thành một ứng dụng lớn, mọi thứ hoạt động tốt. Nhược điểm duy nhất là phải lắng nghe mọi người rên rỉ về "overengineering". – jrockway

6

Các khiếu nại về IoC rất dễ hiểu: IoC biến một thứ đơn giản thành một thứ gì đó phức tạp.Giả sử bạn muốn làm điều gì đó cho từng phần tử trong danh sách (trong mã giả):

for each i in list: 
    do_something(i) 

IoC, về bản chất, đang chuyển trách nhiệm lặp lại vòng lặp cho người khác. Do đó, chúng tôi kết thúc với một cái gì đó như thế này:

ioc = new IocContainer() 
ioc.register_callback(do_something) 
ioc.go() 

Chú ý rằng ngay cả trong hình thức đơn giản này, mã dài hơn bản gốc ... và đó là chưa kể việc thực hiện IocContainer. Sau đó, ở dạng fanciest của nó, IocContainer có thể được khởi tạo tĩnh hoặc bằng hàm Main() tĩnh hoặc một nơi khác ẩn đi, và các hàm register_callback() được gọi tự động dựa trên một số mã khác lặp qua XML. tệp liệt kê tất cả các hàm do_something() của bạn và (đôi khi) ngay cả nội dung của danh sách để lặp qua.

Yay, tệp XML đã làm cho phần mềm của bạn có thể cấu hình được nhiều hơn! Đúng? Bạn có thể thay đổi những gì được thực hiện cho danh sách của bạn chỉ bằng cách thay đổi một tập tin văn bản đơn giản! Ngoại trừ, trớ trêu thay, mã nguồn của bạn bây giờ là dài hơn và khó hiểu hơn, nó phụ thuộc vào tất cả các loại thư viện mới, có thể lỗi (bao gồm IocContainer và trình phân tích cú pháp XML), và bạn đã thực sự tạo ra nó khó hơn để duy trì: Mã XML là khó hơn để hiểu và chỉnh sửa so với vòng lặp mã nguồn đơn giản ban đầu (bất kể ngôn ngữ được viết trong vòng lặp nào). Bạn cũng có ít quyền kiểm soát chính xác hơn cách bạn muốn lặp lại (sắp xếp hoặc phân loại? Độ sâu đầu tiên hoặc chiều rộng đầu tiên?) Vì IocContainer đang lấy trách nhiệm đó khỏi bạn. Đối với những thứ như plugin, bạn có thể sử dụng IoC vì đó là hợp lý để ứng dụng chính kiểm soát quá trình thực hiện và chỉ yêu cầu plugin trợ giúp ở đây và ở đó. Điều này được gọi là "cung cấp các móc" và đã tồn tại lâu hơn nhiều so với thuật ngữ IoC.

Đối với những thứ như kiểm tra đơn vị (thường là nơi mà tôi thường thấy), IoC thường vô ích, vì các bài kiểm tra của bạn cần có khả năng mô phỏng nhiều tình huống khác nhau, và IoC liên tục cản trở điều này.

Giả thiết cơ bản của IoC là việc tải dữ liệu và lặp thông qua bằng cách nào đó khó khăn và cần phải bị loại bỏ; giả định này không đúng, bởi vì nó chưa bao giờ nhiều hơn một vài dòng (hoặc bạn có thể chuyển nó sang một hàm riêng biệt), và thậm chí nếu nó đã lưu mã, nó làm giảm tính linh hoạt của bạn.

+4

Tôi sẽ không viết một bài luận dài để bác bỏ điều này, nhưng về cơ bản, tôi không đồng ý. Ví dụ đầu tiên của bạn là trực giao; "cho mỗi i trong danh sách" nên được viết lại thành một fmap trên một Functor. Điều này không liên quan đến tiêm phụ thuộc. Đối với DI, tôi đã thấy rằng nó làm cho những thay đổi "lớn" đối với ứng dụng của tôi dễ dàng hơn nhiều. Tôi không phải tái cơ cấu một loạt các mã "ở giữa" nếu một cái gì đó "tiếp tục xuống" cần một mảnh dữ liệu mới. Nó cho phép người xây dựng đồ thị đối tượng của tôi đối phó với các chi tiết, và nó làm cho mã của tôi thêm mô-đun. Cả hai đều tốt! – jrockway

+0

"... trình tạo biểu đồ đối tượng của tôi ..." Có ví dụ hay về đồ thị đối tượng trong .NET không? – 4thSpace

15

Sử dụng thùng chứa IoC hoặc không phải là quyết định thực hiện ở cấp độ của từng lớp - trong bất kỳ dự án nào bạn sẽ có loại và không được tạo bởi vùng chứa.

Sự cân bằng phức tạp là thế này:

  • Đối với một số lượng nhỏ các thành phần, một container IoC không thêm một số phí
  • Khi số lượng các thành phần trong một ứng dụng tăng:
    • Nếu không có một Thùng chứa IoC, thêm thành phần mới hoặc tái cấu trúc có xu hướng ngày càng trở nên khó khăn
    • Với thùng chứa IoC, việc thêm thành phần hoặc cấu trúc lại mới vẫn giữ nguyên: bạn chỉ cần lo lắng bout các phụ thuộc ngay lập tức của thành phần bạn đang thêm hoặc thay đổi.

Nếu bạn đã từng trải qua những hiện tượng thậm chí là một thiết kế tốt và duy trì codebase trở thành không thể quản lý với kích thước, bạn đã trải qua những vấn đề mà các container IoC giải quyết.

+0

+1 Chỉ cần tìm thấy chính mình trong một tình huống mà tôi đã hỏi câu hỏi chính xác này, vì tôi không thể thấy bất kỳ lợi ích nào trong việc sử dụng container IoC của tôi (đó là AutoFac, vì vậy cảm ơn! :) cho kịch bản cụ thể này, trong khi ở các phần khác của dự án này, IoC có vẻ là một lựa chọn hiển nhiên vì lợi ích rất rõ ràng. – si618

4

IoC/DI không phải là công nghệ, chúng là mô hình. Câu hỏi của bạn giống như hỏi "Khi nào tôi không nên sử dụng lập trình hướng đối tượng?" Đó là một quyết định lớn hơn các đơn vị cá nhân của codebase của bạn.

Để trả lời câu hỏi cụ thể của bạn, nếu bạn có đủ lớp học, nơi bạn có thể xây dựng đồ thị đối tượng một cách hợp lý bằng tay và bạn không lặp lại cùng một biểu đồ đối tượng nhiều lần, bạn có thể không cần vùng chứa IoC.

+0

Bạn có thể giải thích đồ thị đối tượng không? – 4thSpace

+0

Một đồ thị đối tượng (en.wikipedia.org/wiki/Object_graph) mô tả mối quan hệ giữa các thể hiện thời gian chạy. Ví dụ, giả sử 'FooService' có một' IBarRepository' như là một phụ thuộc hàm tạo, và bạn thực hiện điều đó với một cá thể 'BarRepository'. Đồ thị đối tượng sau đó sẽ là 'FooService0 -> BarRepository0'. Vì một container IoC đáp ứng tất cả các phụ thuộc, bạn có thể nói rằng nó xây dựng biểu đồ đối tượng. –

1

Tôi đoán tôi muốn nói câu trả lời đơn giản chỉ sử dụng IoC trên các đối tượng bạn muốn kiểm tra đơn vị.

Nếu điều này có vẻ khó hiểu, tôi rất thất vọng với mã được viết với nghĩa đen KHÔNG được xem xét cho thử nghiệm đơn vị. Tôi không thực sự hiểu tại sao có thêm sự phức tạp - nếu tôi cần biết loại đối tượng thực sự để chắc chắn tôi luôn có thể in ra lớp khi chạy.

20

Inversion of Control = Hôn nhân

IOC container = Vợ

Hôn nhân là định nghĩa của một mô hình nổi tiếng - Vợ là việc thực hiện rằng mô hình ;-)

Bernard.

+1

LOL +1. Cuối cùng cũng hiểu được sự khác biệt. –

+14

Điều này không chính xác lắm. Ioc Container là một linh mục, cặp vợ chồng (kết hôn) 2 đối tượng (người). – Kugel

0

Bài viết này giải quyết câu hỏi của bạn "nơi cụ thể bạn không nên sử dụng các mẫu này?" và đưa ra các ví dụ chi tiết về nhận xét của bạn rằng "hai mẫu ([IoC và DI]) có thể làm cho mã phức tạp hơn":

http://java.dzone.com/articles/beyond-injection-concerns-ioc.

Để tóm tắt hai trong số những điểm của bài viết này:

  1. Bạn không nên sử dụng IoC/DI nơi đóng gói là rất quan trọng.
  2. Bạn không nên sử dụng IoC/DI, nơi bạn không thể minh họa các ví dụ cụ thể về mức độ phức tạp được thêm vào thông qua việc sử dụng IoC/DI được cân nhắc bởi các lợi ích của việc sử dụng IoC/DI.
Các vấn đề liên quan