并行继承树,其中来自一个树的类具有来自另一个树的类的容器

并行继承树,其中来自一个树的类具有来自另一个树的类的容器

问题描述:

我真的不知道如何在标题中指定问题,所以这里是它的要点。

I really didn't know how to specify the problem in the title, so here's the gist of it.

我正在编写图形类Graph,Node和Edge,然后将它们子类化为VisGraph,VisNode和VisEdge以获得可绘制的图形(在C ++中)。然后我需要进一步将它们子类化为依赖于某些数据的特定类。所以我有很多并行继承:

I am writing graph classes Graph, Node, and Edge, and then subclassing them into VisGraph, VisNode and VisEdge to obtain a drawable graph (in C++). I then need to further subclass those into specific classes that depend on certain data. So I have a lot of parallel inheritance:


Graph -- VisGraph -- RouteGraph

Node  -- VisNode  -- RouteNode

Edge  -- VisEdge  -- RouteEdge

这很丑陋我开始做这个,所以我会逐步实现功能,但有很多问题。例如,其中之一是基类具有图中所有Node实例的容器。问题是,如果我在处理VisNode的VisGraph函数中需要VisNode独有的功能,我必须对从基类中的容器中获取的节点执行dynamic_cast。

This is pretty ugly and I started out doing this, so that I would implement functionality incrementally, but there are a lot of problems. One of them, for example, is that the base class has a container of all the Node instances in the graph. The problem is, if I am in a function in VisGraph dealing with a VisNode, that requires functionality unique to VisNode, I have to do a dynamic_cast on the Nodes that I get from the container in the base class.

也许我应该写一个包含Graph并绘制它的Vis类?
我发现继承很方便,因为每个节点/边可以轻松地绘制自己而不是我
存储关于位置等之外的额外信息并单独绘制它们。

Perhaps I should write a "Vis" class that holds a Graph and draws it? I found inheritance convenient because each node/edge could easily draw itself instead of me storing extra information outside about position etc. and drawing them all individually.

你有什么建议/设计模式可以让这更优雅吗?

Do you have any suggestions/design patterns that could make this more elegant?

提前谢谢你。

如果有疑问,请向问题投掷模板直至投降:

If in doubt, throw templates at the problem until it surrenders:

template <typename N, typename E>
class Graph {
    std::vector<N> nodes;
    std::vector<E> edges;
};

typedef Graph<VisNode, VisEdge> VisGraph;
typedef Graph<RouteNode, RouteEdge> RouteGraph;

你失去了继承(RouteGraph不再继承VisGraph),但在C ++中容器类型是正常的,Graph有点像容器。你可以保持Node - > VisNode - > RouteNode之间的继承。

You lose the inheritance (RouteGraph no longer inherits from VisGraph), but that's normal in C++ for container types, and Graph is somewhat like a container. You can keep the inheritance between Node -> VisNode -> RouteNode, though.

由于节点和边缘应该是匹配的类型,你可以更进一步,给Graph一个模板参数,它本身是一个包含边和节点类型为typedef的类。不过我不确定它是否值得。

Since nodes and edges are supposed to be of matching types, you could go even further, and give Graph a single template parameter, which itself is a class containing the edge and node types as typedefs. I'm not sure it's worth it, though.

编辑

由于你想连续添加功能,你可以保留一种继承形式,但却失去了多态性:

Since you want to successively add functions, you could keep a form of inheritance but lose the polymorphism:

template <typename N, typename E>
class GraphImpl {
    std::vector<N> nodes;
    std::vector<E> edges;
};

template <typename N, typename E>
class VisGraphImpl : public GraphImpl<N, E> {
    // constructors

    // extra functions
};

template <typename N, typename E>
class RouteGraphImpl : public VisGraphImpl<N, E> {
    // constructors

    // extra functions
};

typedef GraphImpl<Node, Edge> Graph;
typedef VisGraphImpl<VisNode, VisEdge> VisGraph;
typedef RouteGraphImpl<RouteNode, RouteEdge> RouteGraph;

但是,通过将这些额外功能捆绑到合理的mixin并使用CRTP,可能会有更好的方法:

There might be a better way, though, by bundling these extra functions up into sensible mixins and using CRTP:

template<typename Derived>
class VisFunctions {
    void somfunc() {
        myself = static_cast<Derived&>(*this);
        // do stuff
    }
};

然后:

class VisGraph : public Graph<VisNode, VisEdge>, public VisFunctions<VisGraph> {
    friend class VisFunctions<VisGraph>;
};

class RouteGraph : public Graph<RouteNode, RouteEdge>, public VisFunctions<RouteGraph>, public RouteFunctions<RouteGraph> {
    friend class VisFunctions<RouteGraph>;
    friend class RouteFunctions<RouteGraph>;
};

不知道如何看待你的真实情况。顺便说一句,如果你不需要/需要朋友声明的额外功能,那么你根本不需要那些额外的功能 - 只需使它们成为带有VisGraph或RouteGraph参数的*函数。

Not sure how that'll look for your real circumstances, though. Btw, if you don't want/need the friend declaration for the extra functions, then you don't need those extra functions to be members at all - just make them free functions that take a VisGraph or RouteGraph parameter.