问题: 我必须确保(最好在编译时)不能将通用的 MyGenericGraphNodes 添加到专业的 My3DGraph 中,因为它高度依赖于添加到 My3DGraphNode 中的 3D 逻辑。(而通用的 MyGenericGraph 将简单地不关心。) 核心问题就这么简单: 我无法覆盖来自MyGenericGraph的这些方法:
- (void)addNode:(MyGenericGraphNode *)aNode;
- (void)removeNode:(MyGenericGraphNode *)aNode;
在我的子类My3DGraph中,使用这些方法:
- (void)addNode:(My3DGraphNode *)aNode;
- (void)removeNode:(My3DGraphNode *)aNode;
我已经想出了三种可能的解决方案,但在选择任何一种之前,我想听听对它们的意见。(希望能为我避免一些未预料到的麻烦)
我想知道是否有其他更好的解决方案或设计模式是我忽略的?如果没有,你会选择我的哪个解决方案?
我很想听听你的意见。
可能的解决方案1
- 添加一个抽象类
MyAbstractGraph
,基本上与我当前实现的MyGenericGraph
(见下文)的通用部分相同,但缺少任何节点添加/删除方法。MyGenericGraph
和My3DGraph
将简单地成为MyAbstractGraph
的子类。虽然MyGenericGraph
仅实现缺少的节点添加/删除方法,但My3DGraph
将进一步实现所有3D空间功能。两者都需要各自的节点类类型。(MyGenericGraphNode
和MyGenericGraphEdge
及其3D对应项也是如此)
这种解决方案的问题: 它会给一个本来相当简单的问题增加相当复杂的复杂性。
此外,由于My3DGraph
应该能够处理My3DGraphNodes
和MyGenericGraphNodes
,因此我必须将MyGenericGraph
的方法实现为:
- (void)addNode:(MyAbstractGraphNode *)aNode;`
但是My3DGraph
的方法如下:
- (void)addNode:(My3DGraphNode *)aNode;
此外,否则我的通用图形将无法接受3D节点。这会不必要地暴露抽象类。
可能的解决方案2:
真正简单的子类+移动MyGenericGraphNode/My3DGraphNode分配权到MyGenericGraph/My3DGraph中,以获得像这样的东西:- (MyGenericGraphNode*)newNode;,它将分配并返回正确类型的节点,并立即将其添加到图形中。然后,完全摆脱 - (void)addNode:(MyGenericGraphNode *)aNode;,只能从图形本身添加节点(因此确保正确的类成员资格)。
这种解决方案的问题是:虽然它不会向类添加任何值得注意的复杂性,但另一方面,一旦我想为我的My3DGraph添加移动节点的功能,就会基本上让我陷入同样的困境。我认为一个类应该能够处理由谁创建和为什么创建的对象。
可能的解决方案3:
真正简单的子类+为3D节点添加专门方法并禁用通用方法,如下所示:
- (void)addNode:(MyGenericGraphNode *)aNode {
[self doesNotRecognizeSelector:_cmd];
}
- (void)add3DNode:(My3DGraphNode *)aNode {
//bla
}
这种解决方案存在的问题:通用的[a3DGraph addNode:aNode]
方法仍会出现在Xcode的自动完成中,在编译时默默通过,但在运行时意外地抛出异常。预计会经常出现问题。
可能的解决方案4
为我的图形创建真正简单的子类,并为节点和边缘使用通用类,但在节点类中添加一个额外的实例变量指针My3DUnit *dimensionalUnit:
(默认为nil,用于MyGraph),该变量实现所有逻辑和属性,并为节点类提供3D功能。如果将通用节点添加到3D图形中,则可以简单地静默创建My3DUnit
(例如,位置为(0,0,0)),并使其兼容。反之,如果将带有DL3DUnit
的节点添加到通用图形中,则它只需保持附加状态并添加节点即可。
头文件
以下是我的类的(缩短的)头文件:
@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目前没有实现三维空间功能,但将来可能会像计算其中心点一样,因此我在这里包含了它。)
[编辑:添加了受ughoavgfhw启发的解决方案4;修正了解决方案1中的一个错误,对此我深表歉意 :(]