Những gì phức tạp làm khung DI thêm?


8

Câu trả lời hiện tại được đánh giá cao nhất cho một câu hỏi rất gần đây nói rằng

Các thùng chứa DI là một mẫu "phần mềm doanh nghiệp", được sử dụng khi biểu đồ đối tượng rất lớn và phức tạp. Tôi nghi ngờ rằng 95% ứng dụng không yêu cầu nó.

đó là điều mà tôi rất không đồng ý Có thể tôi đã hiểu sai thuật ngữ, nhưng đối với tôi, khung DI có nghĩa chỉ là "thứ gì đó nối các đối tượng của tôi lại với nhau". Tui bỏ lỡ điều gì vậy?

Tôi đang sử dụng Guice ngay cả đối với các dự án thực sự nhỏ (như 10 lớp) để đơn giản hóa chúng. Chắc chắn, đó là tệp JAR 400 kB, nhưng đây không phải là điều tôi quan tâm. Đối với một dự án nhỏ, tôi hầu như không cần bất kỳ cấu hình nào và "chi phí chung" duy nhất là thêm @Injectchú thích.

Vì vậy, tôi thực sự tự hỏi, những gì phức tạp thêm làm khung DI gây ra?

Cập nhật địa chỉ câu trả lời

Trong một dự án lớp 82, tôi có

  • 32 @Injectchú thích
  • Chú thích 15 @Singletonvà 1@ProvidedBy
  • 4 Nhà cung cấp (tất cả những gì tôi cần cũng không có DI vì họ là nhà máy của tôi)
  • 1 Mô-đun chứa một dòng duy nhất
  • XML 0 dòng !!!

Đó là tất cả. Chắc chắn, đó là dự án nhỏ, nhưng chính xác đây là quan điểm của tôi. Các công việc bổ sung là một vài từ chứ không phải là dòng .

Tôi chỉ sử dụng phương thức tiêm constructor để có được các đối tượng "dễ dàng" bất biến. Bất cứ khi nào một xuất hiện phụ thuộc mới, tôi thêm một lĩnh vực cuối cùng, chúng ta hãy Lombok của RequiredArgsConstructor chăm lo của việc kê khai, và để cho Guice nâng niu mang gọi nó đúng cách.


Guice đang tiêm tài sản?
Phản ứng

@MathewFoscarini Tôi đoán vậy - Tôi biết các thuật ngữ khác và đây phải là (một trường hợp đặc biệt) phương pháp tiêm.
maaartinus

1
Tôi rất biết ơn Guice. Nó đã đơn giản hóa rất nhiều sự phát triển của tôi và làm cho nó trở nên dễ kiểm tra và mạnh mẽ hơn. Tôi không thể tin rằng tôi đã đi mà không có DI quá lâu. Bánh là có thật.
Điều phối viên

Câu trả lời:


6

Có một vài thỏa hiệp sẽ được thực hiện khi sử dụng các khung DI theo như tôi có thể thấy.

Điều đáng lo ngại nhất đối với tôi là mã ứng dụng của bạn thường được trải đều giữa ngôn ngữ lập trình chính (như Java) và mã cấu hình XML / JSON (đó mã). Điều này có nghĩa là trong trường hợp có vấn đề với ứng dụng của bạn, bạn cần xem xét ở hai nơi. Thông thường mã cấu hình không dễ dàng liên quan đến mã ứng dụng chính.

Tất nhiên, cấu hình cũng nằm ngoài giới hạn của những gì trình biên dịch (và thường là IDE) có thể kiểm tra cho bạn, có nghĩa là dễ mắc lỗi trong cấu hình này hơn nhiều so với khi bạn viết một lớp chính xử lý hệ thống dây. Trong thực tế, điều này có nghĩa là các vấn đề nối dây được đẩy từ các vấn đề thời gian biên dịch sang các vấn đề thời gian chạy.

Cũng như chia tách ứng dụng, sử dụng DI Framework cũng thường có nghĩa là bạn sử dụng @Injecthoặc tương tự trong mã của mình, thay vì các kỹ thuật DI truyền thống (tiêm giao diện CTOR trong Java / C ++, tiêm mẫu trong C ++). Nhược điểm của việc này là sau đó bạn phải sử dụng khung DI với ứng dụng. Một giải pháp thay thế sẽ là thiết kế ứng dụng của bạn mà không mong đợi khung DI và sau đó cho phép khung DI sử dụng lại cách tiêm CTOR / setter truyền thống của các lớp của bạn.

