Lợi ích của hộp chứa tiêm phụ thuộc là gì?


104

Tôi hiểu lợi ích của chính việc tiêm phụ thuộc. Hãy lấy Spring làm ví dụ. Tôi cũng hiểu lợi ích của các tính năng khác của Spring như AOP, các loại trợ giúp khác nhau, v.v. Tôi chỉ tự hỏi, cấu hình XML có những lợi ích gì, chẳng hạn như:

<bean id="Mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
  <property name="girlfriend" ref="Mary"/>
</bean>

so với mã java cũ thuần túy như:

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

được gỡ lỗi dễ dàng hơn, kiểm tra thời gian biên dịch và có thể hiểu được bởi bất kỳ ai chỉ biết java. Vậy mục đích chính của khung tiêm phụ thuộc là gì? (hoặc một đoạn mã cho thấy lợi ích của nó.)


CẬP NHẬT:
Trong trường hợp

IService myService;// ...
public void doSomething() {  
  myService.fetchData();
}

Làm cách nào IoC framework có thể đoán được việc triển khai myService nào mà tôi muốn được đưa vào nếu có nhiều hơn một? Nếu chỉ có một lần triển khai của giao diện đã cho và tôi để vùng chứa IoC tự động quyết định sử dụng nó, nó sẽ bị hỏng sau khi lần triển khai thứ hai xuất hiện. Và nếu cố tình chỉ có một giao diện được triển khai thì bạn không cần phải đưa nó vào.

Sẽ thực sự thú vị khi thấy một đoạn cấu hình nhỏ cho IoC cho thấy lợi ích của nó. Tôi đã sử dụng Spring được một thời gian và tôi không thể cung cấp ví dụ như vậy. Và tôi có thể hiển thị các dòng đơn thể hiện lợi ích của hibernate, dwr và các khung công tác khác mà tôi sử dụng.


CẬP NHẬT 2:
Tôi nhận thấy rằng cấu hình IoC có thể được thay đổi mà không cần biên dịch lại. Nó thực sự là một ý tưởng tốt? Tôi có thể hiểu khi ai đó muốn thay đổi thông tin đăng nhập DB mà không cần biên dịch lại - người đó có thể không phải là nhà phát triển. Trong thực tế của bạn, bao lâu một người khác không phải nhà phát triển thay đổi cấu hình IoC? Tôi nghĩ rằng đối với các nhà phát triển không có nỗ lực để biên dịch lại lớp cụ thể đó thay vì thay đổi cấu hình. Và đối với những người không phải nhà phát triển, bạn có thể muốn làm cho cuộc sống của anh ta dễ dàng hơn và cung cấp một số tệp cấu hình đơn giản hơn.


CẬP NHẬT 3:

Cấu hình bên ngoài của ánh xạ giữa các giao diện và triển khai cụ thể của chúng

Điều gì là tốt để làm cho nó mở rộng? Bạn không đặt tất cả mã của mình ra bên ngoài, trong khi bạn chắc chắn có thể - chỉ cần đặt nó vào tệp ClassName.java.txt, đọc và biên dịch theo cách thủ công ngay lập tức - thật tuyệt, bạn đã tránh được việc biên dịch lại. Tại sao nên tránh biên dịch ?!

Bạn tiết kiệm thời gian viết mã vì bạn cung cấp ánh xạ một cách khai báo, không phải trong mã thủ tục

Tôi hiểu rằng đôi khi cách tiếp cận khai báo tiết kiệm thời gian. Ví dụ: tôi chỉ khai báo một lần ánh xạ giữa thuộc tính bean và cột DB và chế độ ngủ đông sử dụng ánh xạ này trong khi tải, lưu, xây dựng SQL dựa trên HSQL, v.v. Đây là nơi phương pháp khai báo hoạt động. Trong trường hợp Spring (trong ví dụ của tôi), khai báo có nhiều dòng hơn và có cùng biểu cảm như mã tương ứng. Nếu có một ví dụ khi khai báo như vậy ngắn hơn mã - tôi muốn xem nó.

Nguyên tắc Inversion of Control cho phép dễ dàng kiểm tra đơn vị vì bạn có thể thay thế các triển khai thực bằng các triển khai giả (như thay thế cơ sở dữ liệu SQL bằng một trong bộ nhớ)

Tôi hiểu sự nghịch đảo của lợi ích kiểm soát (Tôi thích gọi mẫu thiết kế được thảo luận ở đây là Dependency Injection, vì IoC tổng quát hơn - có nhiều loại kiểm soát và chúng tôi chỉ đảo ngược một trong số chúng - kiểm soát khởi tạo). Tôi đã hỏi tại sao ai đó lại cần một thứ gì đó khác ngoài ngôn ngữ lập trình cho nó. Tôi chắc chắn có thể thay thế các triển khai thực bằng các triển khai giả bằng cách sử dụng mã. Và mã này sẽ thể hiện điều tương tự như cấu hình - nó sẽ chỉ khởi tạo các trường có giá trị giả.

mary = new FakeFemale();

