这是一个基本的OO设计问题。我正在用C ++编写类,以根据已解析的输入C文件在流程图中表示项目。
简单来说,我们有两种类型的项目(类):FlowChartActionItem和FlowChartConditionItem。
这些分别代表流程图的“动作”和“决策/条件”元素。并且它们还分别表示输入C文件中存在的语句和If条件。这两个类都继承FlowChartItem。
每个子类都有许多指向其后各项的指针。是的,我们有一个图,其中包含节点(项目)和链接(指针)。但是FlowChartActionItem仅具有一个向外指针,而FlowChartConditionItem具有3个向外指针(对于then-statements分支,else-statements分支和一个指向if-condition的两个分支之后的指针。
我的问题是为向外的指针(nextItems)写一个整洁的设置器。看一下这些类:
class FlowChartItem
{
public:
//I **need** this setter to stay in the parent class FlowChartItem
virtual void SetNextItem(FlowChartItem* nextItem, char index) = NULL;
};
--
class FlowChartActionItem:public FlowChartItem
{
public:
FlowChartItem* nextItem; //Only 1 next item
public:
void SetNextItem(FlowChartItem* nextItem, char index);
};
--
class FlowChartConditionItem: public FlowChartItem
{
public:
FlowChartItem* nextItem;
FlowChartItem* trueBranchItem;
FlowChartItem* falseBranchItem; //we have 3 next items here
public:
void SetNextItem(FlowChartItem* nextItem, char index);
};
我需要一个不依赖于子类具有的指针数量的通用设置器。
如您所见,我已经使用char索引来告诉setter要设置哪个指针。但是我不喜欢这样,我需要使事情更整洁。因为代码不可读,例如:
item1.setNextItem(item2,1);
我们不记得1是什么意思?当时的分支?否则? ??
显而易见的答案是在FlowCharItem中定义一个枚举,但是然后我们将遇到两个问题之一:
1-现在将定义Enum值,因此将为当前子类FlowChartActioItem和FlowChartConditionItem量身定制,因此在将来的子类上对SetNextItem的调用将具有非常差的可读性。更糟糕的是,它们不能具有超过3个向外的指针!
2-通过使将来的子类的开发人员编辑FlowChartItem的头文件并在枚举中添加任何值来解决第一个问题!当然不能接受!
我有什么解决方案才能保持
-良好的可读性
-我的课程整齐地可扩展?
最佳答案
这是常见架构难题的一种形式。不同的子类的共享行为略有不同,因此您需要以某种合理的方式以某种方式将公共本质提取到基类。您通常会后悔的陷阱是让子类功能渗入父类。例如,对于在FlowChartItem中定义的输出连接类型,我不建议使用一组潜在的枚举名称。这些名称仅在使用它们的各个子节点中有意义。同样,使每个子类复杂化以适应其兄弟姐妹的设计也将是很糟糕的。最重要的是,KIS!保持。它。简单。
在这种情况下,感觉就像您想得太多。根据父类代表什么以及它如何被其他代码使用的抽象概念设计父类,而不是由继承者将其专门化。
可以更改名称SetNextItem以使其更清楚地说明两个参数的作用。就整个图表而言,它只是“下一个”项目,而不是单个FlowChartItem的上下文。流程图是directed graph,每个节点通常只知道自身及其连接。 (而且,您不是在编写Visual Basic,因此容器索引从0开始!:-))
virtual void SetOutConnectionByIndex(FlowChartItem* nextItem, char index);
或者,如果您希望使用较短的名称,则可以设置“第N个”输出项:
SetNthOutItem
。由于使用超出范围的索引设置子项无效,因此您可能想在FlowChartItem中使用另一个纯虚函数,该函数返回受支持子项的最大数量,并使SetChildByIndex返回成功/失败代码(或者,如果如果索引超出范围,则是其中之一,引发异常)。
virtual bool SetChildByIndex(FlowChartItem* item, char index);
现在,已经写完了所有内容,我开始怀疑您将调用此函数的代码。它是否真的只将每个节点都知道为FlowChartItem,但仍需要按其不知道其重要性的特定顺序设置其子级?如果您还有其他代码可以知道真实的物料类型及其子订购的含义,并且该代码正在为进行设置的代码提供物料指针及其索引号,那么这可能是有效的。也许反序列化代码,但这不是处理序列化的正确方法。 FlowChartItem是否通过严格的API公开,并且图表是由了解流程图类型的不同类型但无法访问实际类的代码构建的?在这种情况下也许有效,但我现在的猜测已经超出了您提供的详细信息。
但是,如果仅由知道真实项目类型,可以访问实际类并知道索引含义的代码调用此函数,则可能根本不应该在基类中使用此函数。
但是,我可以想像很多类型的代码,它们需要按顺序获取FlowChartItem的子代,而不知道该顺序的重要性。绘制流程图的代码,执行流程图的代码。如果您为简洁起见而减少了问题,并且还在考虑类似的getter方法,那么上述建议将适用(尽管您也可以考虑使用iterator模式)。
关于c++ - 如何设计具有改进的可用性和可读性的OO图节点类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12250029/