Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IoC容器:创建的实例的结构略有不同_C#_Dependency Injection_Inversion Of Control_Factory - Fatal编程技术网

C# IoC容器:创建的实例的结构略有不同

C# IoC容器:创建的实例的结构略有不同,c#,dependency-injection,inversion-of-control,factory,C#,Dependency Injection,Inversion Of Control,Factory,我正在学习IoC、DDD和AOP概念。我读过很多文章、文档、Ninject手册(我只限于使用.NET3.5)、尝试过一些东西等等 很难一下子把所有事情都想清楚,但动机、概念和技术问题都有点清楚。我一直觉得我错过了什么 首先,据我所知,IoC容器的目的是建立初始对象结构吗 比如,在composition根目录中设置容器,创建“main”对象,该对象一直由IoC容器连接 那么,据我所知,以后所有的对象都是用工厂实例化的?尽管我无法控制自己将工厂视为服务定位器的一个案例(最终被认为是反模式的,顺便说一

我正在学习IoC、DDD和AOP概念。我读过很多文章、文档、Ninject手册(我只限于使用.NET3.5)、尝试过一些东西等等

很难一下子把所有事情都想清楚,但动机、概念和技术问题都有点清楚。我一直觉得我错过了什么

首先,据我所知,IoC容器的目的是建立初始对象结构吗

比如,在composition根目录中设置容器,创建“main”对象,该对象一直由IoC容器连接

那么,据我所知,以后所有的对象都是用工厂实例化的?尽管我无法控制自己将工厂视为服务定位器的一个案例(最终被认为是反模式的,顺便说一句,它被用作IoC容器的核心机制)

因此,问题是: 如果我想创建一个结构稍有不同的实例,例如

interface IFoo{}
interface IBar{}

class SomeClass
{
    SomeClass(IFoo obj){...}   
}

class Foo : IFoo
{
    Foo(IBar obj){...}   
}

class Bar : IBar
{
}

class FooBar : IBar //  also implements IBar interface
{
}
因此,初始绑定配置是创建
SomeClass.Foo.Bar
结构。假设,我还需要
SomeClass.Foo.FooBar
。我该怎么办?我能想到的选择是:

  • “就地”重新配置绑定:仅此而已

  • 为顶级类提供一个构造函数参数,该参数具有整个结构的配置。那太可怕了。除了所有后续构造函数(以及所有其他项目类构造函数,我确信最终)都必须有一个额外的参数之外,还没有清楚地看到它将如何工作,并且没有使用一些肮脏的技巧

  • 替换创建对象后所需的内容。它要么违反了德墨忒尔定律(对此我不太关心,但在这种情况下,它太粗鲁了)和其他一些原则,要么,在一般情况下,根本不可能

  • 使用以某种方式配置的工厂。它只是将需求本身延迟/转移到代码中的稍后/其他位置

  • 使用某种上下文/常规绑定。我看到的一个解决方案(还没有测试)是一直到“激活根”的顶部,检查是什么创建了层次结构。m、 我们必须为顶级类制作装饰器,以便容器检查其类型并相应地进行操作。实际上,容器可以通过一种方式进行配置,即它通过“解析”顶级接口的名称来决定注入哪个具体实例。差不多

    ICfg_混凝土类型1_混凝土类型2_

这里的问题(除此之外,它看起来像黑客):

a) 我们必须引进一些非淫秽/用户友好的记忆系统

b) 我们必须为每个具有此“功能”的工厂设置规则/装饰器(但看起来我们至少可以通过规则简化流程)

c) 它类似于我在使用约定而非配置的反射,我避免了这种做法,并将其视为一种黑客行为


或者我们可以使用属性来设置它。或者可能是我不知道什么。

您通常会使用某种标签系统


您通常会使用某种标签系统


您通常会使用某种标签系统


您通常会使用某种标签系统

首先,据我所知,IoC容器的目的是建立初始对象结构吗

