与接口,工厂和控制反转混淆

时间:2010-03-01 17:37:50

标签: 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; }
    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 */
}

3 个答案:

答案 0 :(得分:6)

你确实需要施放。这是正常的,有时是必要的。这通常表明事情是错误的。

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

假设您有 IContact 界面,实现此功能的一些实体是您的类客户购买者承包商。如果你有一个采用IContact的 SendChristmasCard 方法,它应该只关心IContact成员。如果您在此方法中有逻辑,在obj.GetType().ToString上执行选择案例以确定它是否是客户,那么:

  1. 该功能可能应该在代码库的 Customer 中心方面结束,将Customer对象作为参数。 (在你的例子中,对A类和B类采取行动时会有单独的逻辑。)
  2. IContact应定义 SendChristmasCard 方法将调用的公共成员,并完全不知道特定对象内部的逻辑。实现 IContact 的每个类都将以不同方式实现这些成员。 (在你的例子中,A类也会实现属性B,但它不会对它做任何事情。)
  3. 如果方法返回一个接口而你使用该对象,上面仍然适用,但根据我的经验,它可以然后< / em>,最好忍受铸造。通过“修复”情况添加的复杂功能可能会使其更加复杂。我会说逻辑的进一步向上和非标准,只是采取简单的方法。 SendChristmasCard 显然不是核心功能;如果IContact工厂方法只是方便的方法给你所有的联系人,那么可能只是使用它,传递给 SendChristmassCard(IContact Contact),并在里面检查类型说“它很高兴今年给你买“或者今年给你很好的销售”等等。但如果这是你系统的核心逻辑,你真的需要寻找更好的方法

    但请查看Decorator Pattern,这可能有助于此类情况。

答案 1 :(得分:1)

如果您尝试访问接口无法使用的方法,请不要使用Factory。你显然很难编写一个依赖...所以只需要它(但只有在真的有必要时)。

无需过度复杂化。

尝试强制转换为Object类型而不是接口将引入依赖...但它会隐藏它而不是显然暴露依赖。如果有人在将来更改了Factory并且您的调用返回了不同的Object类型,那么您的代码现在将以非显而易见的方式中断。

答案 2 :(得分:1)

当您遇到向下转换对象的需求时,通常表明API可能会更好。

向下转换抽象类型违反了Liskov Substitution Principle。通常可以通过更改相关接口的样式来解决此问题。而不是公开大量属性查询(在CQS术语中),而是将注意力转向更强命令的方法。这是Hollywood Principle

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

public interface IFoo
{
    void DoStuff();

    void DoSomethingElse(string bar);

    void DoIt(DateTime now);
}

然后,具体实现可以封装他们想要的任何数据(例如X,Y或Z属性),而消费者无需了解它们。

当界面变得太大时,是时候应用Interface Segregation PrincipleSingle Responsibility Principle