6

Tôi đã thử nghiệm bằng cách sử dụng các đại biểu có tên thay vì giao diện đơn phương pháp. Này có một số lợi thế cho mã kích thước, như chúng ta có thể đi từ (một số linebreaks loại bỏ để không nhấn mạnh đến những trường hợp):Có cách nào đơn giản để đăng ký đóng cửa tĩnh với Castle Windsor?

public interface IProductSource 
{ 
    IEnumerable<Product> GetProducts(); 
} 
public class DataContextProductSource : IProductSource 
{ 
    private readonly DataContext _DataContext; 
    public DataContextProductSource(DataContext dataContext) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     _DataContext = dataContext; 
    } 
    public IEnumerable<Product> GetProducts() 
    { 
     return _DataContext.Products.AsEnumerable(); 
    } 
} 

tới:

public delegate IEnumerable<Product> DGetProducts(); 
public static class DataContextFunctions 
{ 
    public DGetProducts GetProducts(DataContext dataContext) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     return() => dataContext.Products.AsEnumerable(); 
    } 
} 

này về cơ bản là lợi dụng thực tế rằng một khi bạn đi đủ xa với tiêm phụ thuộc, rất nhiều lớp học trở nên ít hơn đóng cửa. Các lớp đó có thể được thay thế bằng các hàm trả về lambdas. Toàn bộ các hàm liên quan (không cần phải đóng gói bất kỳ trạng thái có thể thay đổi nào, nhưng sẽ được biểu diễn bằng cách sử dụng các lớp trong "dependency" dependency injection), sau đó có thể được cuộn lên thành một lớp tĩnh (hoặc "module" trong VB parlance) .

Đây là tất cả tốt và tốt, nhưng tôi đang gặp khó khăn khi tìm cách tốt nhất để đăng ký các phương pháp tĩnh này với Castle Windsor. Các phương pháp không phụ thuộc dễ dàng:

Component.For<DGetIntegers>().Instance(Integers.GetOneToTen) 

Nhưng DataContextFunctions.GetProducts của chúng tôi ở trên có một số phụ thuộc. Cách tốt nhất mà tôi đã tìm thấy để đăng ký này là:

