Tại sao Inversion of Control được đặt tên theo cách đó?


98

Các từ inverthoặc hoàn toàn controlkhông được sử dụng để định nghĩa Đảo ngược điều khiển trong các định nghĩa mà tôi đã thấy.

Định nghĩa

Wikipedia

đảo ngược điều khiển (IoC) là một kỹ thuật lập trình, được thể hiện ở đây dưới dạng lập trình hướng đối tượng, trong đó việc ghép đối tượng bị ràng buộc trong thời gian chạy bởi một đối tượng trình biên dịch và thường không được biết đến trong thời gian biên dịch bằng phân tích tĩnh. ~ http://en.wikipedia.org/wiki/Inversion_of_control

Martin Fowler

Inversion of Control là một mẫu phổ biến trong cộng đồng Java, giúp nối các thùng chứa nhẹ hoặc lắp ráp các thành phần từ các dự án khác nhau thành một ứng dụng gắn kết. ~ dựa trên http://www.martinfowler.com/articles/injection.html (được điều chỉnh lại)


Vậy tại sao Inversion of Control có tên Inversion of Control? Kiểm soát nào đang được đảo ngược và bằng gì? Có cách nào để xác định Inversion of Control bằng thuật ngữ: đảo ngượckiểm soát không?


22
Đạo cụ cho người có thể giải thích điều này bằng tiếng Anh.
Robert Harvey

2
Nếu một định nghĩa sử dụng từ mà nó định nghĩa, đó sẽ là một từ điển thất bại. Logic tương tự áp dụng ở đây.
Izkata

2
Xem thêm trên StackOverflow: Inversion of Control là gì?
Izkata

2
@Izkata, rất phổ biến đối với từ điển để định nghĩa các cụm từ theo thuật ngữ riêng của cụm từ hoặc cụm từ đồng nghĩa. tức là: lực của định nghĩa tự nhiên sử dụng các từ lực và tính chất trong định nghĩa của nó hoặc các thuật ngữ của định nghĩa dịch vụ sử dụng các quy tắc từ và dịch vụ trong đó các quy tắc đồng nghĩa với các thuật ngữ.
Korey Hinton

3
Tôi thích sử dụng thuật ngữ "tiêm phụ thuộc tự động" hoặc ADI thay vì IoC, vì lý do này; Bản thân Fowler đã đề cập rằng hầu hết các mô hình hiện tại trong lập trình đều có một số loại "đảo ngược điều khiển", trong đó thuật ngữ đó được định nghĩa chung là "việc loại bỏ các xác định của mã sẽ được thực thi và thời gian mà nó sẽ được thực thi với bên ngoài hệ thống kiểm soát, khi các quyết định đó theo truyền thống được thực hiện bởi chính chương trình ". Mọi thứ từ gián đoạn phần cứng đến lập trình hướng sự kiện đến các máy ảo và đa nhiệm đều xảy ra với IoC.
KeithS

Câu trả lời:


67

Giả sử bạn có một số loại "kho lưu trữ" và kho lưu trữ đó chịu trách nhiệm chuyển dữ liệu cho bạn từ một nguồn dữ liệu.

Kho lưu trữ có thể tự thiết lập kết nối với nguồn dữ liệu. Nhưng điều gì sẽ xảy ra nếu nó cho phép bạn chuyển qua kết nối tới nguồn dữ liệu thông qua hàm tạo của kho lưu trữ?

Bằng cách cho phép người gọi cung cấp kết nối, bạn đã tách riêng phần phụ thuộc kết nối nguồn dữ liệu khỏi lớp kho lưu trữ, cho phép mọi nguồn dữ liệu hoạt động với kho lưu trữ, không chỉ là kho lưu trữ chỉ định.

Bạn đã đảo ngược điều khiển bằng cách giao trách nhiệm tạo kết nối từ lớp kho lưu trữ đến người gọi.