Tôi hiểu lợi ích của DI. Tôi không hiểu những lợi ích nào được thêm vào bởi cấu hình XML bên ngoài so với cấu hình mã tương tự. Tôi không nghĩ rằng nên tránh việc biên dịch - tôi biên dịch hàng ngày và tôi vẫn còn sống. Tôi nghĩ rằng cấu hình của DI là một ví dụ xấu về cách tiếp cận khai báo. Khai báo có thể hữu ích nếu được khai báo một lần VÀ được sử dụng nhiều lần theo nhiều cách khác nhau - như hibernate cfg, nơi ánh xạ giữa thuộc tính bean và cột DB được sử dụng để lưu, tải, xây dựng truy vấn tìm kiếm, v.v. Cấu hình Spring DI có thể dễ dàng dịch sang cấu hình mã, giống như trong đầu của câu hỏi này, nó có thể không? Và nó chỉ được sử dụng để khởi tạo bean, phải không? Có nghĩa là một cách tiếp cận khai báo không thêm bất cứ điều gì ở đây, phải không?

Khi tôi khai báo ánh xạ ngủ đông, tôi chỉ cung cấp một số thông tin cho chế độ ngủ đông và nó hoạt động dựa trên thông tin đó - tôi không cho nó biết phải làm gì. Trong trường hợp mùa xuân, tuyên bố của tôi cho biết chính xác mùa xuân phải làm - vậy tại sao phải khai báo, tại sao không chỉ làm?


CẬP NHẬT CUỐI CÙNG: Các bạn
ơi, rất nhiều câu trả lời đang nói với tôi về việc tiêm thuốc phụ thuộc mà tôi BIẾT LÀ TỐT. Câu hỏi là về mục đích của cấu hình DI thay vì khởi tạo mã - Tôi có xu hướng nghĩ rằng việc khởi tạo mã ngắn hơn và rõ ràng hơn. Câu trả lời duy nhất mà tôi nhận được cho đến nay cho câu hỏi của mình là nó tránh biên dịch lại khi cấu hình thay đổi. Tôi đoán tôi nên đăng một câu hỏi khác, bởi vì nó là một bí mật lớn đối với tôi, tại sao việc biên dịch lại nên tránh trong trường hợp này.


21
Cuối cùng cũng có người đủ can đảm để hỏi câu này. Tại sao bạn thực sự muốn tránh biên dịch lại khi việc triển khai của bạn thay đổi với cái giá phải trả là hy sinh (hoặc ít nhất là làm giảm giá trị) công cụ / hỗ trợ IDE?
Christian Klauser,

3
Có vẻ như tiêu đề không đúng lắm. Tác giả đã nói rằng vùng chứa IOC là tốt, nhưng dường như có vấn đề với việc sử dụng cấu hình XML thay vì cấu hình thông qua mã (và đủ công bằng). Tôi có lẽ đề nghị rằng "Lợi ích của việc định cấu hình vùng chứa IOC thông qua XML hoặc các phương pháp tiếp cận không mã khác là gì?"
Orion Edwards,

@Orion Ví dụ tôi đã cung cấp (với Nam và Nữ) không yêu cầu bất kỳ vùng chứa IOC nào. Tôi ổn với IOC; sử dụng vùng chứa cho dù nó được cấu hình bằng XML hay không, vẫn là một câu hỏi mở đối với tôi.
Pavel Feldman

@ Tiêu chí 2: Trong khi tôi sử dụng một số hình thức IOC trong hầu hết các dự án, một số trong số họ được hưởng lợi từ IOC container nhiều như họ được hưởng lợi từ Vùng chứa chuyển nhượng có thể thay đổi hoặc Vùng chứa câu lệnh - chỉ cần ngôn ngữ là đủ đối với tôi. Tôi không gặp vấn đề gì khi biên dịch lại các dự án mà tôi đang làm và việc mã khởi tạo dev / test / production được phân tách một cách thuận tiện. Vì vậy, đối với tôi tiêu đề là tốt.
Pavel Feldman

Tôi thấy rắc rối với mẫu. Trên nguyên tắc là dịch vụ Tiêm, không phải dữ liệu
Jacek Cz

Câu trả lời:


40

Đối với bản thân tôi, một trong những lý do chính để sử dụng IoC (và sử dụng cấu hình bên ngoài) là xung quanh hai lĩnh vực:

  • Thử nghiệm
  • Bảo trì sản xuất

Thử nghiệm

Nếu bạn chia thử nghiệm của mình thành 3 tình huống (điều này khá bình thường trong phát triển quy mô lớn):

  1. Kiểm tra đơn vị
  2. Thử nghiệm hội nhập
  3. Kiểm tra hộp đen

Những gì bạn sẽ muốn làm là đối với hai kịch bản thử nghiệm cuối cùng (Tích hợp & Hộp đen), không biên dịch lại bất kỳ phần nào của ứng dụng.