Component.For<DGetProducts>().UsingFactoryMethod(
    kernel => DataContextFunctions.GetProducts(kernel.Resolve<DataContext>()) 

này có thể nhận được khá rườm rà, và rõ ràng là cần phải hỏi kernel trực tiếp đối với từng phụ thuộc loại đánh bại điểm một chút. Dường như với tôi rằng tất cả các thông tin tĩnh mà một container cần có sẵn, vì vậy nó sẽ có thể làm điều này.

Câu hỏi là, Castle Windsor (hay bất kỳ container nào khác) có cách đơn giản để làm điều này mà tôi đã bỏ lỡ, hoặc có vấn đề kỹ thuật phát sinh, hoặc nó chỉ là quá thích hợp một trường hợp sử dụng đã được bao gồm?

Trả lời

4

Đó là một cách tiếp cận thú vị. Tôi nghĩ bạn có thể làm việc này khá dễ dàng với một người kích hoạt tùy chỉnh.

+0

Cảm ơn, Krzysztof. Tôi sẽ cố gắng thực hiện điều đó và báo cáo lại kết quả. Bất kỳ liên kết nào cho một nơi tốt để bắt đầu? – ninjeff

+0

@ninjeff nếu bạn đang ở trên 2.5.x tài liệu sẽ giúp bạn bắt đầu docs.castleproject.org/(S(hucszcu5ilznbv45fvrim355))/… Nếu bạn đang sử dụng 3,0 beta, có một số thay đổi về API mặc dù các khái niệm vẫn là tương tự. Hãy cho tôi biết nếu doco ít hơn hoàn hảo –

+0

Tôi đã có thể làm việc này. Tôi sẽ đăng một liên kết và mô tả với câu trả lời của riêng tôi trong một giờ (những người dùng có nghiệp vụ thấp như tôi không thể trả lời các câu hỏi của chúng tôi trong vòng tám giờ). – ninjeff

10

Câu trả lời ngắn

Bạn đang cố gắng để thực hiện một DI container (Lâu đài Windsor) thực hiện chức năng phần, nhưng nó thực sự nhắm vào đối tượng phần. Điều đó sẽ mang đến cho bạn rất nhiều ma sát. Tôi đoán là bạn sẽ có được trải nghiệm tương tự với các container khác.

DI Các thùng chứa được thiết kế xung quanh các khái niệm hướng đối tượng - đặc biệt là SOLID. Chúng hoạt động rất tốt với các nguyên tắc này vì chúng được thiết kế với sự hiểu biết vốn có về những thứ như Constructor Injection và Auto-wiring.

Không có gì sai với cách tiếp cận chức năng hơn, nhưng tôi chưa thấy DI Container được xây dựng xung quanh thành phần hàm thay vì thành phần đối tượng.

Long trả lời

Việc sử dụng các đại biểu như một nguyên tắc chung của DI có xu hướng được vấn đề bằng các ngôn ngữ tĩnh đánh máy (ít nhất là trong .NET) cho một vài lý do. Khái niệm không có gì sai với cách tiếp cận này kể từ một delegate can be considered as an anonymous Role Interface. Tuy nhiên, nó có xu hướng trở nên khó sử dụng vì loại mơ hồ.

Cách tiếp cận phổ biến nhất mà tôi thường thấy là sử dụng các đại biểu được tích hợp sẵn của BCL, chẳng hạn như Func<T>, Action<T> và cứ tiếp tục như vậy.Tuy nhiên, bạn có thể có nhiều người tiêu dùng khác nhau dựa trên Func<string>, trong trường hợp đó mọi thứ trở nên mơ hồ - chỉ vì người tiêu dùng yêu cầu Func<string> không có nghĩa là họ yêu cầu người được ủy quyền có cùng vai trò. Mặc dù máy móc có thể sử dụng DI với các đại biểu, điều xảy ra là các đại biểu ẩn vai trò ứng dụng. Vai trò biến mất, chỉ để lại cơ học.

Bạn có thể sau đó, theo đề nghị trong OP xác định các đại biểu tùy chỉnh cho mỗi vai trò, theo đề nghị của đại biểu này:

public delegate IEnumerable<Product> DGetProducts(); 

Tuy nhiên, nếu bạn đi mà tuyến đường, sau đó có gì là được. Một 'vai trò đại biểu' vẫn phải được xác định cho mỗi vai trò. Ngược lại rằng để xác định một giao diện tương tự và nó nên được rõ ràng rằng tiết kiệm chỉ là một vài dấu ngoặc nhọn và một định nghĩa phương pháp rõ ràng:

public interface IProductSource { IEnumerable<Product> GetProducts(); } 

Đó không phải là rất nhiều chi phí (nếu có).

Bạn cũng có thể muốn có một cái nhìn tại cuộc thảo luận này: http://thorstenlorenz.wordpress.com/2011/07/23/dependency-injection-is-dead-long-live-verbs/

+0

Cảm ơn câu trả lời của bạn, Mark. Tôi thực sự không cố gắng để đi đến một cách tiếp cận hoàn toàn chức năng, nhưng thử nghiệm với một cách tiếp cận lai. Cố gắng để cảm nhận được những thăng trầm và nhược điểm của việc sử dụng các hàm và các đối tượng cho các khái niệm khác nhau, và thấy chúng tương tác tốt như thế nào. – ninjeff

+0

Đối với chi phí của các giao diện, nó dài hơn về chiều dài của các lớp thực hiện chứ không phải là các giao diện. – ninjeff

1

Dựa trên con trỏ của Krzysztof, tôi có thể viết trình kích hoạt tùy chỉnh để xử lý việc này. Nguồn là trên github.

Đăng ký đại biểu tĩnh như thế này (làm theo tấm gương trong câu hỏi):

container.Register(Component. 
    For<DGetProducts>(). 
    ImplementedBy(typeof(DataContextFunctions)). 
    Named("GetProducts"). 
    Activator<StaticDelegateActivator>()); 

Mã này phần lớn là viết lại của DefaultComponentActivator. Tôi cũng cần sao chép-dán DependencyTrackingScope ra khỏi mã nguồn Windsor, vì nó nằm bên trong.

"Thử nghiệm" đơn lẻ trong dự án Kiểm tra chỉ bao gồm một trường hợp sử dụng duy nhất. Tôi không nghi ngờ nó sẽ làm việc cho các kịch bản tiên tiến hơn như proxy.

Tôi đã chấp nhận câu trả lời của Krzysztof, vì hướng dẫn của ông đã dẫn đến giải pháp.

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