依赖注入(DI)“友好"图书馆 [英] Dependency Inject (DI) "friendly" library

查看:22
本文介绍了依赖注入(DI)“友好"图书馆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在考虑设计一个 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 库(例如 StructureMap 注册表、Ninject 模块)提供一些DI 注册模块",以及一组非 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.

切勿通过从代码中调用容器来直接请求依赖项.使用构造函数注入隐式地请求它.

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; }
    }
}

注意 Service 类如何保证其不变量.一旦创建了一个实例,由于 Guard 子句和 readonly 关键字的组合,依赖项保证可用.

Notice how the Service class guarantees its invariants. Once an instance is created, the dependency is guaranteed to be available because of the combination of the Guard Clause and the readonly keyword.

如果你需要一个短暂的对象,请使用抽象工厂

使用构造函数注入注入的依赖项往往是长期存在的,但有时您需要一个短期存在的对象,或者根据仅在运行时已知的值来构建依赖项.

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.

参见 这是了解更多信息.

只在最后负责任的时刻写作

保持对象解耦直到最后.通常,您可以等待并在应用程序的入口点连接所有内容.这称为组合根.

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.

此处有更多详细信息:

使用 Facade 简化

如果您觉得生成的 API 对于新手用户来说过于复杂,您可以随时提供一些Facade 封装常见依赖组合的类.

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.

要提供具有高度可发现性的灵活 Facade,您可以考虑提供 Fluent Builders.像这样:

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);
    }
}

这将允许用户通过编写创建默认的 Foo

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-Friendly Libraries,以及关于 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)“友好"图书馆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