Nếu bất kỳ tình huống thử nghiệm nào của bạn yêu cầu bạn thay đổi cấu hình (tức là: sử dụng một thành phần khác để bắt chước tích hợp ngân hàng hoặc thực hiện tải hiệu suất), điều này có thể dễ dàng xử lý (điều này mang lại lợi ích của việc định cấu hình mặt DI của IoC mặc dù.

Ngoài ra, nếu ứng dụng của bạn được sử dụng tại nhiều trang web (với máy chủ và cấu hình thành phần khác nhau) hoặc có cấu hình thay đổi trên môi trường trực tiếp, bạn có thể sử dụng các giai đoạn kiểm tra sau để xác minh rằng ứng dụng sẽ xử lý những thay đổi đó.

Sản xuất

Là nhà phát triển, bạn không (và không nên) có quyền kiểm soát môi trường sản xuất (đặc biệt khi ứng dụng của bạn đang được phân phối cho nhiều khách hàng hoặc các trang web riêng biệt), đối với tôi đây là lợi ích thực sự của việc sử dụng cả IoC và cấu hình bên ngoài , tùy thuộc vào cơ sở hạ tầng / hỗ trợ sản xuất để tinh chỉnh và điều chỉnh môi trường sống mà không cần phải quay lại nhà phát triển và thông qua thử nghiệm (chi phí cao hơn khi tất cả những gì họ muốn làm là di chuyển một thành phần).

Tóm lược

Những lợi ích chính mà cấu hình bên ngoài của IoC đến từ việc cung cấp cho người khác (không phải nhà phát triển) quyền định cấu hình ứng dụng của bạn, theo kinh nghiệm của tôi, điều này chỉ hữu ích trong một số trường hợp hạn chế:

  • Ứng dụng được phân phối đến nhiều trang web / khách hàng, nơi các môi trường sẽ khác nhau.
  • Kiểm soát phát triển / đầu vào hạn chế đối với môi trường sản xuất và thiết lập.
  • Các kịch bản thử nghiệm.

Trong thực tế, tôi nhận thấy rằng ngay cả khi phát triển một thứ gì đó mà bạn có quyền kiểm soát môi trường, nó sẽ được chạy, theo thời gian, tốt hơn là cung cấp cho người khác khả năng thay đổi cấu hình:

  • Khi phát triển, bạn không biết khi nào nó sẽ thay đổi (ứng dụng rất hữu ích mà công ty của bạn bán nó cho người khác).
  • Tôi không muốn bị mắc kẹt với việc thay đổi mã mỗi khi yêu cầu một thay đổi nhỏ mà lẽ ra có thể được xử lý bằng cách thiết lập và sử dụng mô hình cấu hình tốt.

Lưu ý: Ứng dụng đề cập đến giải pháp hoàn chỉnh (không chỉ tệp thực thi), vì vậy tất cả các tệp cần thiết để ứng dụng chạy .


14

Dependency injection là một phong cách mã hóa có nguồn gốc từ quan sát rằng việc ủy ​​quyền đối tượng thường là một mẫu thiết kế hữu ích hơn là kế thừa đối tượng (nghĩa là, đối tượng có quan hệ hữu ích hơn đối tượng là quan hệ). Tuy nhiên, một thành phần khác cần thiết để DI hoạt động, đó là tạo giao diện đối tượng. Kết hợp hai mẫu thiết kế mạnh mẽ này, các kỹ sư phần mềm nhanh chóng nhận ra rằng họ có thể tạo ra mã được ghép nối lỏng lẻo linh hoạt và do đó khái niệm Dependency Injection ra đời. Tuy nhiên, phải đến khi phản xạ đối tượng có sẵn trong một số ngôn ngữ cấp cao nhất định, DI mới thực sự thành công. Thành phần phản chiếu là cốt lõi của hầu hết ngày nay '

Một ngôn ngữ phải hỗ trợ tốt cho cả các kỹ thuật lập trình Hướng đối tượng thông thường cũng như hỗ trợ cho các giao diện đối tượng và phản xạ đối tượng (ví dụ Java và C #). Mặc dù bạn có thể xây dựng các chương trình bằng cách sử dụng các mẫu DI trong hệ thống C ++, nhưng việc thiếu hỗ trợ phản chiếu trong ngôn ngữ thích hợp sẽ ngăn nó hỗ trợ các máy chủ ứng dụng và các nền tảng DI khác và do đó hạn chế khả năng biểu đạt của các mẫu DI.

Điểm mạnh của một hệ thống được xây dựng bằng các mẫu DI:

  1. Mã DI dễ sử dụng lại hơn nhiều vì chức năng 'phụ thuộc' được ngoại suy thành các giao diện được xác định rõ, cho phép các đối tượng riêng biệt có cấu hình được xử lý bởi nền tảng ứng dụng phù hợp được cắm vào các đối tượng khác theo ý muốn.
  2. Mã DI dễ kiểm tra hơn nhiều. Chức năng được thể hiện bởi đối tượng có thể được kiểm tra trong hộp đen bằng cách xây dựng các đối tượng 'mô phỏng' triển khai các giao diện được mong đợi bởi logic ứng dụng của bạn.
  3. Mã DI linh hoạt hơn. Nó là mã được kết hợp lỏng lẻo bẩm sinh - đến mức cực đoan. Điều này cho phép lập trình viên chọn và chọn cách các đối tượng được kết nối chỉ dựa trên các giao diện yêu cầu của chúng ở một đầu và các giao diện thể hiện của chúng ở đầu kia.
  4. Cấu hình bên ngoài (Xml) của các đối tượng DI có nghĩa là những người khác có thể tùy chỉnh mã của bạn theo các hướng không lường trước được.
  5. Cấu hình bên ngoài cũng là một sự tách biệt của mô hình quan tâm trong đó tất cả các vấn đề về khởi tạo đối tượng và quản lý sự phụ thuộc lẫn nhau đối tượng có thể được xử lý bởi máy chủ ứng dụng.
  6. Lưu ý rằng cấu hình bên ngoài không bắt buộc phải sử dụng mẫu DI, đối với các kết nối đơn giản, một đối tượng trình tạo nhỏ thường là đủ. Có một sự cân bằng về tính linh hoạt giữa hai điều này. Đối tượng trình tạo không phải là một tùy chọn linh hoạt như một tệp cấu hình hiển thị bên ngoài. Nhà phát triển hệ thống DI phải cân nhắc giữa lợi thế của tính linh hoạt so với sự tiện lợi, lưu ý rằng quy mô nhỏ, kiểm soát hạt mịn đối với cấu trúc đối tượng như được thể hiện trong tệp cấu hình có thể làm tăng sự nhầm lẫn và chi phí bảo trì xuống dòng.

Chắc chắn mã DI có vẻ cồng kềnh hơn, nhược điểm của việc có tất cả các tệp XML cấu hình các đối tượng được đưa vào các đối tượng khác là khó khăn. Tuy nhiên, đây là điểm của hệ thống DI. Khả năng trộn và kết hợp các đối tượng mã của bạn dưới dạng một loạt cài đặt cấu hình cho phép bạn xây dựng các hệ thống phức tạp bằng cách sử dụng mã của bên thứ 3 với phần mã hóa tối thiểu.

Ví dụ được cung cấp trong câu hỏi chỉ chạm vào bề mặt của sức mạnh biểu đạt mà một thư viện đối tượng DI được phân tích nhân tố đúng cách có thể cung cấp. Với một số thực hành và kỷ luật bản thân, hầu hết các học viên DI thấy rằng họ có thể xây dựng các hệ thống có phạm vi kiểm tra 100% mã ứng dụng. Chỉ một điểm này thôi đã là phi thường. Đây không phải là phạm vi kiểm tra 100% của một ứng dụng nhỏ gồm vài trăm dòng mã, mà là phạm vi kiểm tra 100% của các ứng dụng bao gồm hàng trăm nghìn dòng mã. Tôi không thể mô tả bất kỳ mẫu thiết kế nào khác cung cấp mức độ kiểm tra này.

Bạn đúng ở chỗ một ứng dụng chỉ gồm 10 dòng mã dễ hiểu hơn một số đối tượng cộng với một loạt tệp cấu hình XML. Tuy nhiên, giống như hầu hết các mẫu thiết kế mạnh mẽ, lợi ích được tìm thấy khi bạn tiếp tục thêm các tính năng mới vào hệ thống.

Tóm lại, các ứng dụng dựa trên DI quy mô lớn vừa dễ gỡ lỗi vừa dễ hiểu hơn. Mặc dù cấu hình Xml không được 'kiểm tra thời gian biên dịch', tất cả các dịch vụ ứng dụng mà tác giả này biết sẽ cung cấp cho nhà phát triển thông báo lỗi nếu họ cố gắng đưa một đối tượng có giao diện không tương thích vào một đối tượng khác. Và hầu hết đều cung cấp tính năng 'kiểm tra' bao gồm tất cả các cấu hình đối tượng đã biết. Điều này được thực hiện dễ dàng và nhanh chóng bằng cách kiểm tra xem đối tượng được đưa vào A có triển khai giao diện theo yêu cầu của đối tượng B cho tất cả các lần tiêm đối tượng được cấu hình hay không.


4
hiểu lợi ích của BHTG. Tôi không hiểu những lợi ích nào được thêm vào bởi cấu hình XML bên ngoài so với việc cấu hình mã hoạt động tương tự. Lợi ích bạn đề cập được cung cấp bởi mẫu thiết kế DI. Câu hỏi là về lợi ích của cấu hình DI so với mã khởi tạo đơn giản.
Pavel Feldman

> Cấu hình bên ngoài cũng là một sự tách biệt ... Tách cấu hình là trái tim của DI là tốt. Và nó có thể de vòm bằng cách sử dụng mã khởi tạo. Cfg bổ sung gì so với khởi tạo mã? Đối với tôi, dường như mỗi dòng cfg có dòng mã khởi tạo tương ứng.
Pavel Feldman

7

Đây là một câu hỏi hơi phức tạp, nhưng tôi có xu hướng đồng ý rằng số lượng lớn cấu hình xml không thực sự mang lại nhiều lợi ích. Tôi muốn các ứng dụng của mình càng ít phụ thuộc càng tốt, bao gồm cả các khung công tác khổng lồ.

Họ đơn giản hóa mã rất nhiều lần, nhưng chúng cũng có một chi phí phức tạp khiến việc theo dõi các vấn đề trở nên khó khăn hơn (tôi đã thấy những vấn đề như vậy đầu tiên và Java thẳng thắn, tôi sẽ thấy thoải mái hơn rất nhiều).

Tôi đoán nó phụ thuộc vào phong cách một chút, và bạn cảm thấy thoải mái với điều gì ... bạn có muốn sử dụng giải pháp của riêng mình và có lợi ích khi biết nó từ trong ra ngoài hay dựa trên các giải pháp hiện có có thể khó khăn khi cấu hình không phù hợp. t vừa phải? Tất cả chỉ là sự đánh đổi.

Tuy nhiên, cấu hình XML là một phần nhỏ của tôi ... Tôi cố gắng tránh nó bằng mọi giá.


5

Bất cứ lúc nào bạn có thể thay đổi mã của mình thành dữ liệu, bạn đang thực hiện một bước đúng hướng.

Mã hóa bất kỳ thứ gì dưới dạng dữ liệu có nghĩa là bản thân mã của bạn tổng quát hơn và có thể tái sử dụng. Điều đó cũng có nghĩa là dữ liệu của bạn có thể được chỉ định bằng ngôn ngữ phù hợp chính xác với nó.

Ngoài ra, một tệp XML có thể được đọc thành GUI hoặc một số công cụ khác và dễ dàng thao tác một cách thực dụng. Làm thế nào bạn sẽ làm điều đó với ví dụ mã?

Tôi liên tục tính toán những thứ mà hầu hết mọi người sẽ triển khai dưới dạng mã thành dữ liệu, nó làm cho những gì mã còn lại sạch hơn NHIỀU. Tôi thấy không thể tưởng tượng nổi rằng mọi người sẽ tạo một menu bằng mã chứ không phải dưới dạng dữ liệu - rõ ràng là thực hiện nó trong mã hoàn toàn là sai vì bảng soạn sẵn.


có ý nghĩa, không suy nghĩ về nó trong quan điểm này
Pavel Feldman

7
sau đó một lần nữa, người ta thường đi theo con đường khác và cố gắng và đưa logic vào dữ liệu mà chỉ có nghĩa là bạn kết thúc mã hóa ứng dụng của bạn trong một ngôn ngữ lập trình kém chất lượng
Casebash

@Casebash Đó là một quan điểm thú vị - tôi sẽ rất hứng thú với một ví dụ. Tôi thấy rằng bất cứ thứ gì tôi có thể chuyển vào dữ liệu đều hữu ích. Tôi cũng thấy rằng nếu tôi làm đúng như những gì bạn đang nói, thì ngôn ngữ này thực sự được cải thiện bởi vì nó là DSL - nhưng ngay cả khi đó cũng cần có sự biện minh nghiêm túc để tạo ra một ngôn ngữ hoàn toàn mới.
Bill K

1
"Bất kỳ lúc nào bạn có thể thay đổi mã của mình thành dữ liệu, bạn đang thực hiện một bước đúng hướng." Chào mừng bạn đến với kiểu chống Mã hóa mềm.
Raedwald

@Raedwald Những gì bạn đang nói đến là ngoại hóa có thể thực sự khó khăn nếu bạn không biết mình đang làm gì (và lý do khiến ai đó không đủ năng lực đã thử nó, thất bại và gọi nó là phản mẫu) Các ví dụ tích cực hơn sẽ là Injection, Iterator , gần như bất kỳ thứ gì có chú thích, bất kỳ thứ gì khởi tạo bằng mảng. Hầu hết các cấu trúc lập trình tuyệt vời là một nỗ lực để tách ra sự khác biệt trong mã của bạn và kết hợp những gì còn lại, thúc đẩy nó với thứ gì đó có thể được nhóm và quản lý tốt hơn.
Bill K

3

Lý do sử dụng DI container là bạn không cần phải định cấu hình trước hàng tỷ thuộc tính trong mã của mình mà chỉ đơn giản là getters và setters. Bạn có thực sự muốn mã hóa cứng tất cả những gì có X () mới không? Chắc chắn, bạn có thể có mặc định, nhưng vùng chứa DI cho phép tạo các tệp đơn cực kỳ dễ dàng và cho phép bạn tập trung vào các chi tiết của mã chứ không phải tác vụ linh tinh khi khởi tạo nó.

Ví dụ: Spring cho phép bạn triển khai giao diện InitializingBean và thêm một phương thức afterPropertiesSet (bạn cũng có thể chỉ định một "init-method" để tránh ghép mã của bạn với Spring). Các phương thức này sẽ cho phép bạn đảm bảo rằng bất kỳ giao diện nào được chỉ định làm trường trong cá thể lớp của bạn đều được định cấu hình chính xác khi khởi động và sau đó bạn không còn phải kiểm tra bộ cài và bộ cài của mình nữa (giả sử bạn cho phép các đĩa đơn của mình vẫn an toàn cho chuỗi ).

Hơn nữa, việc khởi tạo phức tạp với DI container sẽ dễ dàng hơn nhiều thay vì tự mình thực hiện chúng. Ví dụ, tôi hỗ trợ sử dụng XFire (không phải CeltiXFire, chúng tôi chỉ sử dụng Java 1.4). Ứng dụng đã sử dụng Spring, nhưng không may là nó đã sử dụng cơ chế cấu hình services.xml của XFire. Khi một Tập hợp các phần tử cần khai báo rằng nó có KHÔNG hoặc nhiều bản sao thay vì MỘT hoặc nhiều bản sao, tôi phải ghi đè một số mã XFire được cung cấp cho dịch vụ cụ thể này.

Có một số giá trị mặc định XFire được xác định trong lược đồ Spring bean của nó. Vì vậy, nếu chúng ta đang sử dụng Spring để cấu hình các dịch vụ, các bean có thể đã được sử dụng. Thay vào đó, điều đã xảy ra là tôi phải cung cấp một phiên bản của một lớp cụ thể trong tệp services.xml thay vì sử dụng bean. Để làm được điều này, tôi cần cung cấp hàm tạo và thiết lập các tham chiếu được khai báo trong cấu hình XFire. Thay đổi thực sự mà tôi cần thực hiện bắt buộc tôi phải nạp chồng một lớp duy nhất.

Tuy nhiên, nhờ tệp services.xml, tôi đã phải tạo bốn lớp mới, đặt giá trị mặc định của chúng theo giá trị mặc định trong tệp cấu hình Spring trong hàm tạo của chúng. Nếu chúng tôi có thể sử dụng cấu hình Spring, tôi có thể chỉ nêu:

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

Thay vào đó, nó trông giống như thế này:

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

Vì vậy, kết quả thực là bốn lớp Java bổ sung, chủ yếu là vô nghĩa phải được thêm vào cơ sở mã để đạt được ảnh hưởng mà một lớp bổ sung và một số thông tin vùng chứa phụ thuộc đơn giản đạt được. Đây không phải là "ngoại lệ chứng minh quy tắc", đây là quy tắc ... việc xử lý các câu hỏi trong mã sẽ rõ ràng hơn nhiều khi các thuộc tính đã được cung cấp trong vùng chứa DI và bạn chỉ cần thay đổi chúng cho phù hợp với một tình huống đặc biệt, điều này xảy ra thường xuyên hơn không.


3

Tôi có câu trả lời của bạn

Rõ ràng là có sự đánh đổi trong mỗi cách tiếp cận, nhưng các tệp cấu hình XML bên ngoài rất hữu ích cho việc phát triển doanh nghiệp, trong đó hệ thống xây dựng được sử dụng để biên dịch mã chứ không phải IDE của bạn. Sử dụng hệ thống xây dựng, bạn có thể muốn đưa các giá trị nhất định vào mã của mình - ví dụ như phiên bản của bản dựng (có thể gây khó khăn khi phải cập nhật thủ công mỗi lần bạn biên dịch). Nỗi đau lớn hơn khi hệ thống xây dựng của bạn lấy mã khỏi một số hệ thống kiểm soát phiên bản. Việc sửa đổi các giá trị đơn giản tại thời điểm biên dịch sẽ yêu cầu bạn thay đổi tệp, cam kết, biên dịch và sau đó hoàn nguyên mỗi lần cho mỗi thay đổi. Đây không phải là những thay đổi mà bạn muốn thực hiện trong kiểm soát phiên bản của mình.

Các trường hợp sử dụng hữu ích khác liên quan đến hệ thống xây dựng và cấu hình bên ngoài:

  • tiêm các kiểu / bảng định kiểu cho một cơ sở mã duy nhất cho các bản dựng khác nhau
  • đưa các tập hợp nội dung động khác nhau (hoặc các tham chiếu đến chúng) cho cơ sở mã duy nhất của bạn
  • đưa ngữ cảnh bản địa hóa cho các bản dựng / khách hàng khác nhau
  • thay đổi URI dịch vụ web thành máy chủ dự phòng (khi máy chủ chính gặp sự cố)

Cập nhật: Tất cả các ví dụ trên là về những thứ không nhất thiết yêu cầu phụ thuộc vào các lớp. Nhưng bạn có thể dễ dàng xây dựng các trường hợp cần cả một đối tượng phức tạp và tự động hóa - ví dụ:

  • Hãy tưởng tượng bạn có một hệ thống trong đó nó giám sát lưu lượng truy cập vào trang web của bạn. Tùy thuộc vào số lượng người dùng đồng thời, nó sẽ bật / tắt cơ chế ghi nhật ký. Có lẽ trong khi cơ chế tắt, một đối tượng sơ khai được đưa vào vị trí của nó.
  • Hãy tưởng tượng bạn có một hệ thống hội nghị trên web, trong đó tùy thuộc vào # người dùng, bạn muốn chuyển khả năng thực hiện P2P tùy thuộc vào # người tham gia

+1 để làm nổi bật khía cạnh doanh nghiệp ngay ở trên cùng. Đôi khi, việc kiểm tra mã kế thừa được viết kém có thể là một cơn ác mộng kéo dài nhiều ngày.
Richard Le Mesurier

2

Bạn không cần phải biên dịch lại mã của mình mỗi khi bạn thay đổi điều gì đó trong cấu hình. Nó sẽ đơn giản hóa việc triển khai và bảo trì chương trình. Ví dụ, bạn có thể hoán đổi một thành phần này với một thành phần khác chỉ với 1 thay đổi trong tệp cấu hình.


triển khai? chắc là ... bảo trì triển khai? chắc là ... bảo trì mã? Tôi có xu hướng nghĩ là không ... gỡ lỗi thông qua các khuôn khổ có xu hướng là một vấn đề đau đầu và pojos dễ giải quyết hơn rất nhiều về mặt đó.
Mike Stone

1
Mike, tôi không nói gì về mã. Tất cả chúng ta đều biết rằng cấu hình XML rất tệ :)
aku

Hmm .. bạn có bao lâu thay đổi các thành phần mà không cần biên dịch lại và để làm gì? Tôi hiểu nếu ai đó thay đổi thông tin đăng nhập DB và không muốn biên dịch lại chương trình - anh ta có thể không phải là người phát triển nó. Nhưng tôi khó có thể tưởng tượng ai đó khác ngoài nhà phát triển thay đổi cấu hình mùa xuân
Pavel Feldman,

Pavel thường xảy ra tình huống này khi bạn phải triển khai chương trình cho hàng trăm máy khách. Trong tình huống như vậy, việc thay đổi cấu hình dễ dàng hơn nhiều thay vì triển khai phiên bản mới của sản phẩm. Bạn đang nói đúng về nhà phát triển. Thông thường nhà phát triển tạo cfg mới và quản trị viên triển khai nó.
aku

2

Bạn có thể sắp xếp một triển khai mới cho bạn gái. Vì vậy, nữ mới có thể được tiêm mà không cần biên dịch lại mã của bạn.

<bean id="jane" class="foo.bar.HotFemale">
  <property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
  <property name="girlfriend" ref="jane"/>
</bean>

(Ở trên giả sử Female và HotFemale triển khai cùng một giao diện GirlfFriend)


Tại sao sửa đổi logic mà không biên dịch lại được coi là một ý tưởng tốt?
Pavel Feldman

Tôi chắc chắn có thể làm HotFemale jane = new HotFmale (); jane.setAge (19); john.setGirlfriend (jane); Vậy khác biệt duy nhất là cfg có thể thay đổi mà không cần biên dịch lại? Đó dường như là câu trả lời phổ biến khi thảo luận về Mùa xuân. Tại sao?! Tại sao tốt để tránh biên dịch?
Pavel Feldman

Tôi có thể kiểm tra mã tốt hơn Tôi có thể giả mạo đối tượng nữ.
Paul Whelan

@Pavel Feldman: bởi vì nếu bạn đã triển khai ứng dụng tại máy khách thì việc này sẽ dễ dàng hơn.
Andrei Rînea

1

Trong thế giới .NET, hầu hết các khuôn khổ IoC đều cung cấp cả cấu hình XML và Mã.

StructureMap và Ninject, chẳng hạn, sử dụng các giao diện thông thạo để định cấu hình vùng chứa. Bạn không còn bị ràng buộc phải sử dụng các tệp cấu hình XML. Spring, cũng tồn tại trong .NET, phụ thuộc rất nhiều vào các tệp XML vì nó là giao diện cấu hình chính trước đây của anh ấy, nhưng vẫn có thể định cấu hình các vùng chứa theo chương trình.


Thật tuyệt khi cuối cùng tôi có thể sử dụng mã cho những gì nó dự định sẽ được sử dụng :) Nhưng tại sao tôi thậm chí còn cần bất cứ thứ gì khác ngoài ngôn ngữ lập trình để làm những việc như vậy?
Pavel Feldman

