问题描述
我已经实现了一个基本的图类(不是像绘图"中那样,而是像网络"中那样!),该类将用于基本图理论任务 . (请参见下面的摘要头文件摘要)
I've implemented a basic graph class (not as in "plotting" but as in "network"!), that's to be used for basic graph theoretical tasks. (see summarised header file snippets below)
除了通用图形功能之外,它还实现了用于3D空间中节点定位的功能. 我想将此扩展的3D功能隔离到一个子类中,结果是:
In addition to the generic graph functionality it also implements functionality for node positioning in 3D space. And this extended 3D functionality I'd like to isolate into a subclass, resulting in:
- 轻量级通用类
(MyGenericGraph,MyGenericGraphNode, MyGenericGraphEdge)
- 重磅专业子类
(My3DGraph, My3DGraphNode,My3DGraphEdge)
- light-weight generic classes
(MyGenericGraph,MyGenericGraphNode, MyGenericGraphEdge)
- heavier-weight specialized subclasses
(My3DGraph, My3DGraphNode,My3DGraphEdge)
从理论上讲,到目前为止,一切都很好.
So far so good, in theory, that is.
我必须确保(最好是在编译时)不能将通用MyGenericGraphNodes
添加到特殊化的My3DGraph
中,因为它高度依赖于My3DGraphNode
内部添加的3D逻辑. (虽然通用MyGenericGraph
根本不在乎.)
I'd have to ensure (and preferably at compile time) that one cannot add generic MyGenericGraphNodes
to the spezialized My3DGraph
, as it highly depends on the added 3D logic inside My3DGraphNode
. (While the generic MyGenericGraph
would simply not care.)
核心问题是如此简单:我无法从MyGenericGraph中覆盖这些方法:
The core issue is as simple as this: I cannot override these methods from MyGenericGraph:
- (void)addNode:(MyGenericGraphNode *)aNode;
- (void)removeNode:(MyGenericGraphNode *)aNode;
在我的子类My3DGraph中使用这些方法:
with these methods in my subclass My3DGraph:
- (void)addNode:(My3DGraphNode *)aNode;
- (void)removeNode:(My3DGraphNode *)aNode;
到目前为止,我已经提出了三种可能的解决方案,但是在选择其中任何一种之前,我想听听对它们的一些看法. (并希望为我省去一些无法预料的麻烦)
I've so far come up with three possible solutions but before going for any of them I'd like to hear some opinions on them. (and hopefully spare me some unforeseen troubles on my way)
我想知道我是否还缺少另一个更好的解决方案或设计模式? 或者如果不是:您会选择哪种解决方案?
我很想听听您对此的看法.
I'm wondering if there's another and superior solution or design pattern to this that I'm missing? Or if not: which of my solutions would you go for?
I'd love to hear your opinion on this.
- 添加一个抽象类
MyAbstractGraph
,该类基本上与MyGenericGraph
的当前实现的通用部分相同(见下文),但会缺少任何节点添加/删除方法.MyGenericGraph
和和My3DGraph
然后将只是MyAbstractGraph
的子类.尽管MyGenericGraph
将仅实现缺少的节点添加/删除方法,但My3DGraph
将进一步实现所有3D空间功能.两者都需要各自的节点类类型. (与MyGenericGraphNode
和MyGenericGraphEdge
及其3D副本相同)
- Adding an abstract class
MyAbstractGraph
that would basically be identical to the generic parts of my current implementation ofMyGenericGraph
(see below), but would lack any node-addition/removal methods.MyGenericGraph
andMy3DGraph
would then simply be subclasses ofMyAbstractGraph
. And whileMyGenericGraph
would only implement the lacking node-addition/removal methods,My3DGraph
would further more implement all 3D space functionality. Both requiring their respective node class types. (same forMyGenericGraphNode
andMyGenericGraphEdge
and their 3D counterparts)
该解决方案的问题:这会为另外一个相当简单的问题 添加大量的复杂性.
此外,由于My3DGraph
应该能够处理My3DGraphNodes
和MyGenericGraphNodes
,因此我必须将MyGenericGraph
的方法实现为:
Problems with this solution: It would add a significant amount of complexity to an otherwise rather simple problem.
Further more as My3DGraph
should be able to deal with My3DGraphNodes
AND MyGenericGraphNodes
, I'd have to implement MyGenericGraph
's method as:
- (void)addNode:(MyAbstractGraphNode *)aNode;`
,但My3DGraph
的方法为:
- (void)addNode:(My3DGraphNode *)aNode;
以及
,否则我的通用图形将不接受3d节点.但这会不必要地暴露抽象类.
as well, as otherwise my generic graph it wouldn't accept 3d nodes. This would expose the abstract class unnecessarily though.
真实而简单的子类+将 MyGenericGraphNode/My3DGraphNode
分配权直接移入 MyGenericGraph/My3DGraph
,得到如下内容:- (MyGenericGraphNode *)newNode;
,它将分配并返回正确类型为的节点,并将其立即添加到图形中.这样一来,我们将完全摆脱 - (void)addNode:(MyGenericGraphNode *)aNode;
,没有机会从图本身内部添加节点(因此确保了正确的类成员身份).
True and simple subclasses + moving MyGenericGraphNode/My3DGraphNode
allocation right into MyGenericGraph/My3DGraph
, to get something like: - (MyGenericGraphNode *)newNode;
, which would allocate and return a node of correct type and add it to the graph right away. One would then get rid of - (void)addNode:(MyGenericGraphNode *)aNode;
entirely, leaving no chance to add nodes than from within the Graph itself (hence assuring proper class membership).
该解决方案的问题:尽管它不会给类增加任何值得注意的复杂性,但另一方面,只要我(可以说),想要为我的My3DGraph
添加功能以将节点从一个图形移动到另一个图形.而且恕我直言,一个类应该能够处理一个对象,无论是谁创建的以及为什么创建该对象.
Problems with this solution: While it would not add any noteworthy complexity to the classes, it would on the other hand basically get me into the same predicament again, as soon as I—let's say—wanted to add functionality to my My3DGraph
for moving a node from one graph to another. And imho a class should be able to deal with an object no matter who created it and why.
真实而简单的子类+为3D节点添加专用方法并禁用通用方法,例如:
- (void)addNode:(MyGenericGraphNode *)aNode {
[self doesNotRecognizeSelector:_cmd];
}
- (void)add3DNode:(My3DGraphNode *)aNode {
//bla
}
此解决方案的问题:通用的[a3DGraph addNode:aNode]
方法仍将显示在Xcode的自动完成功能中,在编译时以静默方式传递,但在运行时意外引发异常.可预见的是经常头痛.
Problems with this solution: The generic [a3DGraph addNode:aNode]
method would still show up in Xcode's auto-complete, pass silently on compile, but unexpectedly throw exception on run. Frequent headaches foreseen.
图的真实和简单子类,以及节点和边的通用类,但在节点类中有一个额外的ivar指针My3DUnit *dimensionalUnit:
(对于MyGraph,默认为nil)实现了所有的逻辑和属性,并且为节点类提供3D功能. My3DUnit
可以简单地以静默方式创建(例如,位置为(0,0,0)),并附加到通用节点上,以防它们被添加到3d图形中,因此可以兼容的.反之亦然,如果将具有DL3DUnit
的节点添加到通用图形中,它将简单地使其保持连接状态并添加该节点.
True and simple subclasses for my graph and just the generic classes for node and edge, but with an additional ivar pointer My3DUnit *dimensionalUnit:
in node class (defaulting to nil for MyGraph) that implements all the logic and properties and provides 3D functionality to the node class. My3DUnit
can simply be silently created (with position (0,0,0) e.g.) and attached to generic nodes in case they get added to a 3d graph, and hence made compatible. Vice versa if a node with a DL3DUnit
gets added to a generic graph it simply keeps it attached and adds the node.
这是我班级的(缩短的)标题:
Here are the (shortened) headers of my classes:
@interface MyGraph : NSObject {
// properties:
NSMutableSet *nodes;
//...
//extended 3D properties:
double gravityStrength;
//...
}
// functionality:
- (void)addNode:(MyGraphNode *)aNode;
- (void)removeNode:(MyGraphNode *)aNode;
//...
//extended 3D functionality:
- (double)kineticEnergy;
//...
@end
@interface MyGraphNode : NSObject {
// properties:
MyGraph *graph;
NSMutableSet *edges;
//...
//extended 3D properties:
My3DVector position;
//...
}
// properties:
@property (nonatomic, readonly) MyGraph *graph;
@property (nonatomic, readonly) NSSet *edges;
@property (nonatomic, readonly) NSSet *neighbors;
@property (nonatomic, readonly) NSUInteger degree;
//...
//extended 3D properties
@property (nonatomic, assign) My3DVector position;
//...
// functionality:
- (void)attachToGraph:(MyGraph *)aGraph;
- (void)detachFromGraph;
- (void)addNeighbor:(MyGraphNode *)aNode;
- (void)removeNeighbor:(MyGraphNode *)aNode;
- (BOOL)hasNeighbor:(MyGraphNode *)aNode;
- (NSSet *)neighbors;
- (NSUInteger)degree;
//...
//extended 3D functionality:
- (double)distanceToNode:(DLGraphNode *)aNode;
//...
@end
@interface MyGraphEdge : NSObject {
// properties:
MyGraphNode *predecessor;
MyGraphNode *successor;
//...
}
// properties:
@property (nonatomic, readonly) MyGraphNode *predecessor;
@property (nonatomic, readonly) MyGraphNode *successor;
//...
// functionality:
- (id)initWithPredecessorNode:(MyGraphNode *)predecessorNode successorNode:(MyGraphNode *)successorNode;
+ (MyGraphEdge *)edgeWithPredecessorNode:(MyGraphNode *)predecessorNode successorNode:(MyGraphNode *)successorNode;
- (BOOL)hasNeighbor:(MyGraphNode *)aNode;
- (BOOL)hasSuccessor:(MyGraphNode *)aNode;
- (BOOL)hasPredecessor:(MyGraphNode *)aNode;
@end
这基本上就是现在实现我的图形的方式.显然还有很多,但是您应该明白这个想法.
(您可能已经注意到MyGenericGraphEdge当前不实现3D空间功能,但将来可能会像计算其中心点一样,例如,因此我将其包含在此处.)
(As you might have noticed MyGenericGraphEdge currently implements no 3D space functionality, but it might in the future, like computing its center point, e.g., hence I've included it here.)
推荐答案
快速又肮脏的解决方案:
重新分析我的班级结构并找到我的计划图类家庭的一些以前无法预见的影响后,我得出了以下结论: 与我提出的解决方案4差不多,但是伴随着一些重大的重组(请参见随附的简化ER图).我的计划是不要拥有繁重的多用途über类,而是要有几个单一用途的组件(如果结构合理的话),可以组合成各种特殊用途的 >(并且相对轻量级)工具集.
Solution, quick and dirty:
After re-analysing my class structure and finding several previously unforeseen implications on potential pitfalls in future development on my planned graph class family I've come to the conclusion to go pretty much with my proposed solution 4 but accompany it by some major restructuring (see attached simplified ER diagram). My plan is to instead of having heavy multi-purpose über-classes, have several single-purpose components that (if well constructed) can be combined into various kinds of special-purpose (and relatively lightweight) toolsets.
如果我在MyGenericGraph
的子类中实现维特征集,则基本上使我几乎无法轻松创建更具体的图子类(例如,用于专用树),这些子类既可以是轻量级的,也可以是轻量级的以及通用(如MyGenericGraph
)或尺寸(如My3DGraph
).对于MyGenericTree
类(例如,用于树分析),我必须将MyGenericGraph
子类化.对于My3DTree
(例如用于树形显示),我必须将My3DGraph
子类化.因此,My3DTree
无法从MyGenericTree
继承任何逻辑.我会多余地实现尺寸特征.坏的.太糟糕了.
If I implement dimensional feature sets in a subclass of MyGenericGraph
, then this makes it basically impossible for me to easily create more specific graph subclasses (for specialized trees e.g.) that can be either lightweight and generic (like MyGenericGraph
) or dimensional (like My3DGraph
). For a MyGenericTree
class (for tree analysis e.g.) I'd have to subclass MyGenericGraph
. For a My3DTree
(for tree display e.g.) however I'd have to subclass My3DGraph
. My3DTree
could therefor not inherit any logic from MyGenericTree
. I'd have implement the dimensional features redundantly. Bad. Pretty bad.
-
完全摆脱所有具有维度风味"的类.仅使用基本逻辑和必需逻辑,使类严格地准系统数据结构.
Get rid of any "dimensional flavored" classes entirely. Keep classes strictly barebones data structures with just the basic and required logic.
引入MyVertex类,该类提供尺寸属性&方法(如果需要)(通过向MyGraphNode
添加MyVertex *vertex
ivar(默认为nil)).这也使得更容易在MyVertexCloud
(一个简单的点云容器)中重复使用它们,这对于改进我的力驱动布局算法非常有用.
Introduce MyVertex class that provides dimensional attributes & methods to nodes if required (By adding a MyVertex *vertex
ivar to MyGraphNode
(defaulting to nil)). This also makes it much easier to re-use them in MyVertexCloud
, a simple point cloud container, that should come in handy for improving my force-driven layout algorithm.
对图形的数据结构删除特殊要求的逻辑给特殊用途的帮助器类. 因此,MyGraphNodeClusterRelaxer
将负责可视化图形布局所特有的任何逻辑.
Delegate any logic that's not strictly essential to the graphs' data-structure to special purpose helper classes. As such MyGraphNodeClusterRelaxer
will be responsible for any logic specific to visual graph layout.
子类化MyGraph
将会非常容易.
Subclassing MyGraph
will be quick and easy thanks to my single-chained inheritance chain and modularity.
使用外部MyGraphNodeClusterRelaxer
还将使我放宽图节点的子集,而不仅仅是整个图,就像My3DGraph那样.
Utilizing an external MyGraphNodeClusterRelaxer
will also allow me to relax a subset of graph nodes, not just an entire graph, as My3DGraph would have done it.
MyGraphNodeCluster
基本上只不过是一组节点的包装器(所有图均相同). 它的子类可以在集群成员资格标准和算法中更具体.
MyGraphNodeCluster
will be nothing more than a wrapper for basically a set of nodes (all of the same graph). Subclasses of it can be more specific in cluster membership criteria and algorithms.
从整个图形中获取MyGraphNodeCluster
就像调用(MyGraphNodeCluster *)[myGraph nodeCluster];
一样容易,然后您通过MyVertexCloud >. 出于令人信服的原因,无法进行反向转换(对于后者).
Getting a MyGraphNodeCluster
from an entire graph will be as easy as calling (MyGraphNodeCluster *)[myGraph nodeCluster];
and from there you get a MyVertexCloud
via (MyVertexCloud *)[myNodeCluster vertexCloud];
. The reverse (for the latter) is not possible for obious reasons.
这篇关于在Objective-C中子类化一组相互依赖的类,并确保类型安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!