代码之家  ›  专栏  ›  技术社区  ›  mikesigs

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

  •  3
  • mikesigs  · 技术社区  · 15 年前

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

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

    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 回复  |  直到 15 年前
        1
  •  1
  •   Mark Seemann    15 年前

    当您遇到需要向下转换对象时,通常是一个迹象,表明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 Principle Single Responsibility Principle .

        2
  •  6
  •   Patrick Karcher    15 年前

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

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

    假设你有一个 I接触 接口和一些实现该接口的实体是类 客户 , 买方 承包商 . 如果你有 发送圣诞卡 采用IContact的方法,它应该 只关心IContact成员 . 如果在该方法中有逻辑正在对 obj.GetType().ToString 要了解它是否是客户,请执行以下操作:

    1. 该功能可能在 客户 -以客户对象为参数的代码基的中心端。(在您的示例中,对A级和B级的操作有单独的逻辑。)
    2. IContact应定义 发送圣诞卡 方法将调用,并且完全不了解在特定对象内部进行的逻辑。实现的每个类 I接触 将以不同的方式实现这些成员。(在您的示例中,类A还将实现属性B,但它不会对它做任何操作。)

    如果一个方法 退货 一个接口和你使用的对象,上面仍然适用,但在我的经验中 可以 , 偶尔 最好能忍受铸造。通过“修复”情况而增加的复杂性可能会使情况更加复杂。我想说的是,逻辑越高,越不规范,只要采取简单的方法就可以了。 发送圣诞卡 显然不是核心功能;如果一个IContact工厂方法只是一个方便的方法,可以提供所有联系人,那么可以使用它,将其传递给 发送圣诞卡(IContact联系人) 在里面,检查有没有写着“今年从你这里买东西很棒”或“今年卖给你的东西很棒”等字样,但是 如果这是系统中的核心逻辑,那么您真的需要寻找更好的方法 .

    查看 Decorator Pattern 不过,在这种情况下,这是有帮助的。

        3
  •  1
  •   Justin Niessner    15 年前

    如果您试图访问接口不可用的方法,则不要使用工厂。很明显,你很难对依赖项进行编码……那就继续吧。( 但只有在必要的时候 )

    不必过分复杂化。

    试图转换回对象类型而不是接口将引入依赖关系……但它将隐藏它,而不是明显地暴露依赖关系。如果将来有人更改了工厂,而您的调用返回了不同的对象类型,那么您的代码现在将以一种不明显的方式中断。