【问题标题】:Concrete type or Interface?具体类型还是接口?
【发布时间】:2009-01-02 23:52:14
【问题描述】:

我有以下用例,大量代码紧密耦合在具体类型(比如 Concrete1)上。后来发现具体类型需要改,所以定义了一个接口。例如

Class ABC {

 virtual int foo() = 0;
 virtual int getType() = 0;

}

class Concrete1 : public ABC {

    int foo() {
    ... }
    int getType() {
      return 1;
    }  

} 
class Concrete2 : public ABC {
    int foo() {
    ... }
    int getType() {
      return 2;
    }
 }

使用静态工厂模式来创建对象。因此,所有创建对象 new Concrete1 的地方都被替换为 ABCFactory::createType()。

现在代码中有很多地方我需要检查 createType 返回的对象是 Concrete1 还是 Concrete2 并相应地执行相关逻辑(所以代码中有很多 if else :( )。

作为此更改的一部分,我想避免代码中出现大量 if else。有什么建议?

最困扰我的是

if (abc.getType() == 1) {
    ...
} else if (abc.getType() ==2) {
    ...
}

【问题讨论】:

    标签: c++


    【解决方案1】:

    使用接口的全部意义在于您可以使用多态性,这意味着您永远不必检查实例的类型。这样做会产生很大的代码异味(请参阅 Fowlers Refacotring)。将条件逻辑移至具体类,并将处理它的函数添加到接口

    编辑(添加代码示例,因为最初的帖子是从手机完成的):

    你正在尝试做的事情:

    void Main(string[] args)
    {
       Bird bird = BirdFactory.GetPigeon();
       if (bird.GetType().Equals(typeof(Duck)))
       {
          Console.WriteLine("quack");
       }
       else if (bird.GetType().Equals(typeof(Pigeon)))
       {
          Console.WriteLine("coo coo");
       }
    }
    

    请尝试:

    interface Bird
    {
        void Speak();
    }
    
    class Duck : Bird
    {
        void Speak()
        {
            Console.Write("quack");
        }
    }
    
    class Pigeon : Bird
    {
        void Speak()
        {
            Console.Write("coo coo");
        }
    }
    
    void Main(string[] args)
    {
        Bird bird = BirdFactory.GetPigeon();
        bird.Speak();
    }
    

    【讨论】:

    • +1 表示好的变量名。它比 foos 和 bar 更容易理解。
    【解决方案2】:

    将... 放入另一个虚拟方法的实现中:

    if (abc.getType() == 1) {
        ... // A
    } else if (abc.getType() == 2) {
        ... // B
    }
    

    将 A 和 B 像这样放置:

    class ABC {
     virtual int foo() = 0;
     virtual void doIt() = 0; // choose a proper name
    };
    
    class Concrete1 : public ABC {
        int foo() {
        ... }
        void doIt() {
        ... // A
        }
    };
    
    class Concrete2 : public ABC {
        int foo() {
        ... }
        void doIt() {
        ... // B
        }
     };
    

    然后将你的 if 更改为

    abc.doIt();
    

    正如另一个人所说,这正是动态调度的重点!除了更简洁之外,它也永远不会“忘记”处理类型。进行切换时,您可能会默默地不处理特定类型,因为在引入新实现时您错过了更新该位置的代码。还记得在 ABC 中有一个虚拟析构函数。

    【讨论】:

      【解决方案3】:

      与其他答案一致,在我看来,至少 if/else 块中的一些代码需要作为新的虚函数移入具体类中。这将允许您利用多态性,而不是使用自制反射模式切换类型。

      【讨论】:

        【解决方案4】:

        你能把你在类外检测对象类型的地方移到类中吗?这样功能(显然取决于特定类)实际上与适当的类相关联?

        【讨论】:

          【解决方案5】:

          如果您在运行时检查和/或打开类型,您可能需要考虑使用运行时类型信息(如果它可用于您的编译器)。它确实增加了一些开销,但它完成了您正在尝试做的事情,而无需创建或维护自定义方法。

          与纯粹主义者不同,我并不是那种“将功能放在类中以便您可以使用多态性来绕过类型切换”的人。不过,两者都是有效的方法(尽管“类中”方法并不总是可行的,和/或在概念上是干净的)。

          【讨论】:

          • 不赞成打开类型的原因是它会迅速增加代码的复杂性并迅速降低可维护性。当你开始切换类型时,你最终会在你的代码中放置开关,如果有什么改变,现在你有很多地方需要改变
          猜你喜欢
          • 2016-08-03
          • 2011-02-10
          • 1970-01-01
          • 2021-05-20
          • 1970-01-01
          • 2012-11-29
          • 2020-01-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多