Nhược điểm của cơ chế tiêm truyền thống xuất hiện khi một lớp đóng gói phức tạp và đúng cách đòi hỏi một số mũi tiêm tương đối phức tạp khác tại thời điểm CTOR. Điều này thường được giải quyết bằng cách kết hợp một Builder, nhưng nó có thể là một nỗi đau.

CHỈNH SỬA :

Các chàng trai dưới đây đã đề cập rằng Guicekhông cần một cấu hình XML / JSON riêng biệt. Câu trả lời của tôi thực sự áp dụng cho việc sử dụng Spring của riêng tôi.


2
Điều thú vị về Guice là nó có thể được cấu hình đầy đủ trong Java (không có XML trừ khi bạn có một tiện ích bổ sung). Đối với các dự án nhỏ (đó là những gì tôi đang nói về), "tự động nối dây" thực sự đủ. Tôi đã cập nhật câu hỏi của mình; có lẽ tất cả những nỗi sợ hãi đến từ những khuôn khổ DI tồi tệ (hoặc quá enterpricey)?
maaartinus

1
@Dennis: Guice không xâm phạm gì cả. Container Guice chỉ là một cách khác để lấy các thể hiện đối tượng. Bạn vẫn có thể gọi các nhà xây dựng nếu bạn muốn. Đó là thuận tiện cho thử nghiệm đơn vị với các phụ thuộc bị chế giễu.
kevin cline

1
Không thể nói riêng với Guice, nhưng rất nhiều ngăn xếp DI rất tự động nếu bạn chỉ sử dụng chúng cho những thứ có vòng đời dài - singletons, nhà máy và những thứ tương tự. Những loại đối tượng với hàm tạo xây dựng cho phép bạn thoát khỏi những thứ tĩnh và phụ thuộc cứng khá dễ dàng; và tôi thấy nó hữu ích cho những trường hợp đó ngay cả đối với các dự án nhỏ. Tôi sẽ nói rằng, nếu bạn đang sử dụng các tính năng đơn giản hơn và trình xây dựng của một khung công tác DI, và nó làm tăng thêm sự phức tạp ... hãy tìm một khung DI mới.
danwyand

1
@maaartinus - Bạn có thể đúng về điều đó. Thành thật mà nói, tôi không quen với Guice nên tôi cho rằng câu trả lời của tôi chủ yếu áp dụng cho Spring.
Dennis

4

Tôi nghĩ rằng nhược điểm chính của việc sử dụng một container IoC là tất cả những điều kỳ diệu mà nó làm đằng sau hậu trường. (Đây cũng là một vấn đề với ORM, một công cụ ma thuật phổ biến khác.) Gỡ lỗi các vấn đề IoC không thú vị, bởi vì nó trở nên khó khăn hơn để xem cách thức và thời điểm phụ thuộc được thực hiện. Bạn không thể bước qua độ phân giải phụ thuộc trong trình gỡ lỗi.

Các bộ chứa thường gây khó khăn khi định cấu hình "lớp A với một phiên bản của phụ thuộc và lớp B với một phiên bản khác" hoặc các tình huống tương tự trong đó trừu tượng được sử dụng lại. Bạn có thể kết thúc trong các tình huống mà mỗi lớp có giao diện riêng, ngay cả khi chúng có cùng một ý nghĩa. Điều này ngăn bạn khỏi sức mạnh trừu tượng của việc sử dụng lại các giao diện.

Theo tôi, chiến thắng thực sự lớn mà bạn nhận được từ IoC là quản lý lối sống tự động. Bạn có thể khai báo các thành phần là singletons, singletons theo yêu cầu, các thể hiện thoáng qua, v.v. - và container sẽ chăm sóc tất cả cho bạn. Thật khó khăn hơn để thiết lập điều này bằng cách sử dụng new-expressions. Mặt khác, nó vẫn dễ gây ra rò rỉ tài nguyên bằng cách không phù hợp với lối sống.

Một số dự án sử dụng IoC để xây dựng mọi thứ trong hệ thống - ngay cả các lớp đại diện cho dữ liệu kinh doanh - không có newtừ khóa. Tôi nghĩ rằng đây thường là một sai lầm vì nó khuyến khích bạn viết các lớp được gói gọn và quá nhiều giao diện. Mã của bạn trở nên phụ thuộc vào IoC. Nó cũng có vẻ là đồng mắc bệnh với thử nghiệm quá mức.

