2011-10-21 21 views
6

Gần đây tôi đã hỏi về cách thực hiện đúng DI và có một số liên kết đến các bài đăng trên blog về nó. Tôi nghĩ giờ đây tôi đã hiểu rõ hơn - xây dựng đối tượng riêng biệt từ logic, bằng cách đặt nó vào các nhà máy. Nhưng tất cả các ví dụ là dành cho những thứ như trang web, và nói để làm tất cả các hệ thống dây điện lúc khởi động. Gọi một nhà máy lớn có tất cả mọi thứ và đi qua trong tất cả các phụ thuộc của new.Khi nào và ở đâu để gọi các nhà máy trong thời gian chạy?

Nhưng nếu tôi không muốn khởi tạo mọi thứ ở phía trước thì sao? Tôi có một đối tượng có chứa một danh sách các đối tượng khác mà nó có thể ủy nhiệm, nhưng chúng đắt tiền và được sử dụng cùng một lúc, vì vậy tôi xây dựng chúng khi cần và để chúng được thu thập khi tôi hoàn thành. Tôi không muốn đặt new B() bên trong logic của A vì tôi thà dùng DI - nhưng làm cách nào? Có thể A gọi cho nhà máy? Điều đó dường như không tốt hơn nhiều, trừ khi nhà máy duy trì trạng thái bao gồm cả các phụ thuộc hiện tại. Tôi chỉ không muốn chuyển toàn bộ danh sách B s vào A khi nó được xây dựng, vì nó sẽ lãng phí. Nếu bạn muốn, B không nhất thiết để được bên A, mặc dù nó có ý nghĩa logic (A là cấp độ trò chơi, B là một màn hình duy nhất), nhưng trong mọi trường hợp logic của A dictates khi B được tạo ra.

Vì vậy, ai gọi cho nhà máy để nhận B và khi nào?

Làm rõ: Tôi không sử dụng khuôn khổ cho DI. Tôi tự hỏi, nếu thuật ngữ DI ngụ ý điều đó?

+0

Great câu hỏi. Tôi cũng bị bối rối bởi vấn đề này. Ngoài ra, đôi khi tôi muốn nhiều instantiations của một cái gì đó (và một số tùy ý của họ lúc đó!) Và đã rất bối rối như thế nào đó là nghĩa vụ phải làm việc. Với Ninject tôi đã hút nó lên và tiêm vào 'IKernel' nhưng tôi được cho là đã hiểu được điều đó. (mặc dù theo kinh nghiệm của tôi, rất mạnh mẽ và chưa hối tiếc - và không, tôi thậm chí còn không quan tâm đến việc "trao đổi" khung DI của tôi, vì vậy lợi ích đó đang khắc phục tôi) –

+0

có một số hiểu biết sâu sắc về các mẫu. Trong khi tôi sẽ không đi lang thang hoang dã, nơi mọi loại biết về IKernel, hoặc bất cứ điều gì được gọi là trong hương vị của bạn DI framework, đôi khi đó là cách tốt nhất để đi. Tôi đã làm điều này là tốt, và không có gì phát nổ trên tôi. – Andy

Trả lời

6

Trong Ninject, bạn có thể đăng ký Func<B> và yêu cầu trong hàm tạo A.

Autofac sẽ tự động cung cấp Func<B> nếu B đã được đăng ký.

Hoặc, bạn có thể sử dụng phương pháp tiếp cận thẳng hơn và xác định nhà máy rõ ràng cho B và yêu cầu nhà máy đó trong nhà xây dựng; chỉ cần gõ nhiều hơn khi bạn phải tạo một nhà máy cho mọi phụ thuộc mà bạn muốn tạo ra một cách lười biếng.


Dưới đây là một câu trả lời SO cho thấy Ninject phong cách phương pháp nhà máy: How do I handle classes with static methods with Ninject?


