这是无聊的学术 OOP 问题之一,但它不是作业。我从一个新手程序员那里得到了一个关于 OOP 的愚蠢教科书示例的问题。
想象一下,你正在设计一个 Square 类和一个 Cube 类,应该继承哪个?
我看到了关系,但它是什么,我真的看不出来!
你能给我一个关于 OOP 的合乎逻辑的论点吗?
两者都不!由于正方形不是立方体,立方体也不是正方形,所以两者都不应该从另一个继承。正方形可以继承多边形,立方体可以继承多面体,但两者本身是互斥的。
没有继承。继承是一种“is-a”关系(嗯,有时甚至不是“is-a”关系,如下面的链接所述)。立方体不是正方形,正方形也不是立方体。
你将如何构建,这取决于你如何建模。你可以用一个立方体有 6 个正方形(立方体不是,它有 6 个正方形;一个组合),或者一个立方体有一个边大小,就像正方形一样。但是一旦没有“is-a”,继承将是危险的区域......
此外,在继承中,对基类有效的所有内容都必须对 Derived 有效。这是正方形扩展矩形的问题。例如:
假设 Cube 继承了 Square:如果你的 Square 有方法 changeArea(double area) 和 getSide(),那么 Cube 应该也是可以的。但事实并非如此,因为立方体的面积是正方形面积的 6 倍(它有 6 个正方形)。
假设 Square 继承了 Cube:如果你的 Cube 有 setVolume(double volume) 方法,你的 square 将被破坏,一旦它没有体积
最后,如果你想使用继承,你可以创建一个GeometryObjectWithEqualSides对象,然后两者都可以继承它。:)
无论哪种方式都将违反Liskov 替换原则。
都继承自超立方体
struct Square // Rectangle actually
{
Square( int dx, int dy ) : dx(dx), dy(dy) {};
int dx;
int dy;
int floor_area() { return dx*dy; };
};
struct Cube : Square // Cuboid actually
{
Cube( int dx, int dy, int dz ) : Square(dx, dy), dz(dz) {};
int dz;
int cube_area() { return floor_area()*2+dx*dz*2+dy*dz*2; };
};
似乎这里没有违反Liskov 替换原则。
正方形和立方体可以被认为是同一类“超立方体”的两个实例,它也将包含点(0 维)、线段(1 维)和除此之外的其他内容。维度的数量和一侧的长度足以定义 Hypercube 的特定实例(您当然可以添加一个 n 维的原点和方向)。
超立方体可以提供返回特定实例的顶点数、边数、面数等值的函数/方法。
有关更多信息,请参阅Wikipedia 上的Hypercube。
两者都不应该继承另一个。一个是 2 维形状,另一个是 3 维对象。两者之间确实没有足够的相似性来证明继承是合理的。
现在你可以想象制作一个由正方形组成的立方体,如果你需要为每一边一个单独的对象;)
这里的大多数评论都正确地说它们都不应该从另一个继承。在大多数情况下都是如此。但我认为有更通用的答案: 这取决于你对他们的期望。 你希望 Square 做什么?Cube也这样做吗?可能是另一种方式 - 你可以在使用 Cube 时使用 Square 吗?根据常识,我认为“Cube 完成了 Square 所做的一切”和“Square 完成了 Cube 所做的一切”这两种说法都是错误的,因此它们都不应该从另一个继承。但是,由您决定它们的结构和行为,因为是您定义程序的功能和组成。
最有可能的“立方体包含 6 个正方形”是您看到的关系。