Dependency injection 与接口、工厂和控制反转混淆

Dependency injection 与接口、工厂和控制反转混淆,dependency-injection,inversion-of-control,interface,factory,Dependency Injection,Inversion Of Control,Interface,Factory,使用接口是删除依赖项的一种非常简单的方法,但是当您的一个类需要一个接口未定义的方法时会发生什么?如果您使用的是构造函数注入或工厂,那么如何在不强制转换的情况下访问该额外方法?这可能吗 下面是一个有这个问题的工厂的例子。我是在尝试做一些不可能的事情吗?谢谢你的帮助 interface IFoo { int X { get; set; } int Y { get; set; } } public class A : IFoo { int X { get; set; }

使用接口是删除依赖项的一种非常简单的方法,但是当您的一个类需要一个接口未定义的方法时会发生什么?如果您使用的是构造函数注入或工厂,那么如何在不强制转换的情况下访问该额外方法?这可能吗

下面是一个有这个问题的工厂的例子。我是在尝试做一些不可能的事情吗?谢谢你的帮助

interface IFoo {
    int X { get; set; }
    int Y { get; set; }
}

public class A : IFoo {
    int X { get; set; }
    int Y { get; set; }
}

public class B : IFoo {
    int X { get; set; }
    int Y { get; set; }
    int Z { get; set; }
}

public static class FooFactory {
    public static IFoo GetFoo(string AorB) {
        IFoo result = null;
        switch (AorB) {
            case "A":
                result = new A();
                break;
            case "B":
                result = new B();
                break;
        }
        return result;
    }
}

public class FooContainer {
    private IFoo foo;

    public FooContainer(IFoo foo) {
        this.foo = foo;
    }

    /* What methods would you define here. I'm new to IoC. */
}

public class Main(...) {
    int x,y,z;
    IFoo fooA = FooFactory.GetFoo("A");
    x = foo.X;
    y = foo.Y;

    IFoo fooB = FooFactory.GetFoo("B");
    x = foo.X;
    y = foo.Y;
    z = foo.Z; /* Does not compile */
    z = ((B)foo).Z; /* Compiles, but adds unwanted dependency */
}

你确实需要施展才华。这是正常的,有时是必要的。不过,这通常是有问题的迹象

理想情况是,如果一个方法/例程获取/返回一个接口,那么您的逻辑只关心该接口公开的成员。如果在该方法中,您发现自己正在检查确切的类型,以便可以强制转换为该类型,并根据类型调用不同的成员,那么可能是出了问题

假设您有一个IContact接口,实现该接口的一些实体是您的类Customer、Purchaser和Contractor。如果您的SendChristmasCard方法采用IContact,那么它应该只关心IContact成员。如果此方法中的逻辑正在对
obj.GetType().ToString
执行select Case以确定它是否是客户,则:

  • 该功能可能应该在以客户为中心的代码库中结束,将客户对象作为参数。(在您的示例中,对A类和B类采取行动将有单独的逻辑。)
  • IContact应该定义SendChristmasCard方法将调用的公共成员,并且完全不知道特定对象内部的逻辑。实现IContact的每个类将以不同的方式实现这些成员。(在您的示例中,类A还将实现属性B,但它不会对它做任何事情。)
  • 在一个方法返回一个接口而您使用该对象的情况下,上面的内容仍然适用,但根据我的经验,最好时不时地忍受转换。你通过“修复”情况而增加的复杂性可能会使情况更加复杂。我想说的是,逻辑的进一步上升和非标准化,只是采取简单的方法。SendChristmasCard显然不是核心功能;如果IContact factory方法是提供所有联系人的唯一简便方法,那么可以使用它,将其传递给SendChristmassCard(IContact联系人),然后在里面检查类型,说“今年从你那里买的东西很棒”或“今年卖给你的东西很棒”等等。但是如果这是你系统的核心逻辑,你真的需要寻找更好的方法


    但是,请查看该方法,它在这种情况下会有所帮助。

    如果您试图访问接口不可用的方法,请不要使用工厂。显然,您正在对依赖项进行硬编码……所以只需使用它(,但只有在确实需要时才使用它)

    没有必要把事情复杂化


    试图回溯到对象类型而不是接口将引入依赖项…但它将隐藏它而不是公开依赖项。如果将来有人更改了工厂,并且您的调用返回了不同的对象类型,那么您的代码现在将以一种不明显的方式中断。

    当您遇到向下转换对象的需要时,这通常是API可能更好的迹象

    向下转换抽象类型违反了。通常,最好通过更改相关接口的样式来解决此问题。与其公开大量的属性和查询(术语上),不如将重点转向更面向命令的方法。这是最新的

    您不必让
    IFoo
    公开X和Y属性,而是可以针对一组命令重新定义其行为:

    public interface IFoo
    {
        void DoStuff();
    
        void DoSomethingElse(string bar);
    
        void DoIt(DateTime now);
    }
    
    然后,具体的实现可以封装他们想要的任何数据(如X、Y或Z属性),而无需消费者了解它们


    当接口变得太大时,是时候应用或了。

    这是一个泄漏的抽象、缺乏封装和/或违反单一责任原则。换句话说,如果你的代码是以这种方式设计的,那么它就是你代码中的一个设计问题,你应该重新构造它,使它不需要在这个上下文中调用额外的方法。我遇到的问题不是“方法”本身,而是额外的属性。我们当前的解决方案是让接口包含属性的说明。这样,实现类可以包含它们所需的任何属性,而不会破坏接口。我只是希望有一个聪明的方法不用字典就能做到这一点。谢谢,这真的很有帮助。事实上,这是我已经开始遵循的模式。我喜欢引用坚实的原则。