There are many ways a change to a base class can affect derived class behaviour. What if the author suddenly decided to make the class sealed
for example?
Like any interface it needs to be stable if consumers are to not require modification.
The primary "rule" with inheritance is the Liskov Substition Principle. This states that the derived class should be substitutable for the base class or other classes derived from it.
This case breaks this rule on the face of it, as a rectangle with a hole is not a rectangle.
Usually it's best to use interfaces to divide behaviour into sensible implementable chunks. Such interfaces are generally named with adjectives rather than nouns. For example, it makes sense for both a rectangle and a rectangle with a hole to be rendered, so an interface might be IRenderable
. If it's resizable you might have a IResizable
, etc. These could be aggregated into an IShape
, but you want to be careful that your definition of Shape defines just the behaviours in your problem domain.
Directly deriving from another class can be dangerous as you're then bound by the behaviour of that class. If you genuinely need to do that it can be best to extract the implementation you need into a common base class (e.g. Rectangle : RectangleImplementation, IShape
).