我编写了一个名为Container的类来处理层次结构(对类用户可见)并将它们内部转换为平面数组。因此,对于Container的外部,它看起来像Container的层次结构,每个容器都有父节点和子节点。
我希望将此功能添加到某些类中。例如Widget类,它必须具有Container定义的相同功能。
我可以让Widget从Container继承。现在,Container被定义为具有this(),数据成员,成员函数,不变量和单元测试的类。容器包含一个容器数组,因此设计中有一个错误:如果Foobar还继承了Container并且我们将Foobar项添加到Widget的容器中怎么办?这一定是被禁止的。它们共享相同的基类,但它们根本不同,具有不同的目的......它们似乎只是共享一些功能。
将Container定义为接口是不可能的,因为它包含数据成员(并且不能解决问题)。将Container定义为mixin,因为我们也有this()功能(或者这将如何解决?)。 mixin中函数的可见性属性也不起作用。另外,我不能传递Widget类的this
参数,因为它需要是平面数组的第一个元素。
我想给Container一个模板参数,告诉它它是什么容器:
abstract class Container(T)
{
...
T[] elements;
}
class Widget: Container!Widget
{
}
这给出了一个错误:类容器.__ unittest2.Widget基类由Container正向引用。
你会如何实现这个?我还可以在Container中添加检查,以确保在添加子项时,它与父项具有相同的类型。但是我如何检查呢?
abstract class Container
{
void add(Container child)
{
// pseudo-code
assert (is(getFirstDerivedType(this) == getFirstDerivedType(child)));
...
}
...
Container[] elements;
}
修改 即使第一段代码没有发出错误信号,它仍然无法真正解决问题。我不能随意添加更多功能,因为只允许一个基类。其他人需要是接口,这是根本不同的东西。接口确保派生类中存在某些功能,它们本身不会添加功能。
这应该用(模板)mixins来解决。但是mixins不能向构造函数添加代码(只有在未定义时才替换),不能将代码添加到不变量(多次定义不变),不能指定成员函数可见性或使用其他类/结构特定关键字......
答案 0 :(得分:2)
更新:我不确定您的示例与此问题有什么关系。 D不允许您在修改类之外向类添加功能。
继承不允许扩展基类的功能,只修改它。
Mixins只允许您“混合”预定义的功能。同样,您无法使用它修改现有类。
你没有看过的一个选择是“别名”。这会创建一个合成关系,同样不允许修改现有类甚至包含它的那个。
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);
}
或许我们需要的是做一些代码生成并使用字符串mixins。
最后一个选择是改变你的设计。不是创建修改不变量/构造函数等的mixin,而是创建一个mixin,它提供可由类的实现者调用的函数。 “混合MyContainer模板,然后在不变量中调用TestMe。”
对陈述的评论:
设计中存在一个错误:如果Foobar还继承了Container并且我们将Foobar项添加到Widget的容器中怎么办?这一定是被禁止的。
然后不要在Container中放置一个Container数组。
class Widget {
Widget[] elements;
}
请注意,在您使用模板的示例中,即使您正在使用它(它应该),Container!(Widget)与Container不同(!SomeClass)。如此有效地解决您的问题:
class ContainerWidget {
Widget[] elements;
}
class ContainerSomeClass {
SomeClass[] elements;
}
要正确支持平面阵列,您必须创建一个标记的容器,类似于标记的联合的工作方式。
答案 1 :(得分:2)
我认为你这是错误的做法。您正试图从内部向各种类添加功能,而从外部添加它会更有意义,即扩展它们。
容器应该包含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
和Widget
,请注意您仍然可以像w1.m_x
一样使用w1.foo()
。
答案 2 :(得分:1)
如果我是你,我会把Container变成一个小工具。无论如何,这就是在许多现有的GUI工具包中完成的,并且确实如此。