Cá nhân, tôi thường thích đi mà không có container. Thay vào đó, tôi soạn các đối tượng bằng cách sử dụng new-expressions được gói gọn trong một gốc thành phần. Thật dễ dàng để thấy các phụ thuộc cụ thể của một lớp nhất định và khi tôi thay đổi một hàm tạo tôi gặp các lỗi thời gian biên dịch, cả hai đều là các chiến thắng bảo trì lớn. Nó cũng đơn giản để cấu hình các trường hợp riêng biệt với các thành phần khác nhau.

Lời khuyên của tôi, nếu bạn muốn sử dụng bộ chứa IoC, là hoàn thành các phụ thuộc của bạn tại điểm vào hệ thống và chỉ sử dụng nó cho các phụ thuộc thực tế của bạn (kho lưu trữ et al). Và hãy nhớ rằng bạn vẫn có thể sử dụng phép nội xạ phụ thuộc và thậm chí giữ cấu hình trong một tệp duy nhất mà không cần sử dụng bộ chứa.


1
"hãy nhớ rằng bạn vẫn có thể sử dụng phép nội xạ phụ thuộc và thậm chí giữ cấu hình trong một tệp duy nhất mà không cần sử dụng bộ chứa." ++. Tôi nghĩ rằng quá nhiều người quên rằng khung container DI và DI không giống nhau.
RubberDuck

1

Một số nhà phát triển lập luận rằng bất kỳ khung nào bạn sử dụng trong mã của mình đều là vấn đề tiềm ẩn vào ngày sau, bởi vì nó thường yêu cầu bạn viết mã theo một cách nhất định để tương tác với nó một cách chính xác và điều này có thể gây ra sự cố vào ngày sau Hóa ra là những cách xung đột với các yêu cầu thiết kế khác. Một ví dụ về mối quan tâm này được thể hiện bởi Bob Martin ở đây . Cá nhân tôi không đồng ý, nhưng tôi có thể thấy quan điểm của anh ấy.


2
Vì vậy, những lợi ích đạt được bằng cách sử dụng khung này trong một dự án phức tạp hơn có thể làm cho nó có giá trị rủi ro?
JeffO

1

Tóm lại: Có vẻ như sự phức tạp duy nhất đến từ việc sử dụng các phương thức cấu hình không phù hợp (vâng, XML không an toàn kiểu) và cũng có thể từ các khung làm việc nhiều hơn (Spring quản lý vòng đời đối tượng, vượt xa DI).

Đối với một dự án đơn giản, DI làm đúng là cực kỳ hữu ích:

  • nó cho phép viết các dịch vụ nhỏ lớp bất biến với nỗ lực tối thiểu
  • nó đảm bảo tính đơn lẻ trong phạm vi phù hợp trong khi vẫn có thể kiểm tra
  • nó hầu như không cần bất kỳ cấu hình nào (khoảng 1 dòng trên 20 lớp DI)

Nó cũng khá vô hại, vì tất cả có thể được thực hiện bằng tay. Nó chỉ tạo các thể hiện, chuyển chúng đến các hàm tạo để tạo thêm thể hiện, v.v. Chán và dài, nhưng đơn giản.

Một dịch vụ điển hình sử dụng Guice và Lombok trông như thế này

@Singleton
@RequiredArgsConstructor(onConstructor=@__(@Inject))
public class TotalCostsComputer {
    public CostsResult getCosts(Goods goods, Shippment shippment) {
        return taxComputer.applyTax(composeResult(
                    goodsCostComputer.getCosts(goods),
                    shippmentCostComputer.getCosts(shippment));
    }
    ...

    private final GoodsCostComputer goodsCostComputer;
    private final ShippmentCostComputer shippmentCostComputer;
    private final TaxComputer taxComputer;
}

Bây giờ, thêm một phụ thuộc có nghĩa là chỉ cần thêm một trường riêng cuối cùng mới . Nó không thể là null (Guice đảm bảo điều này ngay cả khi không có @NonNull ).

Kiểm tra có nghĩa là tạo các cá thể của riêng bạn hoặc lấy chúng từ Injector(có thể được cấu hình để trả về một số sơ khai). Cả hai đều khá đơn giản.

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.