Tôi nghĩ đó là vì XML cho phép thay đổi thời gian chạy, hoặc ít nhất, thay đổi cấu hình mà không cần phải biên dịch lại dự án.
Romain Verdier

1

Dễ dàng kết hợp các cấu hình từng phần thành một cấu hình hoàn chỉnh cuối cùng.

Ví dụ, trong các ứng dụng web, mô hình, chế độ xem và bộ điều khiển thường được chỉ định trong các tệp cấu hình riêng biệt. Sử dụng phương pháp khai báo, bạn có thể tải, ví dụ:

  UI-context.xml
  Model-context.xml
  Controller-context.xml

Hoặc tải bằng một giao diện người dùng khác và một vài bộ điều khiển bổ sung:

  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

Để làm điều tương tự trong mã, yêu cầu một cơ sở hạ tầng để kết hợp các cấu hình từng phần. Không phải là không thể làm trong mã, nhưng chắc chắn dễ dàng hơn khi sử dụng khung IoC.


1

Thông thường, điểm quan trọng là ai sẽ thay đổi cấu hình sau khi chương trình được viết. Với cấu hình trong mã, bạn mặc nhiên cho rằng người thay đổi nó có cùng kỹ năng và quyền truy cập vào mã nguồn, v.v. như tác giả gốc đã có.

