37

在 WPF 应用程序中,我的类层次结构遇到了一些问题。这是您将两个继承树合并在一起的问题之一,如果没有多重继承,您将无法找到任何合乎逻辑的方法来使继承顺利进行。我想知道是否有人对使这种系统正常工作有什么好主意,而不会使其无法遵循或调试。

我是一个低级工程师,所以我的第一个想法总是,“哦!我会用原生 C++ 编写其中的一些类,并在外部引用它们!这样我就可以尽情享受我的老派 OO 乐趣了!” 唉,当您需要从托管控件继承时,这无济于事......

请允许我展示我当前投影的类图的片段:

 ____________________________________      _____________________________________
| CustomizableObject                 |    | System.Windows.Controls.UserControl |
|____________________________________|    |_____________________________________|
|   string XAMLHeader()              |                        ▲
|   string XAMLFooter()              |◄--┐                    |
|   CustomizableObject LoadObject()  |   \                    |
|   <Possible other implementations> |    \                   |
|____________________________________|     \                  |
         ▲                      ▲           \                 |
         |                      |            \                |
         |                      |             \               |
 _________________    ______________________   \    _____________________
| SpriteAnimation |  | SpriteAnimationFrame |  └---| CustomizableControl |
|_________________|  |______________________|      |_____________________|
                                                      ▲             ▲
                                                      |             |
                                                      |             |
                                                  ________    _____________
                                                 | Sprite |  | SpriteFrame |
                                                 |________|  |_____________|

问题很清楚:CustomizableObject 和 CustomizableControl 对象树的分离 --- 以及将 UserControl 插入到一个树中,但不能同时插入两个树。

将 CustomizableObject 的实现移到其派生类中没有实际意义,因为实现不会因类而异。此外,多次实施它会非常令人困惑。所以我真的不想让 CustomizableObject 成为一个接口。接口解决方案对我来说没有任何意义。(说实话,接口对我来说 从来没有真正意义过......)

所以我再说一遍,有人有什么好主意吗?这是一个真正的泡菜。我想了解更多关于使接口与我的对象树一起工作的信息,而不是反对它。我正在使用 WPF 和 C# 制作这个简单的 sprite 引擎作为一个可靠的练习,比什么都重要。这在 C++ 中很容易解决 - 但我需要弄清楚如何在托管环境中解决这些问题,而不是在遇到困难时举起双手回到 Win32。

4

6 回答 6

29

您在这里有两个选择;使用接口,或使用组合。老实说,接口很强大,看完这一行

接口解决方案对我来说没有任何意义。(说实话,接口对我来说从来没有真正意义过......)

我认为您应该学习如何正确使用它们。也就是说,如果只是多个类需要一些逻辑,但这些类从同一个基类继承没有意义,只需创建一个类来封装该逻辑并将该类的成员变量添加到您的类中这给你带来了问题。这样,所有类都包含逻辑,但可以在它们的继承层次结构中分开。如果类应该实现一个公共接口,那么使用接口。

于 2009-08-17T18:52:32.377 回答
18

一种方法是使用带有接口的扩展方法来提供“派生类”实现,很像 System.Linq.Queryable:

interface ICustomizableObject
{
    string SomeProperty { get; }
}

public static class CustomizableObject
{
    public static string GetXamlHeader(this ICustomizableObject obj)
    {
        return DoSomethingWith(obj.SomeProperty);
    }

    // etc
}

public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject
{
    public string SomeProperty { get { return "Whatever"; } }
}

用法:只要你有一个 using 指令用于定义扩展方法的命名空间(或在同一个命名空间中):

var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
于 2009-08-17T19:00:32.187 回答
7

我正在看这个,CustomizableObject只是尖叫着被做成一个接口(因为每个具体类型都可以转换为对象,所以名称的那部分是多余的)。您遇到的问题是您不确定如何保留一些将共享或仅因实现而略有不同的基本逻辑,并且您希望将此逻辑存储在树本身中,以便它可以多态地工作(是那个词?)。

您可以通过委托来实现这一点。我不确定究竟是哪些成员给你带来了麻烦,但也许更像这样:

 ____________________________________      _____________________________________
