【问题标题】:Abstract Base Class w/o Polymorphism没有多态性的抽象基类
【发布时间】:2011-09-09 12:05:26
【问题描述】:

为什么你会有一个抽象基类为只有一个(永远和永远)派生类的库定义一个接口?

【问题讨论】:

    标签: c++ inheritance interface polymorphism


    【解决方案1】:

    您可能希望将实现换成单元测试之类的东西

    【讨论】:

    • 你是说我会在哪里有类似 class Foo : public Base 和 class FooTest : public Base 的东西,我会在可能具有存根功能的单元测试程序中实例化 FooTest 吗?
    • 是的,或者使用像 googlemock 这样的模拟框架来为你做这件事
    • 你可以在没有特殊接口的情况下通过扩展使用的类来做到这一点(这也意味着你可以重用已经存在的功能)。
    【解决方案2】:

    您这样做的一个原因是为了可测试性。当依赖对象定义为接口时,测试依赖对象要简单得多。这提供了轻松模拟或存根的能力。

    【讨论】:

    • 过去,除非您考虑 IOC 和依赖注入,否则我想不出一个好的理由。这个类有没有可能在不改变签名的情况下改变它的实现?
    • 也许这只是我,但是扩展类本身并添加一些方法(或构造函数)来设置私有变量以进行测试通常不是更容易(并且更不容易出错)吗?或者,如果我想添加日志记录,我可以轻松地覆盖方法、处理日志记录并调用超类。否则,您必须两次实现通用功能,这在 imo 中听起来是个坏主意。
    • 我通常在 .NET 中工作,所以我更喜欢对我的模拟设置期望,然后重放行为。在 c++ 中,这可能完全不同。
    • 您不必编写其他毫无意义的接口来执行此操作。您也可以模拟具体的类(只要它们不是密封的,并且您要模拟的方法是virtual)。
    • 在 C# 中可以(与 Java 或 c++ 相反),您可能会遇到方法默认密封的问题,这对此有问题(并且使所有方法虚拟化并不是 c# 中特别好的编码风格你甚至可能无法控制班级)。但是,即使您想替换一个类的完整实现,也不需要像 Jason 所说的那样使用接口。而且经常有很多我可以通过这种方式重用的类中的功能。
    【解决方案3】:

    违反reused abstraction principle

    简而言之,不要这样做。

    那些说“用于测试”的人忽略了你可以替换

    Base < - > Derived
    Base < - > DerivedForMockingAndTesting
    

    Derived < - > DerivedForMockingAndTesting    
    

    也就是说,让您现有的实现 Derived 作为“抽象”,在单元测试中进行模拟和测试。

    【讨论】:

    • 如果Derived 的构造函数做了一些你不希望模拟做的事情怎么办?
    • 或者有非虚函数?
    【解决方案4】:

    如果您可以 100% 确定永远只有一个且完全是一个派生类?没有太多理由。但是:实际上,您几乎不会 100% 确定任何事情,当然也不是您代码的未来。

    【讨论】:

    • 如果您遇到不确定的未来点,您必须再次实现它,您也无法确定您提出的抽象将是正确的。
    • @Jason 当然,您很可能会发现您的接口本身存在严重问题。因此,虽然您没有摆脱所有问题,但您至少摆脱了一个问题。并且只是添加一个 Jason 提出的确切问题的示例:如果您环顾许多成熟的框架,您会发现具有 X2 之类的名称的接口正是在这种情况下创建的(可怕的拐杖,但仍然比您必须做的更好)如果您使用过类,请执行此操作!)。
    【解决方案5】:

    您可能会发现该类的不同版本需要二进制兼容。您可能还会发现,出于其他原因,您希望封装类的定义——例如,因为定义需要一个具有不良宏的标头,诸如此类,或者包含定义类所必需的标头的标头具有编译时间很长。

    例如,我编写了一个类来封装我的操作系统提供的功能 - 目前,例如动态加载和创建窗口。尽管对于一个编译目标(Windows 等)只有一个实现,但我选择使用运行时抽象,因为我想保证我的其余代码永远不会看到特定于平台的标头,并且Windows 标头充满了很多宏和东西,我不希望它们泄露出去。

    【讨论】:

      【解决方案6】:

      最明显的原因是能够把课程' 在源文件中实现,而不是在头文件中。就是这样 头文件中暴露的是抽象基类(和一个工厂函数 有必要构造它,但这可能是一个静态成员)。这 避免必须包含任何成员数据的头文件;粉刺 为此,成语在 C++ 中更为惯用,但使用抽象类,如 这远非未知,而且效果也很好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多