Trong các hệ thống sản xuất, rất thực tế khi trích xuất một số cài đặt con (ví dụ: tuổi trong bạn chẳng hạn) sang tệp XML và cho phép người quản trị hệ thống hoặc bộ phận hỗ trợ cá nhân thay đổi giá trị mà không cần cấp cho họ toàn quyền đối với mã nguồn hoặc các cài đặt khác - hoặc chỉ để cách ly chúng khỏi sự phức tạp.


Đó là một điểm hợp lệ, nhưng cấu hình lò xo thường có xu hướng khá phức tạp. Mặc dù có thể dễ dàng thay đổi độ tuổi, sysadmin vẫn phải xử lý tệp xml lớn mà anh ấy không cần thiết phải hiểu hoàn toàn. Không phải tốt hơn là trích xuất phần được cho là được cấu hình thành một thứ thậm chí còn đơn giản hơn cấu hình XML mùa xuân? Giống như tệp thuộc tính, với một dòng duy nhất "age = 23" và không cho phép quản trị viên thay đổi các chi tiết khác, như tên lớp, v.v., yêu cầu kiến ​​thức về cấu trúc chương trình nội bộ.
Pavel Feldman

Gần đây tôi đang làm việc trong một dự án có sự kết hợp giữa mã Java và XSLT. Nhóm là một hỗn hợp những người giỏi về Java (và có thể kém thoải mái khi làm việc với XML và XSLT); và những người đã làm việc rất hiệu quả với XML và XSLT (và ít thoải mái hơn với Java). Vì cấu hình sẽ được quản lý bởi nhóm thứ hai, nên sử dụng Spring và có các cấu hình XML. Nói cách khác, Spring đã giải quyết được vấn đề phân công lao động trong đội. Nó không giải quyết được vấn đề "kỹ thuật"; theo nghĩa là cấu hình có thể được thực hiện dễ dàng với mã Java.
Dawood ibn Kareem