暂时忘掉IoC容器吧。依赖注入与使用工具无关。它首先是关于应用原则和模式的。依赖注入背后的驱动力是坚实的原则。我甚至可以在不使用IoC容器的情况下启动您的应用程序,并且只有在真正有理由这样做时才开始使用IoC容器。这意味着您只需手动构建对象图。这样做的正确位置是在作文的根目录中。这应该是组成对象图的单一位置

请注意,这个建议来自于他自己

那么,据我所知,以后所有的对象都是用工厂实例化的

在实践依赖注入时,您将看到使用工厂的需求实际上最小化了。它们仍然是有用的,但我现在只是很少使用它们

原因是工厂通常只添加一个额外的(无用的)抽象层

当开始使代码更松散耦合时,开发人员倾向于使用工厂,如下所示:

public class SomeClass
{
    public void HandleSomething() {
        IFoo foo = FooFactory.Create();
        foo.DoSomething();
    }
}
public class SomeClass
{
    private readonly IFooFactory fooFactory;
    public SomeClass(IFooFactory fooFactory) {
        this.fooFactory = fooFactory;
    }

    public void HandleSomething() {
        IFoo foo = this.fooFactory.Create();
        foo.DoSomething();
    }
}
new SomeClass(
    new Foo(
        new Bar()));
new SomeClass(
    new Foo(
        new BarProxy(
            new Bar(),
            new FooBar()));
public class BarProxy
{
    private readonly IBar left;
    private readonly IBar right;
    public BarProxy(IBar left, IBar right) {
        this.left = left;
        this.right = right;
    }

    public void BarMethod(BarOperation op) {
        this.GetBar(op).BarMethod(op);
    }

    private IBar GetBar(BarOperation op) {
        return op.SomeValue ? this.left : this.right;
    }
}
尽管这允许
Foo
实现与
SomeClass
解耦,
SomeClass
仍然强烈依赖
FooFactory
。这仍然使得
SomeClass
难以测试,并降低了可重用性

遇到这样的问题后,开发人员通常开始抽象
FooFactory
类,如下所示:

public class SomeClass
{
    public void HandleSomething() {
        IFoo foo = FooFactory.Create();
        foo.DoSomething();
    }
}
public class SomeClass
{
    private readonly IFooFactory fooFactory;
    public SomeClass(IFooFactory fooFactory) {
        this.fooFactory = fooFactory;
    }

    public void HandleSomething() {
        IFoo foo = this.fooFactory.Create();
        foo.DoSomething();
    }
}
new SomeClass(
    new Foo(
        new Bar()));
new SomeClass(
    new Foo(
        new BarProxy(
            new Bar(),
            new FooBar()));
public class BarProxy
{
    private readonly IBar left;
    private readonly IBar right;
    public BarProxy(IBar left, IBar right) {
        this.left = left;
        this.right = right;
    }

    public void BarMethod(BarOperation op) {
        this.GetBar(op).BarMethod(op);
    }

    private IBar GetBar(BarOperation op) {
        return op.SomeValue ? this.left : this.right;
    }
}
这里使用了
IFooFactory
抽象,它是使用构造函数注入注入的。这允许
SomeClass
完全松耦合

SomeClass
但是现在有两个外部依赖项。它既知道
IFooFactory
又知道
IFoo
。这重复了
SomeClass
的复杂性,但没有令人信服的理由这样做。在编写单元测试时,我们会立即注意到复杂性的增加。我们现在将突然不得不模拟两种不同的行为,并对它们进行测试

因为我们在这里练习构造函数注入,所以我们可以简化
SomeClass
——没有任何缺点——如下所示:

public class SomeClass
{
    private readonly IFoo foo;
    public SomeClass(IFoo foo) {
        this.foo = foo;
    }

    public void HandleSomething() {
        this.foo.DoSomething();
    }
}
长话短说,尽管Factory设计模式仍然有效和有用,但您几乎不需要它来检索

尽管我无法控制自己将工厂视为服务定位器的一个案例

不可以。工厂不是服务定位器。工厂和定位器之间的区别在于,使用工厂只能构建一种特定类型的对象,而l