2013-04-16 26 views
26

Vì vậy, tôi hiện đang thiết kế lại ứng dụng Android của tôi để sử dụng Dagger. Ứng dụng của tôi lớn và phức tạp và gần đây tôi đã xem xét kịch bản sau:Sử dụng Dagger để tiêm phụ thuộc vào các nhà thầu

Đối tượng A yêu cầu một cá thể DebugLogger đặc biệt là ứng cử viên hoàn hảo để tiêm. Thay vì đi vòng quanh logger, tôi chỉ có thể tiêm nó thông qua hàm tạo của A. Điều này trông giống như sau:

class A 
{ 
    private DebugLogger logger; 

    @Inject 
    public A(DebugLogger logger) 
    { 
     this.logger = logger; 
    } 

    // Additional methods of A follow, etc. 
} 

Điều này có ý nghĩa. Tuy nhiên, A có nhu cầu được xây dựng bởi một lớp B. Nhiều trường hợp của A phải được xây dựng, vì vậy sau cách làm việc Dagger, tôi đơn giản tiêm một Provider<A> vào B:

class B 
{ 
    private Provider<A> aFactory; 

    @Inject 
    public B(Provider<A> aFactory) 
    { 
     this.aFactory = aFactory; 
    } 
} 

Ok, tốt cho đến nay. Nhưng chờ đợi, đột nhiên Một nhu cầu đầu vào bổ sung, chẳng hạn như một số nguyên được gọi là "số lượng" đó là quan trọng để xây dựng của nó. Bây giờ, nhà xây dựng của tôi cho A cần phải trông như thế này:

@Inject 
public A(DebugLogger logger, int amount) 
{ 
... 
} 

Đột nhiên tham số mới này can thiệp vào việc tiêm. Hơn nữa, ngay cả khi điều này đã làm việc, sẽ không có cách nào cho tôi để vượt qua trong "số tiền" khi lấy một trường hợp mới từ nhà cung cấp, trừ khi tôi nhầm. Có một số điều tôi có thể làm ở đây, và câu hỏi của tôi là câu hỏi nào là tốt nhất?

Tôi có thể tái cấu trúc A bằng cách thêm phương thức setAmount() được dự kiến ​​sẽ được gọi sau hàm tạo. Tuy nhiên, điều này là xấu, bởi vì nó buộc tôi phải trì hoãn việc xây dựng A cho đến khi "số tiền" được điền vào. Nếu tôi có hai tham số, "số lượng" và "tần suất", thì tôi sẽ có hai người định cư, có nghĩa là phức tạp kiểm tra để đảm bảo rằng việc xây dựng hồ sơ xin việc của A sau khi cả hai setters được gọi, hoặc tôi sẽ phải bổ sung được nêu ra một phương pháp thứ ba vào hỗn hợp, như vậy:

(Somewhere in B): 

A inst = aFactory.get(); 
inst.setAmount(5); 
inst.setFrequency(7); 
inst.doConstructionThatRequiresAmountAndFrequency(); 

các thay thế khác là tôi không sử dụng constructor dựa trên tiêm và đi với tiêm dựa trên trường. Nhưng bây giờ, tôi phải công khai lĩnh vực của mình. Điều này không tốt với tôi, bởi vì bây giờ tôi bắt buộc phải tiết lộ dữ liệu nội bộ của các lớp học của tôi cho các lớp khác.

Cho đến nay, giải pháp duy nhất có phần thanh lịch tôi có thể nghĩ đến là sử dụng tiêm lĩnh vực dựa trên các nhà cung cấp, như vậy:

class A 
{ 
    @Inject 
    public Provider<DebugLogger> loggerProvider; 
    private DebugLogger logger; 

    public A(int amount, int frequency) 
    { 
     logger = loggerProvider.get(); 
     // Do fancy things with amount and frequency here 
     ... 
    } 
} 

Thậm chí vẫn còn, tôi không chắc chắn về thời gian, kể từ khi tôi m không chắc chắn nếu Dagger sẽ tiêm nhà cung cấp trước khi constructor của tôi được gọi.

Có cách nào tốt hơn không? Tôi chỉ thiếu một cái gì đó về cách Dagger hoạt động?

Trả lời

49

Những gì bạn đang nói về được gọi là tiêm hỗ trợ và hiện không được Dagger hỗ trợ theo bất kỳ cách tự động nào.

Bạn có thể làm việc xung quanh điều này với các mô hình nhà máy:

class AFactory { 
    @Inject DebugLogger debuggLogger; 

    public A create(int amount, int frequency) { 
    return new A(debuggLogger, amount); 
    } 
} 

Bây giờ bạn có thể tiêm nhà máy này và sử dụng nó để tạo ra các trường hợp A:

class B { 
    @Inject AFactory aFactory; 

    //... 
} 

