2012-01-01 44 views
4

我编写了一个名为Container的类,它处理层次结构(对类用户可见),并将它们内部转换为平面数组。所以对于Container的外部来说,它看起来像Containers的层次结构,每个都有父节点和子节点。D以非耦合的方式向类中添加功能

这个功能我想添加到某些类。例如Widget类,它必须具有由Container定义的相同功能。

我可以让Widget继承Container。容器现在被定义为一个具有this(),数据成员,成员函数,不变式和unittests的类。 Container包含一个Containers数组,因此设计中有一个错误:如果Foobar也继承Container并且我们将Foobar项添加到Widget的容器​​?这一定是被禁止的。它们确实共享相同的基类,但它们具有根本不同的目的......它们似乎共享一些功能。

将Container定义为接口是不可能的,因为它包含数据成员(并且不能解决问题)。既然我们也有这个()函数(或者如何解决这个问题?),所以既没有将容器定义为mixin也没有。 mixin中的函数的可见性属性也不起作用。另外,我不能通过Widget类的this参数,因为它需要是平面数组的第一个元素。

我想过放弃集装箱一个模板参数,告诉它这是什么样的容器:

abstract class Container(T) 
{ 
    ... 

    T[] elements; 
} 

class Widget: Container!Widget 
{ 
} 

这给出了一个错误:类容器.__ unittest2.Widget基类向前集装箱引用。

你将如何实现这一点?我还可以在Container中添加检查,以确保在添加子项时,它具有与父项相同的类型。但我该如何检查?

abstract class Container 
{ 
    void add(Container child) 
    { 
     // pseudo-code 
     assert (is(getFirstDerivedType(this) == getFirstDerivedType(child))); 

     ... 
    } 

    ... 

    Container[] elements; 
} 

编辑: 即使代码的第一块不发出错误信号,它仍然没有真正解决问题。由于只允许一个基类,因此我不能随意添加更多功能。其他人需要成为接口,这是根本不同的东西。接口确保派生类中具有某些功能,但它们本身不添加功能。

这应该是用(模板)mixin来解决的。但mixin不能将代码添加到构造函数中(只有在未定义时才进行替换),不能将代码添加到不变量(定义多次不变),不能指定成员函数可见性或使用其他类/结构特定关键字...

+0

我没有收到错误:class container .__ unittest2.Widget base ...'。谨慎分享更多? – Arlen 2012-01-02 01:25:57

+0

这很奇怪。我无法在更简单的环境中重现这一点。我想这是一个编译器错误,因为如果我将Widget定义放在unittest中,它会发出错误信号。否则它不会......但现在我得到了“断言失败:'文件'template.c'中的行4893上的'global.errors';异常程序终止”而不是... – 2012-01-02 10:24:59

+0

您可以使用多个子类型:add 'Container'作为'Widget'的子类型? (我不是专家:我刚刚在D书中几天前阅读过这方面的技术,看起来与你的问题有关。) – Xophmeister 2012-01-04 16:38:52

回答

2

我认为你是以这种错误的方式去做的。您试图从内部向各种类添加功能,而从外部添加功能会更有意义,即扩展它们。

Container应该包含Widget,而不是其他方式。然后,您可以使用alias this将非容器调用转发给包含的Widget。

事情是这样的:

import std.stdio; 

class Container(T) 
{ 
public: 
    this(T elem) { m_this = elem; add(this); } 
    void add(Container elem) { m_elems ~= elem; } 

    alias m_this this; 

private: 
    T m_this; 
    Container m_elems[]; 
} 

class Widget 
{ 
public: 
    this(int x) { this.m_x = x; } 
    int foo() { return m_x; } 
    int m_x; 
} 

alias Container!Widget CWidget; 

void main() 
{ 
    CWidget w1 = new CWidget(new Widget(1)); 
    CWidget w2 = new CWidget(new Widget(2)); 

    w1.add(w2); 
    writeln(w1.m_x); 
    writeln(w1.foo()); 
} 

注意如何你仍然可以通过调用w1.m_xw1.foo()使用w1Widget

+0

是不是一个mixin应该被用于代码拒绝和不继承? – 2012-01-04 22:40:20

+0

很多东西都可以用于代码重用。 Mixins是一个,继承是另一个。不过,我刚刚注意到你的编辑,我会考虑这个,因为我认为我的答案并不令人满意。 – 2012-01-04 22:55:55

+0

你有没有想过什么? – 2012-01-08 09:32:38

2

更新:我不确定你的例子与这个问题有什么关系。 D不允许在修改类之外向类添加功能。

继承不允许扩展基类的功能,只能修改它。

Mixin只允许您“混合”预定义的功能。同样,你不能用它修改现有的类。

你没有看过的一个选项是“别名”。这会创建一个构图关系,这又不允许修改现有的类或者包含它的构造关系。


import std.stdio; 

struct B 
{ 
    int p, q, r, s; 
} 

struct A 
{ 
    B b; 
    alias b this; 

    void foo() { 
     p = 6; 
    } 
} 

void main() 
{ 
    A a; 
    a.foo(); 

    writeln(a.p); 
} 

或者,也许我们需要的是做一些代码生成和使用字符串混入。

而最后一个选项是改变你的设计。不是创建一个修改不变量/构造函数和类似对象的混合,而是创建一个混合,该混合提供可以由类的实现者调用的函数。 “混入MyContainer模板,然后在你的不变中调用TestMe。”

评论上发言

there is one fault in the design: what if Foobar also inherits Container and we add Foobar items to the Widget's container? That must be forbidden.

然后不要把集装箱的集装箱阵列。

class Widget { 
    Widget[] elements; 
} 

注意,在你的例子中使用的模板,即使你这是工作(而且应该),集装箱!(小工具)是不是同一个班集装箱!(SomeClass的)。因此,有效地解决你的问题:

class ContainerWidget { 
    Widget[] elements; 
} 

class ContainerSomeClass { 
    SomeClass[] elements; 
} 

要正确地支持平板阵列,你将不得不创建一个标签的容器类似于标签联合将如何工作。

+0

我想要的是构图。但相反,合成数据成员的功能应该在类作用域中,而不是数据成员的作用域中。有几个丑陋的方法来做到这一点。对我来说,现在感觉mixin太简单了。它很好地可以添加正常的功能等,但在类/结构中它有限制使用(IMO)。 – 2012-01-05 21:39:18

+0

@Daevius,你可能想看看我为别名添加的例子。这是你想要的? – 2012-01-07 18:05:49

+0

对于在类作用域中表示单个合成数据成员,这确实是一种简洁的方式。但它的局限性在于你只能以这种方式添加一个类的功能。不允许使用多个'别名',所以您只能将一个类的功能添加到另一个类中。不是任意数字...... – 2012-01-07 18:20:22

1

如果我是你,我会让Container成为一个Widget。无论如何,这就是它在许多现有的GUI工具包中的工作方式,而且确实如此。

+0

是的,这是一个不错的主意。我只是这么想的:很多小部件都包含其他小部件(例如,滚动条由3个按钮组成)。但是也许在内部处理这个问题更有意义,而不是通过更一般的层次结构。尽管如此,(其他)通用案例仍然存在问题。 – 2012-01-05 21:34:53

+0

这样做也有很多好处。以FLTK为例,'fltk :: Group'也是'fltk :: Widget'。在Java SWING中,JComponent实际上持有JComponents列表... – DejanLekic 2012-01-06 10:58:07