如何避免重复的接口代码?

时间:2012-08-21 21:09:13

标签: c# oop refactoring

由于接口不能包含实现,在我看来,这会导致从接口继承的类中的代码重复。在下面的示例中,假设,假设从流中设置读取的前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的抽象基类并添加非抽象成员是一种解决方案。但是,如果我真的真的想要使用界面呢?

7 个答案:

答案 0 :(得分: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标签来到这里的。

答案 1 :(得分:14)

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

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

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);
    }
}

帮助器类不需要是静态的:如果你的共享方法需要状态,你可以让你的帮助器成为常规类,并在你想要共享代码的接口的每个实现中放置它的实例。 / p>

答案 2 :(得分: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);
    }
}

答案 3 :(得分:0)

如您所示,abstract类提供了解决方案。如果你“非常,真的想要”,你可以使用界面。没有什么可以排除它。您的abstract课程应该实施IDatabaseProcessor

答案 4 :(得分:0)

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

答案 5 :(得分: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
    }
}

答案 6 :(得分:0)

可以使用一些类来实现一个接口来共享接口的一些实现。

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