【问题标题】:How to remove code duplication when implementing interfaces that share common methods实现共享通用方法的接口时如何去除代码重复
【发布时间】:2015-08-31 23:08:08
【问题描述】:

编辑 我已经改写了这个问题,以便更好地反映我想要做的事情

我正在尝试创建一套全部继承自 1 个“超类”或“基类”的类。

但是我正在寻找一种方法来解决必须在每个类中实现每个方法的代码,因为它似乎有很多重复。

这是超级类:

public abstract class WebObject
{
    string Id { get; set; }
    string Name { get; set; }

    public void Click() { Console.WriteLine("Clicking object"); }
    public string GetAttribute() { return "An attribute"; }
    public void SetAttribute(string attribute, string value)
    {
        //code to set attribute
    }
}

我还创建了几个接口:

interface IReadable
{
    string GetText();
}

interface IWriteable
{
    void SetText(string value);
}

这是一个派生类示例:

public class TextBox: WebObject, IWriteable, IReadable
{


}

当然,上面的类包含一个错误。它没有实现 IWriteable 或 IReadable。

所以我可以这样做:

public class TextBox: WebObject, IWriteable, IReadable
{
    public void SetText(string value)
    {
        //Implemented code
    }

    public string GetText()
    {
        //Implemented code
        return "some value";
    }
}

编译得很好... 但是这里的问题是 SetText 和 GetText 包含很多代码。我不想每次我想实现该方法时都复制它。我宁愿只编写一次代码,并在我需要使用该方法的任何时候调用它。

我知道我们不能在 C# 和 Java 中进行多重继承。所以我最初的想法是简单地创建一套静态类,其中 SetText 和 GetText 的代码如下所示:

public static class Setter
{
    public static void SetText(string value)
    {
        //Code to set text
    }
}

public static class Getter
{
    public static string GetText()
    {
        //Code to get text
        return "";
    }
}

然后将我的 TextBox 类更改为以下内容:

public class TextBox: WebObject, IWriteable, IReadable
{
    public void SetText(string value)
    {
        Setter.SetText(value);
    }

    public string GetText()
    {
        return Getter.GetText();
    }
}

我不禁觉得这是一个相当冗长的解决方案。它实现了我想要的,因为 TextBox 具有 vanilla 方法以及它自己实现的 2 个方法。

但我的问题是,我可以使用更简洁的设计来实现相同的目标吗?

脚注

每个对象实际上都实现了几种常用方法。拿TextBox、ComboBox和SelectBox来说应该都可以SetText,但是只有CombBox和SelectBox应该可以用Select。

【问题讨论】:

  • 如果您访问任何类型的状态,这将不起作用。
  • SetTextGetText 是做什么的?如果你有它们的静态实现,为什么你需要做同样事情的实例方法?
  • 也许您可以将部分代码移动到 C++ 项目中。在那里你可以有多重继承并公开接口......
  • 请解释为什么,如果要在所有子类之间共享此代码,您不能将其全部放在基类中(包括接口声明)。就目前而言,您甚至问什么都不是很清楚,因为对您的问题最明显的解释也伴随着一个非常明显的解决方案。
  • “有些元素是不同的”——这并不能解释什么。你的问题具体说“theres [sic] 很多重复”。如果有不同的元素,那么显然那些不是我们要讨论的,因为“不同”是“重复”的反面。多态有利于在基本接口相同的情况下处理“不同”;基类有利于处理“重复”。没有理由不能混合使用这两种技术。

标签: c# oop inheritance multiple-inheritance


【解决方案1】:

完成您所要求的最干净的方法是在您的基类中实现 protected 辅助方法,将“大量重复”的问题分解为可以在您的具体方法实现中组合的更小的部分,就像这样:

public abstract class WebObject {
  protected void SetTextImpl() { /* Implementation */ } 
  protected void GetTextImpl() { /* Implementation */ } 
}

然后在您的派生类中,仅实现适用的接口和适当的方法:

public class TextBox: WebObject, IWriteable, IReadable {
  public void SetText() { SetTextImpl(); }
  public void GetText() { GetTextImpl(); }
}

public class Span: WebObject, IReadable {
  public void GetText() { GetTextImpl(); }
}

如果你知道所有的子类都是 IReadable,你可以进一步简化:

public abstract class WebObject : IReadable {
  protected void SetTextImpl() { /* Implementation */ } 
  protected void GetTextImpl() { /* Implementation */ } 

  // Implement IReadable -- this could be combined with GetTextImpl() but
  // is implemented separately for consistency.
  public void GetText() { GetTextImpl(); }
}

public class TextBox: WebObject, IWriteable {
  public void SetText() { SetTextImpl(); }
}

public class Span: WebObject, IReadable {
}

【讨论】:

  • 我最喜欢这个解决方案。看起来干净多了。
【解决方案2】:

如果这两个方法的代码总是相同或大部分相同,您可以创建另一个抽象类(例如:WebObjectReadWrite),它继承自 WebObject 并实现接口。

public abstract class WebObjectReadWrite : WebObject, IReadable, IWritable
{
    // Could be made virtual if some subclasses need to overwrite default implementation.
    public void Read() 
    {
        // Implementation
    }

    // Could be made virtual if some subclasses need to overwrite default implementation.
    public void Write() 
    {
        // Implementation
    }
}

public class TextBox : WebObjectReadWrite
{
}

但是,这可能会导致多重继承问题或没有意义的继承关系。另一种选择是使用strategy pattern(方式)将读/写操作委托给其他可以重用的类。

public class TextBox : WebObject, IReadable, IWriteable
{
    private IReadable _readable = new TextReader();
    private IWriteable _writeable = new TextWriter();

    public void Read() 
    {
        _readable.Read();
    }

    public void Write() 
    {
        _writable.Write();
    }
}

public class Span : WebObject, IReadable
{
    // Reused class.
    private IReadable _readable = new TextReader();        

    public void Read() 
    {
        _readable.Read();
    }
}

public class TextReader : IReadable
{
    public void Read()
    {
        // Reusable implementation
    } 
}

这不是策略模式,因为您不允许调用者选择 IReadable 和 IWriteable 的实现。但是,它确实允许您重用 IReadable 和 IWriteable 类。

【讨论】:

  • 提供的第二个选项与我自己的建议非常接近。创建一个执行 settext/gettext 的新类/接口,并将它的一个实例注入到每个需要该功能的类中。然后直接调用该实例。它可能仍然感觉像“浪费”,因为一种方法可能会直接调用另一种方法,但您的应用程序将更具可扩展性和解耦性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 2018-03-21
相关资源
最近更新 更多