【问题标题】:Should I declare properties as Interfaces or as Base classes (when they implement both)?我应该将属性声明为接口还是基类(当它们同时实现时)?
【发布时间】:2016-03-22 03:51:54
【问题描述】:

我有一个类以类似的方式使用一组属性(为简洁起见,示例中只显示了两个)。 一般行为是在基类中定义的,而具体行为是在特定接口中定义的

问题是:如果我将它们声明为基类,我必须将它们转换为接口才能调用接口方法。现在如果我将它们声明为接口,当我想调用基方法时,我必须将它们转换为基类。

我在这里使用接口时的目标是提高可测试性(稍后使用依赖注入),并培养“对接口编程”的习惯,但我无法决定哪种方式最好,或者即使整个理由是一开始就很好。

public class Conductor
{
    // These properties inherit from base class
    // and implement one specific interface each:

    // declared as interface:
    IPlotterHelper  _plotter_helper = new PlotterHelper();

    // declared as base class:
    Helper _file_writer_helper = new FileWriterHelper();


    // When using handlers defined in specific interfaces:

    // have to cast this:
    this.NewFrame   += ((IPlotterHelper)_file_writer_helper).ProcessFrame();

    // but not this:
    this.NewSamples += _plotter_helper.ProcessSamples();



    // While when using handlers from the base class

    // have to cast this to the base class (since it is an interface):
    this.CommandSent += ((Helper)_plotter_helper).RunCommand;

    // but not this:
    this.CommandSent += _file_writer_helper.RunCommand;
}



internal class FileWriterHelper : Helper, IFileWriterHelper
{
    IFileWriterHelper.ProcessFrame()
    {
        // ...
    }

    // ...
}

internal class PlotterHelper : Helper, IPlotterHelper
{
    IPlotterHelper.ProcessSamples ()
    {
        ///
    }

    // ...
}

internal class Helper
{
    internal void RunCommand()
    {
        // ...
    }
}

【问题讨论】:

  • 仅供参考,在命名变量时不要使用下划线,除非在开头(表示它们是字段)。 _plotter_helper 错了,应该是_plotterHelper 等等。
  • “特定行为在特定接口中定义”是什么意思,因为接口不定义行为?
  • 如果您发布的代码能够真正编译,那就太好了。现在是狗的早餐。
  • @Enigmativity 我表达得很糟糕。我想说的是“多态性”,每个接口实现者都会以不同的方式实现给定的方法。

标签: c# inheritance interface casting


【解决方案1】:

很难准确地看出你想要做什么,但看起来这可能是一个更合适的设计:

public class Conductor
{
    private IPlotterHelper _plotter_helper = new PlotterHelper();

    private IFileWriterHelper _file_writer_helper = new FileWriterHelper();

    public void Conduct()
    {
        _file_writer_helper.ProcessFrame();
        _file_writer_helper.RunCommand();
        _plotter_helper.ProcessSamples();
        _plotter_helper.RunCommand();
    }
}

internal interface IHelper
{
    void RunCommand();
}

internal interface IFileWriterHelper : IHelper
{
    void ProcessFrame();
}

internal interface IPlotterHelper : IHelper
{
    void ProcessSamples();
}


internal class FileWriterHelper : Helper, IFileWriterHelper
{
    public void ProcessFrame()
    {
    }
}

internal class PlotterHelper : Helper, IPlotterHelper
{
    public void ProcessSamples()
    {
    }
}

internal class Helper : IHelper
{
    public void RunCommand()
    {
    }
}

【讨论】:

  • 感谢您的回答!我想我在解释时没有说清楚,但事实是:RunCommandHelper 中定义了一个通用实现(现在我想这是一个非常糟糕的名字)。之所以如此命名,是因为指挥员向它发出命令,因此它“帮助”执行协同动作(以及其他助手)。但是每个专门的助手也有专门的成员来做额外的东西(比如ProcessSamplesProcessFrames,这些东西与命令运行功能是分开的。
  • 哇,太好了!即使现在我正在以其他方式解决问题,我相信这完全正确地回答了这个问题。谢谢!
【解决方案2】:

接口和抽象类的目的相同:提供抽象。使抽象连贯一致,如果 基类 有公共成员,请确保它们也在接口上。

但是,为什么我需要抽象类或接口呢? - 对。摆脱基类或接口 - 您可能并不真正需要两者。我会放弃基类。

【讨论】:

    【解决方案3】:

    当我希望在接口中具有默认行为时,我通常会考虑使用带有受保护的辅助方法和一组抽象接口方法或“接口”方法的默认实现的抽象基类。即使我只从一个具体的实现开始,也可能是这种情况。

    许多人将抽象类和接口视为同一个广泛的实现选项类别。

    抽象类的问题是单继承,所以我们应该只使用抽象类,如果它真的是类层次结构的基础(即使是浅层次的)。接口可用于装饰具有共同行为的类(来自不同的层次结构)。

    对于测试,我认为使用接口伪造和使用抽象类伪造之间没有太大区别 - 但这可能取决于您的测试基础架构。

    在这种情况下,我会使用一个抽象类并忘记接口(除非它已经存在,在这种情况下你别无选择)。

    【讨论】:

    • 好的,这就是我最终要做的。而不是IPlotterHelper,我有一个直接的PlotterHelper 子类,所以我不必再转换回Helper。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2017-11-10
    • 2019-03-11
    • 2011-12-24
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    相关资源
    最近更新 更多