在另一个社区,我有人建议你应该"永远"在你的基类中拥有属性.相反,它应该只有纯虚函数,让派生类负责为所有这些方法提供定义.
我的看法是"这不是一个经验法则".我给出了以下简单的例子:
class Shape
{
protected:
double Area;
double Perimeter ;
public:
Shape():Area(0.0), Perimeter(0.0){}
double getArea(){return Area;}
double getPerimeter (){return Perimeter;}
virtual void setArea () = 0 ;
virtual void setPerimeter () = 0 ;
};
class Rectangle : public Shape
{
private :
double length ;
double breadth ;
public:
Rectangle ():length(0.0),breadth(0.0){}
Rectangle (int _length, int _breadth):length(_length),breadth(_breadth){}
void setArea ()
{
Area = length * breadth ;
}
void setPerimeter ()
{
Perimeter = 2 * (length + breadth) ;
}
} ;
Run Code Online (Sandbox Code Playgroud)
通过上面给出的示例,我觉得任何形状都有以下属性'Area'和'Perimeter',因此如果我们不在类中提供这些属性,则Shape类将不适合表示'真实世界形状'
请告诉我你对此的看法.
编辑:
我要说的第一件事是我的代码确实是一个天真的代码并且在设计上没有多大意义.我之前想过,我会添加一行说这只是一个例子,这篇文章的重点是要知道'基类应该永远不会有属性'.但后来我认为我也会得到一些好的设计建议,我确实有很多:)
提出问题,以下所有帖子,我明白它不是一个经验法则,它是设计选择(这正是我想强调的).话虽如此,我也承认,如果这些属性可以计算(或导出)为Area = Length*Breadth,那么在基类中不需要属性.非常感谢您的所有答案(我希望我可以选择接受多个答案).
Ste*_*sop 12
我不同意你的例子.当然,矩形有一个区域和一个周长,当你可以在吸气剂中即时计算它们时,将它们与长度和宽度分开存储没有多大意义.为什么当你可以提供更好的界面时,为什么强迫人们调用这些"设置"功能以获得准确的结果?
我会做Shape一个纯粹的界面:
struct Shape
{
virtual ~Shape(); // probably need this
virtual double getArea () = 0 ;
virtual double getPerimeter () = 0 ;
};
class Rectangle : public Shape
{
double length ;
double breadth ;
public:
Rectangle ():length(0.0),breadth(0.0){}
Rectangle (int _length, int _breadth):length(_length),breadth(_breadth){}
double getArea ()
{
return length * breadth ;
}
double getPerimeter ()
{
return 2 * (length + breadth) ;
}
};
Run Code Online (Sandbox Code Playgroud)
我觉得任何形状都有以下属性'Area'和'Perimeter',因此如果我们不在类中提供这些属性,Shape类将不适合表示'真实世界形状'
我也不同意这一点.Shape类应该有一个公共API,可以访问其区域和周边.它是否具有包含这些值的字段是实现细节,并且与它是否代表外部世界的形状无关.
例如,为外部世界提供一个漂亮的界面,85%的课堂设计.另外15%的人确保适合调用者的接口实际上是可行的.使内部实现细节遵循与外部接口相同的模式,在某种程度上函数返回的对象的每个属性必然存储在一个字段中,不仅仅是低优先级,它是没有意义的.
如果你有一个圈子,你可以设想有getRadius和getDiameter 功能,但你会他们,而不是存储在不同的领域尽管当然圈子中有半径,也是一个直径.
现在,如果这不是一个Rectangle,而是一些非常复杂的形状,其中计算区域和周长很慢,那么您可能希望缓存对象中的区域和周边(并且无论何时定义,都要重新计算它们或标记缓存陈旧参数变化).但这是复杂形状的特殊属性,与矩形等简单形状无关,因此并不属于基类Shape.基类应该只包含所有派生类共有的东西,或者至少与你愿意扮演的东西相同,就好像它们是共同的(并覆盖或忽略它们不适用的子类中的那些东西)至).在您的示例中,报告区域和周长的功能对于所有形状都是通用的.
你永远不应该在基类中拥有属性
我也不同意这一点,虽然我觉得我很欣赏他们可能会去哪里.
Shape基类可能合理地包含的字段包括位置坐标和方向(如果我们讨论的是在屏幕上显示的实际矩形等,而不是抽象中的矩形),颜色,标签(如果我们坚定地致力于众包分类,并且可能从另一个基类继承,而不是在Shape本身中定义),以及那种事情,无论涉及什么派生类,都将以相同的方式处理.
也就是说,实现的继承被过度使用(因为它经常显得容易出错),但我个人认为它有时是值得的.在形状的情况下,您可以在尝试定义几何图形的类层次结构时遇到各种麻烦.
如果你坚持使用不可变对象,那也不算太糟糕.Square是一个Rectangle(其宽度和宽度恰好相同),因此它可以是Rectangle的子类.但是一个月后你想引入通用的四边形.你在哪里添加新课程?糟糕 - 在类层次结构的顶部,它可能会干扰您现有的类.添加四边形使你重新思考正方形.在其他条件相同的情况下,您希望这种广泛的变化是可选的,而不是强迫自己.
但是可变对象呢?可变的Square 不是可变的Rectangle,因为可变矩形应该能够将宽度设置为2,将高度设置为3. Square不应该提供此功能.不可变的矩形也不是可变的方形(因为可变的方形应该具有如果你改变宽度,高度改变的属性.可变矩形不应该具有这个属性).所以宽度和高度恰好相同的可变矩形与可变方块完全不同.这些类不能通过继承关联.现在,应该Rectangle(2,2) == Square(2)是真还是假?无论我们决定什么,我们都会给某人一个惊喜.所以我们发现我们必须将它们作为无关的,无与伦比的类.也就是说,不要使用继承.
所以纯粹的接口尽可能好,有些人已经认定这类问题是如此常见,以至于其他类型的继承都不值得.Go编程语言没有继承.顺便说一句,许多人也决定避免使用可变对象,并且做得很好,但这不是问题....
我认为在很多情况下,作为一个实现细节,在基类中使用通用功能很方便.只是不要认为这种常见行为是您的基类真正固有的.它是实现,而不是接口,有一天你可能会定义一个不想使用它的派生类,因为其他一些实现更合适.您可以方便地将常见行为放在嵌入对象而不是基类中,这样做.
在基类中包含变量是可以接受的.但是,很多语言都不允许这样做,所以基类只能有虚函数.这可能是你听说过的原因.
关于这个问题有很多不同的看法,但在C++中,我认为基类存储数据是相当普遍的.