【问题标题】:Design pattern to use instead of multiple inheritance使用设计模式代替多重继承
【发布时间】:2010-03-25 13:30:22
【问题描述】:

来自 C++ 背景,我习惯于多重继承。我喜欢霰弹枪直接对准我的脚的感觉。现在,我更多地在 C# 和 Java 中工作,你只能继承一个基类,但可以实现任意数量的接口(我理解的术语对吗?)。

例如,让我们考虑两个实现公共接口但不同(但需要)基类的类:

public class TypeA : CustomButtonUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

public class TypeB : CustomTextUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

两个类都是UserControls,所以我不能替换基类。两者都需要实现DoMagic 函数。我现在的问题是该函数的两个实现是相同的。而且我讨厌复制粘贴代码。

(可能的)解决方案:

  1. 我自然希望TypeATypeB 共享一个公共基类,在那里我可以只编写一次相同的函数定义。但是,由于只有一个基类的限制,我无法在层次结构中找到合适的位置。
  2. 也可以尝试实现一种composite pattern。将DoMagic 函数放在一个单独的辅助类中,但这里的函数需要(并修改)相当多的内部变量/字段。将它们全部作为(参考)参数发送看起来很糟糕。
  3. 我的直觉告诉我adapter pattern 可以在这里占有一席之地,必要时可以在两者之间转换的某个类。但它也让人感觉很老套。

我将其标记为与语言无关,因为它适用于所有使用这种单基类多接口方法的语言。

另外,如果我似乎误解了我命名的任何模式,请指出。

在 C++ 中,我只需创建一个带有私有字段的类,即函数实现并将其放入继承列表中。 C#/Java 之类的正确方法是什么?

【问题讨论】:

    标签: language-agnostic design-patterns oop


    【解决方案1】:

    您可以使用策略模式或类似的东西来使用 has a(组合)而不是 is a(继承):

    public class TypeA : CustomButtonUserControl, IMagician {
        IMagician magicObj = new Magical();
        public void DoMagic() {
            magicObj.DoMagic();
        }
    }
    
    public class TypeB : CustomButtonUserControl, IMagician {
        IMagician magicObj = new Magical();
        public void DoMagic() {
            magicObj.DoMagic();
        }
    }
    
    public class Magical : IMagician {
        public void DoMagic() {
            // shared magic
        }
    }
    

    还有其他方法可以实例化您的私有 IMagician 成员(例如通过构造函数将它们作为参数传递),但以上内容应该可以帮助您入门。

    【讨论】:

      【解决方案2】:
      • 在 .Net 中,您可以将扩展方法应用于接口。它在可能的情况下非常简洁,并且适用于您,因为这是将通用实现应用到接口的一种罕见方式。当然考虑一下,但它可能不适合你,因为你说DoMagic 与很多私人成员一起工作。你能打包这些私有变量internal吗?这样扩展方法就可以访问它们。
      • 具有其他类的通用功能。如果有一个放置这个通用功能的合理位置,请将您的对象传递给这个其他类方法(也许这是 UI 功能,并且您已经有一个 UI 助手......)。同样,您可以使用内部/公共属性公开私有数据吗? (当然,安全/封装是所有这一切中的一个问题。我不知道您的课程是仅供内部使用还是会公开。)
      • 否则,将单独的功能类(或特定函数指针)传递给接口定义的方法。您必须有一些重复的代码才能将私有变量传递给这个外部函数引用,但至少不会太多,而且您的实现会在一个地方。
      • 我们可能把它弄得太复杂了。今晚睡觉时,它不会让你觉得完全是面向对象的,但是你能在你的库中的某个地方有一个所有 IMagician 实现者都调用的静态例程吗?
      • 最后,Adapter 可能确实是您正在寻找的东西。不太可能但仍然值得考虑的是Decorator pattern

      如果没有什么特别好的,选择感觉最好的,使用它几次,然后明天重新安排。 :)

      【讨论】:

        【解决方案3】:

        用组合替换继承。

        将您的“通用”函数移动到单独的类,创建该类的实例,并将其插入 TypeA 对象和 TypeB 对象。

        【讨论】:

          【解决方案4】:

          在这种情况下,你的直觉是正确的。适配器模式正是您要寻找的。​​p>

          DoFactory 有很好的 .NET 示例(应该也非常接近于它们的 Java 对应示例):

          Adapter Design Pattern in C# and VB.NET

          【讨论】:

            【解决方案5】:

            复合模式适用于复杂的对象,这意味着重点是由其他对象组成的一个对象。 strategy-pattern 可以看作是一个特例,但策略不一定是对象。我认为这更适用于您的情况。话又说回来,这在很大程度上取决于 DoMagic() 所做的事情的性质。

            【讨论】:

              【解决方案6】:
              public interface  IMagician{ /* declare here all the getter/setter methods that you need; they will be implemented both in TypeA and TypeB, right? */ }
              
              public static class MyExtensions {
                public static void doMagic(this IMagician obj)
                { 
                         // do your magic here
                }
              }   
              

              现在,问题是如果您真的需要使用私有属性/方法(而不是“内部”的),这种方法将不起作用。好吧,实际上,如果您可以通过反射读取这些属性,您也许可以施展魔法,但即使它有效,它也是一个相当丑陋的解决方案:)

              [请注意,“doMagic”将自动显示为 TypeA 和 TypeB 的一部分,这仅仅是因为您实现了 IMagician - 不需要任何实现]

              【讨论】:

                【解决方案7】:

                您可以使用组合将魔术师作为 typeA 和 typeB 的属性

                class Magician : IMagician
                {
                    public void DoMagic()
                    {}
                }
                
                Class TypeA : CustomButtonUserControl
                {
                   //property
                   Magician magicianInTypeA
                }
                
                Class TypeB : CustomTextUserControl
                {
                    //property
                    Magician magicianInTypeB
                }
                

                【讨论】:

                  【解决方案8】:
                  abstract class Magical: CustomButtonUserControl
                  {
                      public void DoMagic()
                      {
                          // ...
                      }
                  }
                  
                  public class TypeA : Magical
                  {
                  
                  }
                  
                  public class TypeB : Magical
                  {
                  
                  }
                  

                  【讨论】:

                  • 这正是我的本能。可悲的是,CustomButtonUserControlCustomTextUserControl 是不同的方式来完成这项工作,因此我的问题。或者我在这里错过了什么?仅发布代码时,很难判断您的答案。
                  • OP具体说:我不能替换基类。
                  猜你喜欢
                  • 1970-01-01
                  • 2017-02-11
                  • 2019-07-20
                  • 2011-01-19
                  • 2013-08-16
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-09
                  • 2014-04-15
                  相关资源
                  最近更新 更多