Martin Fowler đề nghị sử dụng thuật ngữ "Dependency Injection" để mô tả loại Inversion of Control này, vì Inversion of Control như một khái niệm có thể được áp dụng rộng rãi hơn là chỉ tiêm phụ thuộc vào phương thức xây dựng.


3
Đây là một ví dụ về tiêm phụ thuộc, nhưng tiêm phụ thuộc chỉ là một tập hợp con của đảo ngược kiểm soát. Có những thứ khác hoàn toàn không liên quan đến tiêm phụ thuộc mà thực hành đảo ngược kiểm soát.
dsw88

1
@ mustang2009cobra: Cảm ơn. Một ví dụ là những gì tôi đã theo dõi, không phải là một danh mục toàn diện về các trường hợp xảy ra và thuật ngữ "Đảo ngược điều khiển" có liên quan chặt chẽ nhất với DI, không phải là thông điệp truyền đi hoặc các khái niệm tương tự khác.
Robert Harvey

Đúng, tiêm phụ thuộc là ví dụ phổ biến nhất của IoC, vì vậy một ví dụ về DI có ý nghĩa nhất. Có lẽ một thay đổi nhỏ đối với câu trả lời của bạn chỉ định rằng đây là ví dụ DI, loại IoC nổi bật nhất?
dsw88

1
Điều này thật đúng với gì mà tôi đã tìm kiếm! Rất nhiều ví dụ và định nghĩa về IoC không chỉ định rõ ràng điều khiển nào đang được đảo ngược. Tôi nhận ra rằng IoC có thể có nhiều thứ hơn là chỉ tiêm phụ thuộc, nhưng bây giờ thứ gì đó cuối cùng đã nhấp vào não tôi. Cảm ơn!
Korey Hinton

79

Tôi không nghĩ ai có thể giải thích điều đó tốt hơn Martin Fowler, tiếp tục đi sâu vào bài viết mà bạn liên kết .

Đối với giống container mới này, sự đảo ngược là về cách họ tra cứu việc thực hiện plugin. Trong ví dụ ngây thơ của tôi, người nghe đã tra cứu việc thực hiện công cụ tìm bằng cách trực tiếp khởi tạo nó. Điều này ngăn người tìm thấy là một plugin. Cách tiếp cận mà các thùng chứa này sử dụng là để đảm bảo rằng bất kỳ người dùng plugin nào cũng tuân theo một số quy ước cho phép một mô đun trình biên dịch mã riêng biệt đưa việc triển khai vào trình nghe.

Như ông giải thích trong các đoạn trên, điều này không hoàn toàn giống với lý do thuật ngữ "Đảo ngược kiểm soát" bắt nguồn.

Khi những container này nói về việc chúng rất hữu ích vì chúng thực hiện "Đảo ngược kiểm soát", tôi rất bối rối. Đảo ngược điều khiển là một đặc điểm chung của các khung, vì vậy nói rằng các thùng chứa nhẹ này là đặc biệt bởi vì chúng sử dụng đảo ngược điều khiển giống như nói xe của tôi đặc biệt vì nó có bánh xe.

Câu hỏi đặt ra là khía cạnh nào của sự kiểm soát mà họ đang đảo ngược? Khi tôi lần đầu tiên gặp phải sự đảo ngược của điều khiển, nó nằm trong sự kiểm soát chính của giao diện người dùng. Giao diện người dùng sớm được kiểm soát bởi chương trình ứng dụng. Bạn sẽ có một chuỗi các lệnh như "Nhập tên", "nhập địa chỉ"; chương trình của bạn sẽ lái các lời nhắc và nhận phản hồi cho từng người. Với các UI đồ họa (hoặc thậm chí dựa trên màn hình), khung UI sẽ chứa vòng lặp chính này và chương trình của bạn thay vào đó cung cấp các trình xử lý sự kiện cho các trường khác nhau trên màn hình. Kiểm soát chính của chương trình đã được đảo ngược, chuyển từ bạn sang khuôn khổ.

Đó là lý do tại sao anh ta tiếp tục sử dụng thuật ngữ "Dependency Injection" để thực hiện việc thực hiện Inversion of Control cụ thể này.

