I disagree with your example. While of course a Rectangle has an area and a perimeter, it doesn't make much sense to store them separately from from the length and breadth, when you could just calculate them on the fly in the getters. Why force people to call these "Set" functions in order to get accurate results, when you can provide a much nicer interface?
I would make Shape
a pure interface:
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) ;
}
};
i feel that any shape has following attributes 'Area' and 'Perimeter' and hence if we do not provide these attributes in the class, the Shape class would not be a appropriate representation of 'Real world shape'
I don't agree with this either. The Shape class should have a public API which provides access to its area and perimeter. Whether or not it has fields containing those values is an implementation detail, and has no bearing on whether it represents a shape to the outside world.
Providing the outside world with a nice interface is, say, 85% of class design. The other 15% is making sure that the interface that suits the caller is actually feasible to implement. Making the internal implementation details follow the same pattern as the external interface, to the extent that every property of the object returned by a function necessarily is stored in a field, isn't just low priority, it's pointless.
If you had a circle, you might conceivably have getRadius
and getDiameter
functions, but you wouldn't store them in different fields, despite the fact that of course circles have a radius and also a diameter.
Now, if this weren't a Rectangle, but some very complicated shape where calculating area and perimeter is slow, then you might want to cache the area and perimeter in the object, (and either recalculate them or mark the cache stale whenever the defining parameters change). But that's a particular property of complicated shapes, nothing to do with simple shapes like Rectangles, and so doesn't really belong in the base class Shape. Base classes should only contain things which are common to all derived classes, or at least common to enough of them that you're willing to act as though it's common to all (and override or ignore those things in the subclasses they don't apply to). In your example, the ability to report area and perimeter are common to all shapes. The fact of storing area and perimeter in data members need not be common to all shapes, and in the case of Rectangle occupies extra memory and requires extra code for pretty much no benefit.
you should 'NEVER' have attributes in your base class
I don't agree with this either, although I think I appreciate where they might be going with it.
Fields which a Shape
base class might reasonably have include position co-ordinates and orientation (if we're talking about actual rectangles etc to be displayed on screen, rather than rectangles in the abstract), colour, tags (if we're firmly committed to crowd-sourced taxonomies, and probably inherited from another base class rather than defined in Shape itself), and that kind of thing, that will be handled the same way irrespective of what derived class is involved.
That said, inheritance of implementation is over-used (because it often appears misleadingly easy), but personally I think it's sometimes worthwhile. In the case of shapes, you can get into all kinds of trouble trying to define a class hierarchy of geometric figures.
It's not too bad if you stick to immutable objects. A Square is a Rectangle (whose width and breadth happen to be equal), so it can be a subclass of Rectangle. But then a month later you want to introduce generic Quadrilaterals. Where do you have to add the new class? Oops - at the top of the class hierarchy, where it potentially disturbs both your existing classes. Adding quadrilaterals has made you re-think squares. All else being equal you'd like that kind of wide-ranging change to be optional, not force it on yourself.
What about mutable objects, though? A mutable Square is not a mutable Rectangle, because mutable rectangles should provide the ability to set the width to 2 and the height to 3. Squares should not provide this ability. Neither is a mutable Rectangle a mutable Square (since mutable Squares should have the property that if you change the width, the height changes. Mutable Rectangles should not have this property). So mutable rectangles whose width and height happen to be equal, aren't the same thing as mutable squares at all. The classes can't be related by inheritance. Now, should Rectangle(2,2) == Square(2)
be true or false? Whichever we decide, we're going to surprise somebody. So we find we have to leave them as unrelated, incomparable classes. That is to say, don't use inheritance.
So pure interfaces are good where possible, and some people have decided that these kinds of problem are so common that other kinds of inheritance just aren't worth it. The Go programming language does not have inheritance. As an aside, many people have also decided to avoid mutable objects, and done quite well out of it, but that's not the question....
I think there are plenty of cases where, as an implementation detail, it is convenient to have common functionality in base classes. Just don't think this common behaviour is truly intrinsic to your base class. It's implementation, not interface, and some day you might define a derived class that doesn't want to use any of it, because some other implementation is more appropriate. Where you can conveniently put the common behaviour in embedded objects rather than base classes, do so.