Khi nào 'nhân viên hỗ trợ' sẽ biết bất cứ điều gì về việc phải thay đổi một số lớp đang được tạo trong một vùng chứa tiêm phụ thuộc ?? Thực sự theo giả định rằng công việc của một nhà phát triển là làm điều này?
Jimbo

Đó chính xác là lý do tại sao việc trích xuất các giá trị cấu hình (ví dụ: URL của hệ thống mà bạn tích hợp với) lại có ý nghĩa: bộ phận hỗ trợ chỉnh sửa tệp thuộc tính hoặc (trong trường hợp xấu nhất) tệp XML, lớp Java đã biên dịch vẫn giữ nguyên.
Miro A.

1

Từ góc nhìn của mùa xuân, tôi có thể cho bạn hai câu trả lời.

Đầu tiên, cấu hình XML không phải là cách duy nhất để xác định cấu hình. Hầu hết mọi thứ có thể được định cấu hình bằng cách sử dụng chú thích và những thứ phải thực hiện với XML là cấu hình cho mã mà bạn không viết bất cứ lúc nào, như một nhóm kết nối mà bạn đang sử dụng từ một thư viện. Spring 3 bao gồm một phương pháp để xác định cấu hình DI bằng Java tương tự như cấu hình DI cuộn tay trong ví dụ của bạn. Vì vậy, sử dụng Spring không có nghĩa là bạn phải sử dụng tệp cấu hình dựa trên XML.