Kết quả là tôi nghĩ rằng chúng ta cần một tên cụ thể hơn cho mẫu này. Inversion of Control là một thuật ngữ quá chung chung và do đó mọi người thấy nó khó hiểu. Kết quả là có rất nhiều cuộc thảo luận với những người ủng hộ IoC khác nhau, chúng tôi đã giải quyết cái tên Dependency Injection.

Để làm rõ một chút: Inversion of Control có nghĩa là bất cứ điều gì đảo ngược cấu trúc điều khiển của chương trình từ thiết kế thủ tục cổ điển.

Trong những ngày mới, một ví dụ chính về điều này là cho phép một khung xử lý giao tiếp giữa UI và mã của bạn, thay vì để mã của bạn tạo UI trực tiếp.

Trong thời gian gần đây (khi các khung như vậy chiếm ưu thế khá nhiều, vì vậy câu hỏi không còn phù hợp nữa), một ví dụ đã đảo ngược sự kiểm soát đối với việc khởi tạo các đối tượng.

Fowler, và những người khác, đã quyết định rằng thuật ngữ Inversion of Control bao trùm quá nhiều kỹ thuật và chúng tôi cần một thuật ngữ mới cho ví dụ cụ thể về khởi tạo đối tượng (Dependency Injection), nhưng tại thời điểm thỏa thuận đã được đưa ra, cụm từ "Container IoC "Đã cất cánh.

Điều này làm vấy bẩn nước rất nhiều, bởi vì một thùng chứa IoC là một loại tiêm phụ thuộc cụ thể, nhưng Dependency Injection là một loại đảo ngược kiểm soát cụ thể. Đây là lý do tại sao bạn nhận được câu trả lời nhầm lẫn như vậy, bất kể bạn nhìn vào đâu.


4
Tôi thường không bỏ phiếu nhiều nhưng đây là một câu trả lời tuyệt vời. 1 phiếu sẽ không đủ. :(
RJD22

1
câu trả lời này thực sự trả lời tất cả các câu hỏi về sự nhầm lẫn của IoC và DI.
Ali Umair

32

Đây là các chương trình luồng điều khiển "thông thường" thường theo sau:

  • Chạy các lệnh tuần tự
  • Bạn duy trì quyền kiểm soát luồng điều khiển của chương trình

Đảo ngược điều khiển "đảo ngược" điều khiển dòng chảy, có nghĩa là nó lật nó trên đầu:

  • Chương trình của bạn không kiểm soát dòng chảy nữa. Thay vì gọi các lệnh khi bạn thấy phù hợp, bạn chờ người khác gọi cho bạn .

Dòng cuối cùng là một trong những quan trọng. Thay vì gọi cho người khác khi bạn cảm thấy thích, người khác gọi cho bạn khi họ cảm thấy thích.

Một ví dụ phổ biến về điều này là các khung web như Rails. Bạn xác định Bộ điều khiển, nhưng bạn thực sự không quyết định khi nào chúng được gọi. Rails gọi cho họ khi nó quyết định có nhu cầu.


19
[chèn trò đùa "bắt buộc" ở Liên Xô tại đây]
Robert Harvey

2
Đùa sang một bên, những gì tôi nghĩ rằng bạn đang mô tả giống như thông điệp truyền đi, vốn là một yếu tố chính của OO trong nhiều thập kỷ.
Robert Harvey

3
@JimmyHoffa - Điều này không sai chút nào - xem tại đây . Đảo ngược kiểm soát là một khái niệm tổng quát hơn so với việc tạo ra các phụ thuộc và tiêm phụ thuộc chỉ là một dạng của IoC.
Lee

7
@JimmyHoffa: Tôi không đồng ý. Đây là một định nghĩa chính xác về Inversion of Control giống như câu trả lời của bạn hoặc của Robert, đó chính xác là sự nhầm lẫn mà Fowler mô tả khi đặt cụm từ Dependency Injection (đó là những gì bạn đang gọi cả IoC).
pdr

5
Tôi đồng ý với tuyên bố này của @pdr: "Inversion of Control có nghĩa là bất cứ điều gì đảo ngược cấu trúc điều khiển của chương trình từ thiết kế thủ tục cổ điển." Đó là những gì tôi đang mô tả, và đó là sự đảo ngược chung của kiểm soát. Tiêm phụ thuộc là một loại IoC, nhưng đó không phải là điều duy nhất thực hành IoC
dsw88

13

Đó là về người kiểm soát việc khởi tạo các phụ thuộc.

Theo truyền thống, khi một lớp / phương thức cần sử dụng một lớp khác (phụ thuộc), nó được khởi tạo trực tiếp bởi lớp / phương thức. Nó kiểm soát sự phụ thuộc của nó.

Với Inversion of Control (IoC), người gọi đã chuyển qua phần phụ thuộc, do đó nó tạo ra sự phụ thuộc. Người gọi kiểm soát các phụ thuộc.

Kiểm soát nơi mà một phụ thuộc được khởi tạo đã được đảo ngược - thay vì nằm ở "đáy", nơi mã cần, nó được khởi tạo ở "trên cùng", nơi mã cần được gọi.


1
Không đơn giản tiếng Anh. Thất bại.
Robert Harvey

2
@RobertHarvey - Đồng bằng như thế nào?
Oded

4
@RobertHarvey Huh? Điều này có vẻ khá đơn giản đối với tôi ... Điều gì không đơn giản? Từ "khởi tạo" hay "phụ thuộc"?
Jimmy Hoffa

@JimmyHoffa: Xem câu trả lời của tôi, trong đó cung cấp một ví dụ cụ thể. IoC là một trong những thuật ngữ rắc rối mà mọi người sử dụng nhưng không ai giải thích được; mọi người sử dụng các bộ chứa IoC trong các chương trình không yêu cầu chúng vì chúng hiểu sai. Điều đó nói rằng, tôi nghĩ rằng câu trả lời này có một vài chỉnh sửa ninja. :)
Robert Harvey

