依赖注入(DI)QUOT;友好和QUOT;图书馆 [英] Dependency Inject (DI) "friendly" library
问题描述
我在琢磨一个C#库的设计,这将有几个不同的高级功能。当然,那些高层次的功能将使用 SOLID 一流的设计原则,尽可能地实现。因此,很可能会向消费者直接定期使用类,而支持类即是那些比较常见的最终用户类别的依赖关系。
I'm pondering the design of a C# library, that will have several different high level functions. Of course, those high-level functions will be implemented using the SOLID class design principles as much as possible. As such, there will probably be classes intended for consumers to use directly on a regular basis, and "support classes" that are dependencies of those more common "end user" classes.
现在的问题是,什么是设计库的最佳方式所以它是:
The question is, what is the best way to design the library so it is:
- DI不可知论者 - 虽然加一个或两个普通DI库(StructureMap,Ninject等)似乎是合理的基本的支持,我希望消费者能够使用图书馆的任何DI框架 。
- 非DI可用 - 如果库的消费者不使用DI,图书馆应该还是尽可能容易使用,减少用户需要做的只是创建所有这些不重要的依赖关系的工作量得到他们想要使用真正的类。
我目前的想法是提供一种几DI登记模块为共同的DI库(例如一个StructureMap注册表,一Ninject模块),和一组或工厂类,都是非DI和包含耦合到那些几工厂。
My current thinking is to provide a few "DI registration modules" for the common DI libraries (e.g a StructureMap registry, a Ninject module), and a set or Factory classes that are non-DI and contain the coupling to those few factories.
思考?
推荐答案
这其实很简单,一旦你了解DI即将做的的模式和原则,而不是技术。
This is actually simple to do once you understand that DI is about patterns and principles, not technology.
要设计一个DI容器不可知的方式API,请按照下列总体原则:
To design the API in a DI Container-agnostic way, follow these general principles:
计划到接口,而不是实施
这原则实际上是从设计模式的报价(从内存虽然)的,但它应该永远是你的真正的目标即可。的 DI只是实现这一目的的手段的
This principle is actually a quote (from memory though) from Design Patterns, but it should always be your real goal. DI is just a means to achieve that end.
应用的好莱坞原则
好莱坞原则DI方面表示:不要调用DI容器,它会叫你的
The Hollywood Principle in DI terms says: Don't call the DI Container, it'll call you.
不要直接从您的code内调用容器要求的依赖。要求它隐含使用的构造器注入
Never directly ask for a dependency by calling a container from within your code. Ask for it implicitly by using Constructor Injection.
使用构造函数注入
当你需要一个依赖,要求它的静态的通过构造函数:
When you need a dependency, ask for it statically through the constructor:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
注意,服务类如何保证其不变量。一旦一个实例被创建,依赖关系是保证是因为卫队条款和只读
关键字。
使用抽象工厂,如果你需要一个短暂的对象
与构造器注入注入依赖往往是长期存在的,但有时你需要一个短暂的对象,或者构造的基础上只在运行时已知的值的依赖。
Dependencies injected with Constructor Injection tend to be long-lived, but sometimes you need a short-lived object, or to construct the dependency based on a value known only at run-time.
请参阅this了解更多信息。
只在最后一刻负责撰写
请分离直到最后的对象。通常情况下,你可以等待,并在应用程序的入口点连线一切。这就是所谓的构成根
Keep objects decoupled until the very end. Normally, you can wait and wire everything up in the application's entry point. This is called the Composition Root.
下面的更多细节:
- Where should I do Injection with Ninject 2+ (and how do I arrange my Modules?)
- Design - Where should objects be registered when using Windsor
简化使用门面
如果您觉得导致API成为新手太复杂,可以随时提供一些门面类,封装常用的依赖性组合。
If you feel that the resulting API becomes too complex for novice users, you can always provide a few Facade classes that encapsulate common dependency combinations.
要提供具有高度可发现的灵活门面,你可以考虑提供流利的建设者。事情是这样的:
To provide a flexible Facade with a high degree of discoverability, you could consider providing Fluent Builders. Something like this:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
这将允许用户通过写创建一个默认富
This would allow a user to create a default Foo by writing
var foo = new MyFacade().CreateFoo();
这将,但是,很发现,它可能提供一个自定义的依赖性,你可以写
It would, however, be very discoverable that it's possible to supply a custom dependency, and you could write
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
如果你想象MyFacade类封装了很多不同的依赖关系,我希望这是清楚它将如何提供合适的默认设置,同时还使得可扩展性发现。
If you imagine that the MyFacade class encapsulates a lot of different dependencies, I hope it's clear how it would provide proper defaults while still making extensibility discoverable.
FWIW,长写这个答案后,我在此对扩展的概念,并写了一个更长的博客文章的 DI-友好图书馆和了解 DI-友好的框架同伴后一>
FWIW, long after writing this answer, I expanded upon the concepts herein and wrote a longer blog post about DI-Friendly Libraries, and a companion post about DI-Friendly Frameworks.
这篇关于依赖注入(DI)QUOT;友好和QUOT;图书馆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!