@Not Sử dụng Khung: Nếu bạn có thể, tôi muốn có thể nhìn vào sử dụng một: một IoC/DI framework thường sẽ xử lý việc tạo trễ cho bạn ra khỏi hộp.

Nếu bạn muốn tiếp tục cuộn, hãy chuyển nhà máy tạo B cho đối tượng của bạn. Hoặc, nếu bạn chỉ không thích Funcs thô và không muốn phải tạo ra các nhà máy rõ ràng cho tất cả các đối tượng của bạn, sau đó bạn có thể nhìn vào sử dụng Lazy<B> cho một giải pháp chính thức hơn.

+1

+1, câu trả lời hợp lý, tuyệt vời. – Andy

+0

Thật tuyệt vời! Đồng ý với Andy. –

+0

Giải pháp tôi đã đi không khớp chính xác với bất kỳ câu trả lời nào trong số này (tôi đã đi liền với hầu hết mọi thứ ở phía trước), nhưng điều này đã cho tôi một số hiểu biết mới về cách tiếp cận của DI, vì vậy tôi chấp nhận nó. – Tesserex

1

Thường có hai mẫu để sử dụng các đối tượng hiếm khi cần thiết đắt tiền để tạo. Mô hình đầu tiên là sử dụng một nhà máy, như David Faivre gợi ý. Khác là bằng cách sử dụng một proxy.

Một proxy là từ quan điểm thiết kế - có lẽ là giải pháp sạch nhất, mặc dù có thể cần thêm mã để triển khai. Nó là sạch nhất, bởi vì ứng dụng có thể hoàn toàn không nhận thức được điều này, bởi vì bạn không cần một giao diện phụ (như cách tiếp cận nhà máy cần).

Dưới đây là một ví dụ đơn giản với một số giao diện và một thực hiện đắt:

interface IAmAService 
{ 
    void DoSomething(); 
} 

class ExpensiveImpl : IAmAService 
{ 
    private object x = [some expensive init]; 

    void IAmAService.DoSomething() { } 
} 

Không bạn có thể thực hiện một proxy dựa trên giao diện đó, có thể trì hoãn việc tạo ra các thực rằng:

class ServiceProxy : IAmAService 
{ 
    private readonly Func<IAmAService> factory; 
    private IAmAService instance; 

    public ServiceProxy(Func<IAmAService> factory) 
    { 
     this.factory = factory; 
    } 

    void IAmAService.DoSomething() 
    { 
     this.GetInstance().DoSomething(); 
    } 

    private IAmAService GetInstance() 
    { 
     // TODO: Implement double-checked lock only a single 
     // instance may exist per ServiceProxy. 
     if (this.instance == null) 
     { 
      this.instance = this.factory(); 
     } 

     return this.instance; 
    } 
} 

Lớp proxy này chấp nhận một đại biểu nhà máy là phụ thuộc, giống như David Faivre được mô tả trong câu trả lời của ông, nhưng theo cách này, ứng dụng sẽ không phải phụ thuộc vào Func<IAmAService>, nhưng có thể chỉ phụ thuộc vào IAmAService.

Bây giờ thay vì tiêm một ExpensiveImpl, bạn có thể tiêm một ServiceProxy vào trường hợp khác:

// Create the proxy 
IAmAService service = 
    new ServiceProxy(() => new ExpensiveImpl()); 

// Inject it into whatever you wish, such as: 
var customerService = new CustomerService(service); 
+1

+1 - Tôi luôn phải tạo ra một "nhà máy", tôi phải tạo hai giao diện: một cho IService và một cho IServiceFactory, và sau đó triển khai cho cả hai (đây là lý do tại sao tôi chỉ sử dụng Func - và bởi vì tôi lười biếng, có lẽ sẽ tiếp tục làm như vậy). Tuy nhiên, tôi thích thực tế là bạn đang ẩn nhà máy trong việc triển khai trực tiếp IService. Vẫn còn hai triển khai, nhưng bây giờ ít nhất là một giao diện của nó. Neeto. –

Các vấn đề liên quan