Điều này và bài viết được @ dsw88 ở trên khá đơn giản đối với tôi. Hãy nhìn xem, nếu tôi chưa bao giờ nghe nói về IoC, tôi sẽ trực giác nghĩ rằng "Ồ, nơi mà ai sẽ kiểm soát thứ gì đó lật từ bên này sang bên kia". Tốt công việc này!
Oliver Williams

2

Thông thường các cuộc gọi mã cấp cao hơn (tức là các điều khiển) mã cấp thấp hơn. Hàm gọi () hàm function (), function () gọi libraryFunction ().

Điều đó có thể được đảo ngược, do đó, chức năng thư viện cấp thấp ở phía dưới gọi các hàm cấp cao hơn.

Tại sao bạn lại làm vậy? Middleware. Đôi khi bạn muốn kiểm soát cấp cao nhất và cấp dưới, nhưng có rất nhiều công việc ở giữa mà bạn không muốn làm. Hãy thực hiện quicksort trong C stdlib . Bạn gọi quicksort ở cấp cao nhất. Bạn trao cho qsort () một con trỏ hàm cho hàm riêng của bạn thực hiện một bộ so sánh trên bất cứ thứ gì bạn cảm thấy thích. Khi qsort () được gọi, nó gọi hàm so sánh này. qsort () đang điều khiển / gọi / điều khiển chức năng cấp cao của bạn.


1

Đây là một tổng quan đơn giản:

  1. Kiểm soát đề cập đến những gì chương trình làm tiếp theo
  2. Ở cấp độ cao nhất, thường có hai điều khiển điều khiển: chính ứng dụng và người dùng

Vào thời xa xưa, quyền kiểm soát được sở hữu bởi ứng dụng trước và người dùng thứ hai. Nếu ứng dụng cần thứ gì đó từ người dùng, nó sẽ dừng lại và hỏi và sau đó chuyển sang nhiệm vụ tiếp theo. Tương tác của người dùng cung cấp chủ yếu dữ liệu thay vì kiểm soát những gì ứng dụng đã làm tiếp theo. Điều này hơi xa lạ với chúng ta ngày nay vì chúng ta không thấy loại hành vi này rất thường xuyên.