Thứ hai, Spring không chỉ là một khung DI. Nó có rất nhiều tính năng khác bao gồm quản lý giao dịch và AOP. Cấu hình Spring XML kết hợp tất cả các khái niệm này với nhau. Thông thường, trong cùng một tệp cấu hình, tôi chỉ định các phụ thuộc bean, cài đặt giao dịch và thêm các bean phạm vi phiên thực sự xử lý bằng AOP trong nền. Tôi thấy cấu hình XML cung cấp một nơi tốt hơn để quản lý tất cả các tính năng này. Tôi cũng cảm thấy rằng cấu hình dựa trên chú thích và cấu hình XML mở rộng quy mô tốt hơn so với cấu hình dựa trên Java.

Nhưng tôi thấy quan điểm của bạn và không có gì sai khi xác định cấu hình tiêm phụ thuộc trong Java. Tôi thường tự làm điều đó trong các bài kiểm tra đơn vị và khi tôi đang làm việc trên một dự án đủ nhỏ mà tôi chưa thêm khung DI. Tôi thường không chỉ định cấu hình trong Java vì đối với tôi đó là loại mã đường ống dẫn nước mà tôi đang cố gắng tránh viết khi tôi chọn sử dụng Spring. Tuy nhiên, đó là một ưu tiên, không có nghĩa là cấu hình XML vượt trội hơn cấu hình dựa trên Java.


