【问题标题】:Workaround for inheriting from multiple classes?从多个类继承的解决方法?
【发布时间】:2013-05-20 03:58:12
【问题描述】:

所以我正在开发一个 GUI 库,我有 3 个类:UIElement,每个 UI 对象的基础,UIContainer,它实现了保存其他子元素的可能性和 UIRect,它实现了元素的位置和大小。现在我想创建一个同时使用 UIRect 和 UIContainer 的类。显然这是不可能的,但是这个问题有什么优雅的解决方案吗?

【问题讨论】:

标签: c# class inheritance


【解决方案1】:

C# 通常倾向于组合而不是继承和使用接口进行通信。

例子:

public interface IUIElement
{
}

public interface IUIContainer
{
    ICollection<IUIElement> Children;
}

public interface IUIRect
{
    IPosition Position { get; }
    ISize Size { get; }
}

public abstract class UIElement : IUIElement
{
}

public class Multiple : UIElement, IUIContainer, IUIRect
{
    private readonly ISize _size;
    private readonly IPosition _position;
    private readonly List<UIElement> _children = new List<UIElement>();

    public Multiple()
    {
    }

    public IPosition Position { get { return _position; } }
    public ISize Size { get { return _size; }; }

    public ICollection<IUIElement> Children { get { return _children; } }
}

【讨论】:

    【解决方案2】:

    “使用界面和组合”的通用答案似乎有点矫枉过正。可能不需要使用界面 - 您不太可能扮演有时由UIRect 扮演的角色,有时由不是UIRectUIElement 扮演的角色。只需将 UIRect 类型的属性 rect 添加到您的 UIContainer。

    更新

    (在 cmets 之后)我的答案的症结在于建议不要遵循制作接口并将调用委托给 UIRect 对象的私有实例的模式。

    UIRect,从名字上看,有各种处理屏幕上矩形空间几何的数据和逻辑。这意味着:

    • 您可能不会有多个实现。一个欧几里得就足够了;
    • 您可能会发现有许多描述 UIContainer 的矩形:可能是边界框、变换前后的大小、插图等。

    这只是我的判断,我没有很多数据。但据我所知,您需要一个简单的组合,而不是描述矩形属性的附加接口。

    【讨论】:

    • @NickFreeman:这就是我所说的“不使用接口”。如果您在理解我的回答时遇到问题,请随时提问。
    • @NickFreeman:这个问题专门针对“问题的优雅解决方案”,而不是“在考虑多重继承时我通常应该注意什么”。我的答案的症结在于建议不要遵循制作接口并将调用委托给 UIRect 对象的私有实例的模式。我将尝试进行一些小修改以适应这一点。
    • @NickFreeman:我试图澄清一下,谢谢你的帮助。
    • 我认为这里的主要思想是,“组合优于继承”的答案本身与接口无关,并且添加比需要更多的接口是过度工程,并且使代码的可维护性降低。两者都非常正确。
    • @NickFreeman 我不是在发表哲学声明或试图发现任何更深层次的编程真理。甚至分享一个好的做法。我只是想对一个特定的问题给出一个合理的建议。前段时间我对 gui 工具包和场景图很感兴趣,他们中的大多数人都有一些“矩形”的想法。 AFAIK,都使用了一个实现,而不是一个需要在矩形上下文中继承或实现任何东西的实现。就是这样。
    【解决方案3】:

    这是一种可能性:从其中一个类继承(例如,UIRect),并嵌入另一个(例如,UIContainer)。实现IUIContainerbu的接口,将所有调用转发到嵌入对象。

    class UIRect {
        ...
    }
    interface IUIContainer {
        IEnumerable<IUIElement> AllElements {get;}
        void AddElement(IUIElement toAdd);
    }
    class UIContainer : IUIContainer {
        public IEnumerable<IUIElement> AllElements {
            get {
                ...
            }
        }
        public void AddElement(IUIElement toAdd) {
            ...
        }
    }
    class Multiple : UIRect, IUIContainer {
        private readonly IUIContainer _cont = new UIContainer();
        ...
        public IEnumerable<IUIElement> AllElements {
            get {
                return _cont.AllElements;
            }
        }
        public void AddElement(IUIElement toAdd) {
            _cont.AddElement(toAdd);
        }
    }
    

    另一种可能是使用两个接口,通过扩展方法共享实现。

    【讨论】:

      【解决方案4】:

      您可以创建一个接受 UIElement、UIContainer、UIRect 实例作为属性的混合类,然后让您的子类实现混合并从那里获取它。

       class HybridDerived : Hybrid
       {
       }
      
       class Hybrid
       {
           public UIElement Element { get; set; }
           public UIContainer Container { get; set; }
           public UIRect Rect { get; set; }
       }
      
       class UIElement
       {
       }
      
       class UIContainer
       {
       }
      
       class UIRect 
       {
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 2019-12-05
        • 1970-01-01
        • 2021-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多