| ICustomizable                      |    | System.Windows.Controls.UserControl |
|                                    |    |_____________________________________|
|   Func<string> XAMLHeader;         |                        ▲
|   Func<string> XAMLFooter          |◄--┐                    |
|   ICustomizabl LoadObject() |   \                    |
|   <Possible other implementations> |    \                   |
|____________________________________|     \                  |
         ▲                      ▲           \                 |
         |                      |            \                |
         |                      |             \               |
 _________________    ______________________   \    _____________________
| SpriteAnimation |  | SpriteAnimationFrame |  └---| CustomizableControl |
|_________________|  |______________________|      |_____________________|
                                                      ▲             ▲
                                                      |             |
                                                      |             |
                                                  ________    _____________
                                                 | Sprite |  | SpriteFrame |
                                                 |________|  |_____________|

此外,您可能拥有一些真正属于您的 CustomizableObject 类型的真正静态逻辑。但这可能是错误的:您构建类型的目的是在特定情况下使用该类型。例如,从上下文看来,您将创建这些控件和动画并在 Windows 窗体上使用它们。要做的事情是拥有自己的表单,该表单继承自基本 System.Windows.Form,并且这个新表单类型应该了解 ICustomizableObject 以及如何使用它。那就是您的静态逻辑将去的地方。

这似乎有点尴尬,但是当您决定更改演示引擎时,它被证明是准确的。如果将此代码移植到 WPF 或 Silverlight 会发生什么?他们可能需要使用与 Windows 窗体稍有不同的实现代码,并且您仍然可能需要更改您的 CustomizableControl 实现。但是您的静态逻辑现在完全正确。

最后,您使用的 LoadObject() 方法在我看来也很明显,因为它在错误的地方也是如此。您是说您希望每个 Customizable 类型都提供一个您可以调用的方法,该方法知道如何加载/构造自身。但这确实是不同的东西。您可能需要另一个名为的接口IConstructable<T>并让您的 ICustomizable 类型实现该 ( IConstructable<ICustomizable>)。

于 2009-08-17T19:03:52.727 回答
2

看起来您将不得不求助于接口和对象组合。

于 2009-08-17T18:52:18.147 回答
2

在这种情况下,我使用 mixins。mixins 是许多语言中用于在运行时向类添加功能的强大概念。mixin 在许多语言中都是众所周知的。re-mix 框架是将 mixin 技术引入 .NET 的框架。

这个想法很简单:用代表 mixin 的类属性装饰你的类。在运行时,此功能将添加到您的类中。所以你可以模拟多重继承。

您的问题的解决方案可能是将 CustomizableObject 设置为 mixin。为此,您将需要重新混合框架。

[Uses(CustomizableObjectMixin)] CustomizableControl : UserControl

请查看 remix.codeplex.com 了解更多详情。

于 2011-03-24T08:42:11.530 回答
2

我认为是一种解决方案。

public interface IClassA
{
    void Foo1();
    void Foo2();
}

public interface IClassB
{
    void Foo3();
    void Foo4();
}

public class ClassA :IClassA
{
    #region IClassA Members

    public void Foo1()
    {
    }

    public void Foo2()
    {
    }

    #endregion
}

public class ClassB :IClassB
{
    #region IClassB Members

    public void Foo3()
    {
    }

    public void Foo4()
    {
    }

    #endregion
}

public class MultipleInheritance :IClassA, IClassB
{
    private IClassA _classA;
    private IClassB _classB;

    public MultipleInheritance(IClassA classA, IClassB classB)
    {
        _classA = classA;
        _classB = classB;
    }

    public void Foo1()
    {
        _classA.Foo1();
    }

    public void Foo2()
    {
        _classA.Foo2();
        AddedBehavior1();
    }

    public void Foo3()
    {
        _classB.Foo3();
        AddedBehavior2();
    }

    public void Foo4()
    {
        _classB.Foo4();
    }

    private void AddedBehavior1()
    {

    }

    private void AddedBehavior2()
    {

    }
}

类 MultipleInheritance 向两个不同的对象添加新行为,而不影响该对象的行为。MultipleInheritance 具有与交付对象相同的行为(继承这两种行为)。

于 2013-04-03T21:39:28.610 回答