0

Spring cũng có một bộ nạp thuộc tính. Chúng tôi sử dụng phương pháp này để thiết lập các biến phụ thuộc vào môi trường (ví dụ: phát triển, thử nghiệm, chấp nhận, sản xuất, ...). Đây có thể là ví dụ như hàng đợi để nghe.

Nếu không có lý do gì khiến thuộc tính thay đổi, thì cũng không có lý do gì để định cấu hình nó theo cách này.


0

Trường hợp của bạn rất đơn giản và do đó không cần vùng chứa IoC (Inversion of Control) như Spring. Mặt khác, khi bạn "lập trình cho các giao diện chứ không phải triển khai" (đây là một thực tiễn tốt trong OOP), bạn có thể có mã như sau:

IService myService;
// ...
public void doSomething() {
  myService.fetchData();
}

(lưu ý rằng loại myService là IService - một giao diện, không phải là một triển khai cụ thể). Giờ đây, có thể hữu ích khi để vùng chứa IoC của bạn tự động cung cấp phiên bản cụ thể chính xác của IService trong quá trình khởi tạo - khi bạn có nhiều giao diện và nhiều cách triển khai, việc làm đó bằng tay có thể phức tạp. Các lợi ích chính của vùng chứa IoC (khung tiêm phụ thuộc) là:

  • Cấu hình bên ngoài của ánh xạ giữa các giao diện và triển khai cụ thể của chúng
  • IoC container xử lý một số vấn đề phức tạp như giải quyết các đồ thị phụ thuộc phức tạp, quản lý thời gian tồn tại của thành phần, v.v.
  • Bạn tiết kiệm thời gian viết mã vì bạn cung cấp ánh xạ một cách khai báo, không phải trong mã thủ tục
  • Nguyên tắc Inversion of Control cho phép dễ dàng kiểm tra đơn vị vì bạn có thể thay thế các triển khai thực bằng các triển khai giả (như thay thế cơ sở dữ liệu SQL bằng một trong bộ nhớ)

0

Khởi tạo trong tệp cấu hình XML sẽ đơn giản hóa công việc gỡ lỗi / điều chỉnh của bạn với một khách hàng đã triển khai ứng dụng của bạn trên máy tính của họ. (Vì nó không yêu cầu biên dịch lại + thay thế tệp nhị phân)


-2

Một trong những lý do hấp dẫn nhất là " nguyên tắc Hollywood ": đừng gọi cho chúng tôi, chúng tôi sẽ gọi cho bạn. Một thành phần không bắt buộc phải thực hiện việc tra cứu chính các thành phần và dịch vụ khác; thay vào đó chúng được cung cấp tự động. Trong Java, điều này có nghĩa là không cần thiết phải thực hiện tra cứu JNDI bên trong thành phần.

Việc kiểm tra đơn vị một thành phần một cách đơn lẻ cũng dễ dàng hơn rất nhiều: thay vì cung cấp cho nó một triển khai thực tế của các thành phần mà nó cần, bạn chỉ cần sử dụng các mocks (có thể được tạo tự động).


Câu trả lời này là về tiêm phụ thuộc. Tôi biết nó là gì, tôi biết nó là tốt và tôi nói rõ điều đó trong câu đầu tiên của câu hỏi. Câu hỏi là về lợi ích của cấu hình DI, so với mã khởi tạo đơn giản.
Pavel Feldman

Không thực sự trả lời câu hỏi
Casebash
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.