Nếu chúng ta chuyển đổi xung quanh và cung cấp cho người dùng điều khiển chính, thì chúng ta đã đảo ngược điều khiển. Điều này có nghĩa là thay vì người dùng chờ đợi ứng dụng để cung cấp cho nó một cái gì đó để làm, thì ứng dụng nằm xung quanh chờ người dùng cung cấp cho nó một cái gì đó để làm. GUI là một ví dụ tuyệt vời về điều này và hầu như mọi thứ với vòng lặp sự kiện đều đảo ngược điều khiển.

Lưu ý rằng ví dụ của tôi ở cấp cao nhất và khái niệm đảo ngược điều khiển này có thể được trừu tượng hóa thành các lớp kiểm soát khác nhau trong ứng dụng (tức là tiêm phụ thuộc). Đây có thể là lý do tại sao rất khó để có được một câu trả lời thẳng.


0

Chúng ta hãy cố gắng hiểu nó thông qua hai ví dụ.

ví dụ 1

Trong những ngày trước, các ứng dụng được sử dụng để tạo lời nhắc lệnh chấp nhận đầu vào của người dùng lần lượt. Ngày nay, các khung công tác UI khởi tạo các thành phần UI khác nhau, lặp qua các sự kiện khác nhau của các thành phần UI đó (như di chuột, nhấp chuột, v.v.) và các chương trình người dùng / chính cung cấp các hook (ví dụ: trình nghe sự kiện UI trong Java) để nghe các sự kiện đó. Vì vậy, "điều khiển" luồng phần tử UI chính được chuyển từ chương trình người dùng sang khung UI. Trong những ngày trước, nó là trong chương trình người dùng.

Ví dụ 2

Xem xét lớp CustomerProcessordưới đây:

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}

Nếu tôi muốn processCustomer()độc lập với bất kỳ triển khai nào getAllCusts(), không chỉ là do SqlCustRepotôi cung cấp , tôi sẽ cần phải thoát khỏi dòng: SqlCustRepo custRepo = new SqlCustRepo()và thay thế nó bằng một cái gì đó chung chung hơn, có khả năng chấp nhận loại thực hiện khác nhau, như vậy processCustomers()sẽ đơn giản hoạt động bất kỳ thực hiện được cung cấp. Mã ở trên (khởi tạo lớp bắt buộc SqlCustRepobằng logic chương trình chính) là một cách truyền thống và nó không đạt được mục tiêu tách rời processCustomers()khỏi việc thực hiện getAllCusts(). Khi đảo ngược điều khiển, bộ chứa khởi tạo lớp triển khai được yêu cầu (như được chỉ định bởi, nói cấu hình xml), đưa nó vào logic chương trình chính bị ràng buộc theo móc nối được chỉ định (nói theo @Autowiredchú thích hoặc getBean()phương thức trong khung mùa xuân).

Hãy xem làm thế nào điều này có thể được thực hiện. Xem xét mã dưới đây.

Cấu hình

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>

CustRepo.java

interface ICustRepo 
{ ... }

JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }

App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}

Chúng ta cũng có thể có

class GraphCustRepo implements ICustRepo { ... }   

<bean id="custRepo" class="GraphCustRepo">

và chúng tôi sẽ không cần thay đổi App.java.

Phía trên thùng chứa (là khung mùa xuân) có trách nhiệm quét tệp xml, khởi tạo bean loại cụ thể và đưa nó vào chương trình người dùng. Chương trình người dùng không có quyền kiểm soát lớp nào được khởi tạo.

PS: IoC là khái niệm chung và đạt được theo nhiều cách. Trên ví dụ đạt được nó bằng cách tiêm phụ thuộc.

Tham khảo: Bài viết của Martin Fowler .

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.