1

我有一个名为 WidgetBase 的抽象基类,如下所示:

public abstract class WidgetBase : Control, INamingContainer
{
     // This class declares abstract and virtual properties and methods but doesn't override anything
}

我有另一个名为 ScrollerBase 的抽象基类,它看起来像这样:

public abstract class ScrollerBase : Control
{
    // This class has one abstract property and overrides OnInit and OnLoad
}

我需要创建一个继承自这两个类的类。这两个类都不能转换为接口。我能想到的最简单的解决方案是创建一个额外的 WidgetBase 类,如下所示:

public abstract class ScrollingWidgetBase : ScrollerBase, INamingContainer
{
     // Lots of duplicated code from WidgetBase
}

如果我能做这样的事情会很好:

public abstract class ScrollingWidgetBase : WidgetBase<ScrollerBase>    { /* Empty */ }
public abstract class WidgetBase : WidgetBase<Control>    { /* Empty */ }
public abstract class WidgetBase<T> : T, INamingContainer    { /* Code goes here */ }

然而这是不可能的。我能想到的唯一半优雅的解决方案是这样的:

public interface IScroller { /* Overridable members go here */ }
public class WidgetBase : Control, INamingContainer
{
    protected override void OnInit(EventArgs e)
    {
        if (this is IScroller)
        {
            // Do scroller logic here
        }
    }
    protected override void OnLoad(EventArgs e)
    {
        if (this is IScroller)
        {
            // Do scroller logic here
        }
    }
}

public class Test : WidgetBase, IScroller {  }

但是,如果创建更多类,这将变得非常混乱。肯定有某种设计模式可以解决这个问题吗?任何帮助将不胜感激。

谢谢,

4

4 回答 4

4

正如海德所说,您确实需要将您的行为与您的课程分开。你说你不想使用接口;好吧,我想不出办法。您可能不得不硬着头皮或做一些代码重复。

至少,您可以将行为逻辑移动到一个单独的控制类,并简单地向它提供一些公开所需成员的接口。考虑一下可滚动 UI 控件所需的几个属性(请注意,这只是一些示例,我怀疑这些类型是否存在,或者这些是所需的实际最小集):

public interface ISCrollable
{
    Control Target { get; }
    ScrollBar Bar { get; }
    double FullLength { get; }
    double VisibleLength { get; }
}

然后,您可以将滚动行为放在附加该行为或适用于IScrollable

public class Scroller
{
    public Scroller(ISCrollable scrollable)
    {
        //attach behaviour
        scrollable.Bar.OnClick += DoScroll;
        //and other stuff
    }
}

最后,您ScrollableWidget将成为您的Control并附加滚动行为而不是继承它:

public class ScrollableWidget : Control, INamingContainer, ISCrollable, IWidget
{
    private Scroller ScrollBehaviour;
    private Widgeter = WidgetBehaviour;

    public Control Target { get; private set; }
    public ScrollBar Bar { get; private set; }
    public double FullLength { get; private set; }
    public double VisibleLength { get; private set; }

    public ScrollableWidget()
    {
        this.Target = this;
        this.Bar = CreateScrollBarControl();
        this.FullLength = 9001;
        this.VisibleLength = this.ActualHeight;

        this.ScrollBehaviour = new Scroller(this);
        this.WidgetBehaviour = new Widgeter(this);
    }
}

所以,是的,你会想要使用接口。我不明白你的要求不要。还有其他模式,例如Decorator,可能比我刚刚发布的混乱更适合。

于 2012-10-11T13:14:15.270 回答
0

旧原则适用:组合优于继承。

不要继承,除非该类实际上需要充当基类,而是将可能的基类作为可能的派生类的成员。

使用接口而不是类作为方法参数类型等,因此您实际上可以使用组合而不是继承。

使用组合时,学习使用 IDE 重构功能为成员对象插入委托方法。

于 2012-10-11T13:03:40.263 回答
0

如果我理解正确,那么滚动条就是小部件的装饰器。

1)创建接口IWidgetBase。

2) 实现装饰器

class ScrollDecorator:IWidgetBase{
   IWidgetBase _decoratedWidget;
   public ScrollDecorator(IWidgetBase widget){
       _decoratedWidget=widget;
   }
   //implementation  
}
于 2012-10-11T13:14:13.423 回答
0

我认为由于某种原因ScrollerBase不能修改为继承自。WidgetBase那真的是最好的解决方案。

如果做不到这一点,您可以通过将扩展方法接口相结合来实现多重继承。

public interface IWidgetBase
{
  bool SomeBool { get; }
  void DoWidgetyStuff();
}
public static class IWidgetBaseExtensions
{
  public static void WidgetStuff(this IWidgetBase base)
  {
    if (base.SomeBool) 
      base.DoWidgetyStuff();
  }
}


public class ScrollerBase : Control, IWidgetBase
{
  public bool SomeBool { get; protected set; }
  public void DoWidgetyStuff() {
    // Do stuff
  }
}

// Main code:
IWidgetBase myScroller = new ScrollerBase();
myScroller.WidgetStuff();

根据您的代码所做的事情,这可能是也可能不是比其他人建议的组合更好的答案。

于 2012-10-11T13:38:46.193 回答