【问题标题】:Design Problem - Is Inheritance the right way to simplify this code?设计问题 - 继承是简化此代码的正确方法吗?
【发布时间】:2010-08-23 20:37:54
【问题描述】:

我想解决一个设计问题。 我有一个接口,我们称之为IProtocol,它由两个单独的类实现。我们在这里查看超过 600 行代码。他们所做的绝大多数事情都是一样的,除了对于某些特定领域,比如DiffStuff();

目前的结构是这样的:

public class Protocol1 : IProtocol
{
  MyInterfaceMethod1()
  {
     Same1();
     DiffStuff();
     Same2();
  }
}

public class Protocol2 : IProtocol
{
  MyInterfaceMethod1()
  {
     Same1();
     Same2();
  }
}

如果我将这两个协议分开,我担心会出现复制粘贴错误和典型的代码重复问题。我们谈论的是完整的 600 行代码,而不是一些简单的方法。

我正在考虑将 Protocol1 的实现更改为从 protocol2 继承,就像这样(Protocol2 基本上保持不变,除非我必须将 Same1()Same2() 包装到私有方法中。)

public class Protocol1 : Protocol2
{
  void Same1()
  {
     base.Same1();
  }

  void Same2()
  {
     base.Same2();
  }

  MyInterfaceMethod1()
  {
     Same1();
     DiffStuff();
     Same2();
  }
}  

这是解决这个问题的正确方法吗?

编辑: 很多人帮助我解决了这个问题,感谢您的清晰理解。在我的例子中,这两个对象不是同一类型,尽管它们的大部分实现是共享的,所以我使用Bobby's suggestion 使用抽象基类,创建小方法来封装类之间的更改。额外感谢:

  • jloubert
  • 汉斯·帕桑特
  • 杰夫·斯特恩

【问题讨论】:

  • 为什么不使用一个抽象类来定义共享方法和未实现的必要方法的抽象定义?
  • 看看 Robet C. Martin 的“清洁代码”
  • 你应该只让Protocol1Protocol2继承,如果这真的有意义的话。你需要问,“每个协议 1 是否也是协议 2 的一个实例,就像每只猫都是哺乳动物一样?”如果是,那么就按照你的想法去做。如果没有,我会使用抽象基类,如 Bobby 的回答所示。

标签: c# design-patterns inheritance


【解决方案1】:
    /// <summary>
    /// IProtocol interface
    /// </summary>
    public interface IProtocol
    {
        void MyInterfaceMethod1();
        void Same1();
        void Same2();
    }

那么……

public abstract class ProtocolBase : IProtocol
{
    #region IProtocol Members

    public void MyInterfaceMethod1()
    {
        // Implementation elided...
    }

    public void Same1()
    {
        // Implementation elided...
    }

    public void Same2()
    {
        // Implementation elided...
    }

    public abstract void DiffStuff();

    #endregion
}

终于……

public sealed class Protocol1 : ProtocolBase
{
    public override void DiffStuff()
    {
        // Implementation elided...
    }
}

public sealed class Protocol2 : ProtocolBase
{
    public override void DiffStuff()
    {
        // Implementation elided...
    }
}

【讨论】:

    【解决方案2】:

    不完全是。添加 Same1 和 Same2 方法没有意义,您从 ProtocolBase 继承它们。并且 DiffStuff() 应该是一个虚拟方法,以便您可以覆盖它并赋予它不同的行为。

    【讨论】:

      【解决方案3】:

      您已经非常接近于描述 template method pattern 了,它长期以来一直是解决像您这样的问题的有效解决方案。

      但是,您应该考虑使用组合而不是继承。 The two approaches offer different advantages and disadvantages,但组合通常(通常?)更好*

       public class Protocol {
      
            private ISpecialHandler specialHandler;
      
            public Protocol() {}
      
            public Protocol(ISpecialHandler specialHandler) {
                this.specialHandler = specialHandler;
            }
      
            void Same1() {}
            void Same2() {}
      
            public void DoStuff() {
                this.Same1();
                if (this.specialHandler != null) {
                    this.specialHandler.DoStuff();
                }
                this.Same2();
            }
       }
      

      然后调用者可以传入对象实例 (strategies),这些实例提供专门的算法来处理手头的任何情况:

       Protocol protocol1 = new Protocol(new DiffStuffHandler());
       protocol1.DoStuff();
      

      *请参阅Patterns I Hate #2: Template Method,详细了解为什么在您的情况下组合通常比继承更好。

      【讨论】:

      • 这可能比我建议的倒置组合更适合协议。
      • 我会提出的小建议是将 ISpecialHandler 设为属性,而不是将其放入构造函数中。这让 API 的调用者明确知道 ISpecialHandler 是可选的。
      【解决方案4】:

      您可能需要考虑这种模式是否有效:

      public class ProtocolHelper
      {
          public void Same1() {}
          public void Same2() {}
      }
      
      public class Protocol1 : IProtocol
      {
          private readonly ProtocolHelper _helper = new ProtocolHelper();
      
          void MyInterfaceMethod1()
          {
              _helper.Same1();
              DiffStuff();
              _helper.Same2();
          }
      }
      

      您可以通过查看是否可以为“ProtocolHelper”类起一个好名字来判断这是否有意义。如果一个名字自然地来自你的逻辑,那么这是一个分解类的好方法。您可能需要将一些依赖项(例如私有字段)作为参数传递给方法才能使其工作。

      【讨论】:

        【解决方案5】:

        我更好的设计(在我看来)

        public abstract class Protocol_common : IProtocol 
        { 
          MyInterfaceMethod1() 
          { 
             Same1(); 
             DiffStuff(); 
             Same2(); 
          } 
        
          abstract void DiffStuff();
        
        }
        
        public class Protocol1 : Protocol_common
        { 
          DiffStuff() 
          { 
              /// stuff here.
          } 
        }
        
        public class Protocol2 : Protocol_common
        { 
          DiffStuff() 
          { 
              /// nothing here.
          } 
        }
        

        (这实际上是比正确的 C# 更多的伪代码,但我达到了高点)

        【讨论】:

        • Protocol1 和继承自 Protocol_common 和 Protocol_common 不应该是抽象的吗?
        • Protocol1 和 Protocol2 的 MyInterfaceMethod1 的实现略有不同(Protocol2 实际上并不调用 DiffStuff),因此这需要抽象化,并且也需要每个派生类单独实现。或者也许使用虚拟方法并将 DiffStuff 实现之一视为默认行为。
        【解决方案6】:

        我同意 MathEpic。我会使用Template method 模式。

        【讨论】:

        • (-1) 请将您的 cmets 发布为 cmets
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-16
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多