và khi bạn cần để tạo ra một A với 'số tiền' và 'tần suất' của bạn, bạn sử dụng nhà máy.

A a = aFactory.create(amount, frequency); 

Điều này cho phép Afinal trường hợp của logger, số tiền và lĩnh vực tần số trong khi vẫn sử dụng tiêm để cung cấp các ví dụ logger.

Guice có một plugin tiêm hỗ trợ về cơ bản tự động hóa việc tạo ra các nhà máy này cho bạn. Có have been discussion trên danh sách gửi thư của Dagger về cách thích hợp để chúng được thêm vào nhưng không có gì đã được quyết định khi viết bài này.

+0

Cảm ơn bạn đã phản hồi nhanh chóng. Mẫu nhà máy bạn đã mô tả có vẻ giống như cách tiếp cận tốt nhất. Tôi cũng đã nhận ra rằng nếu Dagger hỗ trợ tiêm trường riêng, nó sẽ dễ dàng cho phép những gì tôi đang cố gắng thực hiện. Tôi tự hỏi tại sao đó không phải là một phần thiết kế ban đầu của Dagger? Tôi giả sử Dagger tiêm sử dụng sự phản chiếu. Nếu vậy thì các trường tư nhân sẽ không có vấn đề gì, đúng không? – Alex

+0

Dagger rơi trở lại phản xạ nhưng đó không phải là phương tiện tiêm chính. Nó tạo ra mã trực tiếp thiết lập các trường hoặc gọi các hàm tạo của bạn. Như vậy nó hoạt động giống như bất kỳ đoạn mã nào khác trong cây nguồn của bạn và không thể truy cập các thành viên 'private'. –

+0

Và một số người quản lý bảo mật sẽ phá vỡ sự phản ánh dựa trên việc thay đổi khả năng truy cập trong các lớp học của bạn. –

3

Bài đăng của Jake nói là hoàn toàn đúng sự thật. Điều đó nói rằng, chúng tôi (một số người dân Google làm việc với Guice và Dagger) đang làm việc trên một phiên bản thay thế của "tiêm hỗ trợ" hoặc tạo nhà máy tự động mà có thể sử dụng được bởi Guice hoặc Dagger hoặc độc lập - nghĩa là, sẽ tạo mã nguồn cấp nhà máy cho bạn. Các lớp nhà máy này sẽ (nếu thích hợp) được tiêm như bất kỳ lớp JSR-330 tiêu chuẩn nào. Nhưng nó vẫn chưa được phát hành.

Đang chờ giải pháp như thế này, phương pháp tiếp cận của Jake Wharton được khuyến khích.

+0

Không phải lo lắng, tôi nhận ra Dagger vẫn còn trong các phiên bản đầu của nó. Tôi làm như vậy một số các tính năng khác đang được phát triển như bổ sung để giữ kích thước tổng thể của nhị phân xuống (đó là, đối với tôi ít nhất, những gì làm cho nó như một lựa chọn hấp dẫn cho phát triển Android). – Alex

+1

Vì vậy, đã đề cập đến tạo nhà máy tự động: https://github.com/google/auto/tree/master/factory – phazei

3

Bạn đang gặp sự cố bởi vì bạn đang trộn thuốc tiêm và không tiêm trong nhà xây dựng của bạn. Các quy tắc chung để tiêm mà sẽ giúp bạn tiết kiệm tấn đau lòng và giữ mã của bạn sạch là:

  1. thuốc chích có thể yêu cầu thuốc chích khác trong constructor của họ, nhưng không phải cho newables.

  2. Newables có thể yêu cầu các newables khác trong hàm tạo của chúng nhưng không thể cho injectables.

thuốc chích là đối tượng loại dịch vụ, tức là đối tượng mà làm việc như một CreditCardProcessor, MusicPlayer vv

Newables là kiểu giá trị các đối tượng như CreditCard, Song vv

0

bài của Jake là tuyệt vời, nhưng có cách đơn giản hơn. Google đã tạo thư viện AutoFactory để tạo nhà máy tự động tại thời gian biên dịch.

Đầu tiên, tạo lớp A với @AutoFactory chú thích và chú thích cho @Provided tiêm đối số:

@AutoFactory 
public class A { 

    private DebugLogger logger; 

    public A(@Provided DebugLogger logger, int amount, int frequency) { 
     this.logger = logger; 
    } 
} 

Sau đó, thư viện tạo AFactory lớp tại thời gian biên dịch. Vì vậy, bạn chỉ cần tiêm nhà máy đến một nhà xây dựng của lớp học B.

public class B { 

    private final AFactory aFactory; 

    @Inject 
    public B(AFactory aFactory) { 
     this.aFactory = aFactory; 
    } 

    public A createA(int amount, int frequency) { 
     return aFactory.create(amount, frequency); 
    } 
} 
Các vấn đề liên quan