14

由于接口不能包含实现,在我看来这会导致从接口继承的类中的代码重复。在下面的示例中,假设设置从 Stream 读取的前 10 行左右是重复的。 尽量不要关注这里的措辞,而是关注在每个类之间创建重复代码是多么容易的概念。

例如:

public interface IDatabaseProcessor
{
   void ProcessData(Stream stream);
}
public class SqlServerProcessor : IDatabaseProcessor
{
    void ProcessData(Stream stream)
    {
      // setting up logic to read the stream is duplicated code
    }
}
public class DB2Processor : IDatabaseProcessor
{
    void ProcessData(Stream stream)
    {
      // setting up logic to read the stream is duplicated code
    }
}

我意识到为 ProcessData 使用抽象基类并添加非抽象成员是一种解决方案。但是,如果我真的,真的想改用界面怎么办?

4

7 回答 7

18

这是您希望同时使用接口和抽象基类的情况。

您同时拥有两者的唯一原因是另一个类不会共享抽象基代码,但会尊重接口。考虑:

public interface IDatabaseProcessor {
   void ProcessData(Stream stream);
}

public abstract class AbstractDatabaseProcessor : IDatabaseProcessor {
    public void ProcessData(Stream stream) {
      // setting up logic to read the stream is not duplicated
    }
}

public class SqlServerProcessor : AbstractDatabaseProcessor {
    //SqlServerProcessor specific methods go here
}

public class DB2Processor : AbstractDatabaseProcessor {
    // DB2Processor specific methods go here
}

public class NonSharedDbProcessor : IDatabaseProcessor {
    void ProcessData(Stream stream) {
      // set up logic that is different than that of AbstractDatabaseProcessor
    }
}

语法可能有点不对劲,我不是普通的 C# 用户。我是通过 OOP 标签来到这里的。

于 2012-08-21T21:12:07.647 回答
14

跨接口共享代码的最佳方式是通过无状态扩展方法。您可以一次构建这些扩展,并在所有实现接口的类中使用它,而不管它们的继承链如何。这就是 .NETIEnumerable<T>在 LINQ 中所做的,结果相当令人印象深刻。这种解决方案并不总是可行的,但您应该尽可能喜欢它。

共享逻辑的另一种方法是创建一个内部“助手”类。在您的情况下,这看起来是正确的选择:实现可以将内部共享代码调用为辅助方法,而无需复制任何代码。例如:

internal static class SqlProcessorHelper {
    public void StreamSetup(Stream toSetUp) {
        // Shared code to prepare the stream
    }
}
public class SqlServerProcessor : IDatabaseProcessor {
    void ProcessData(Stream stream) {
        SqlProcessorHelper.StreamSetup(stream);
    }
}
public class DB2Processor : IDatabaseProcessor {
    void ProcessData(Stream stream) {
        SqlProcessorHelper.StreamSetup(stream);
    }
}

助手类不需要是静态的:如果您的共享方法需要状态,您可以使您的助手成为一个常规类,并在您希望共享代码的接口的每个实现中放置它的一个实例。

于 2012-08-21T21:13:23.633 回答
2

正如您已经说过的,一种选择是使用基本抽象(甚至可能是非抽象)类。另一种选择是创建另一个实体来运行公共代码。在您的情况下,它可能是DataProcessor

internal class DataProcessor
{
    public void Do(Stream stream) 
    {
        // common processing here
    }
}
public class SqlServerProcessor : IDatabaseProcessor
{
    void ProcessData(Stream stream)
    {
        new DataProcessor().Do(stream);
    }
}
public class DB2Processor : IDatabaseProcessor
{
    void ProcessData(Stream stream)
    {
        new DataProcessor().Do(stream);
    }
}
于 2012-08-21T21:12:56.853 回答
0

正如您所指出的,一个abstract类提供了解决方案。如果您“真的,真的想”,您可以使用界面;没有什么能阻止它。你的abstract班级应该实现IDatabaseProcessor.

于 2012-08-21T21:11:53.967 回答
0

如果您真的不想使用基类,同时仍然能够从共享代码访问private和/或protected成员,那么唯一可用的其他选项是代码生成。它是内置的 VS(已经存在了很长时间)并且非常强大。

于 2012-08-21T21:12:23.747 回答
0

只需使用带有抽象基类的接口:

public interface IDatabaseProcessor
{
   void ProcessData(Stream stream);
}
public abstract class AbstractDatabaseProcessor : IDatabaseProcessor
{
    public virtual void ProcessData(Stream stream)
    {
      // setting up logic to read the stream is duplicated code
    }
}
public class SqlServerProcessor : AbstractDatabaseProcessor
{
    public void ProcessData(Stream stream)
    {
        base.ProcessData(stream);

        // Sql specific processing code
    }
}
public class DB2Processor : AbstractDatabaseProcessor
{
    public void ProcessData(Stream stream)
    {
        base.ProcessData(stream);

        // DB2 specific processing code
    }
}
于 2012-08-21T21:12:31.580 回答
0

有一些层次结构的类来实现一个接口来共享接口的一些实现是很好的。

即在您的情况下,您可以将共享的 ProcessData 代码移动到类似的东西ProcessorBase并从中DB2Processor派生SqlServerProcessor。您可以决定哪个级别实现接口(即,无论出于何种原因,您只需SqlServerProcessor实现IDatabaseProcessor接口 - 它仍然会从基类中获取 ProcessData 作为接口的实现)。

于 2012-08-21T21:14:49.357 回答